Unit testing #23
@ -17,3 +17,7 @@ func (app *application) home(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
func (app *application) notImplemented(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	views.ComingSoon("Coming Soon", app.newCommonData(r)).Render(r.Context(), w)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ping(w http.ResponseWriter, r *http.Request) {
 | 
			
		||||
	w.Write([]byte("OK"))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										60
									
								
								cmd/web/handlers_guestbook_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								cmd/web/handlers_guestbook_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,60 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"git.32bit.cafe/32bitcafe/guestbook/internal/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestPing(t *testing.T) {
 | 
			
		||||
	app := newTestApplication(t)
 | 
			
		||||
	ts := newTestServer(t, app.routes())
 | 
			
		||||
	defer ts.Close()
 | 
			
		||||
 | 
			
		||||
	code, _, body := ts.get(t, "/ping")
 | 
			
		||||
 | 
			
		||||
	assert.Equal(t, code, http.StatusOK)
 | 
			
		||||
	assert.Equal(t, body, "OK")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func TestGetGuestbookView(t *testing.T) {
 | 
			
		||||
	app := newTestApplication(t)
 | 
			
		||||
	ts := newTestServer(t, app.routes())
 | 
			
		||||
	defer ts.Close()
 | 
			
		||||
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name     string
 | 
			
		||||
		urlPath  string
 | 
			
		||||
		wantCode int
 | 
			
		||||
		wantBody string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Valid id",
 | 
			
		||||
			urlPath:  fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(1)),
 | 
			
		||||
			wantCode: http.StatusOK,
 | 
			
		||||
			wantBody: "Guestbook for Example",
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "Non-existent ID",
 | 
			
		||||
			urlPath:  fmt.Sprintf("/websites/%s/guestbook", shortIdToSlug(2)),
 | 
			
		||||
			wantCode: http.StatusNotFound,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:     "String ID",
 | 
			
		||||
			urlPath:  "/websites/abcd/guestbook",
 | 
			
		||||
			wantCode: http.StatusNotFound,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(t *testing.T) {
 | 
			
		||||
			code, _, body := ts.get(t, tt.urlPath)
 | 
			
		||||
			assert.Equal(t, code, tt.wantCode)
 | 
			
		||||
			if tt.wantBody != "" {
 | 
			
		||||
				assert.StringContains(t, body, tt.wantBody)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										121
									
								
								cmd/web/handlers_user_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								cmd/web/handlers_user_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,121 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"testing"
 | 
			
		||||
 | 
			
		||||
	"git.32bit.cafe/32bitcafe/guestbook/internal/assert"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func TestUserSignup(t *testing.T) {
 | 
			
		||||
	app := newTestApplication(t)
 | 
			
		||||
	ts := newTestServer(t, app.routes())
 | 
			
		||||
	defer ts.Close()
 | 
			
		||||
 | 
			
		||||
	_, _, body := ts.get(t, "/users/register")
 | 
			
		||||
	validCSRFToken := extractCSRFToken(t, body)
 | 
			
		||||
 | 
			
		||||
	const (
 | 
			
		||||
		validName     = "John"
 | 
			
		||||
		validPassword = "validPassword"
 | 
			
		||||
		validEmail    = "john@example.com"
 | 
			
		||||
		formTag       = `<form action="/users/register" method="post">`
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	tests := []struct {
 | 
			
		||||
		name         string
 | 
			
		||||
		userName     string
 | 
			
		||||
		userEmail    string
 | 
			
		||||
		userPassword string
 | 
			
		||||
		csrfToken    string
 | 
			
		||||
		wantCode     int
 | 
			
		||||
		wantFormTag  string
 | 
			
		||||
	}{
 | 
			
		||||
		{
 | 
			
		||||
			name:         "Valid submission",
 | 
			
		||||
			userName:     validName,
 | 
			
		||||
			userEmail:    validEmail,
 | 
			
		||||
			userPassword: validPassword,
 | 
			
		||||
			csrfToken:    validCSRFToken,
 | 
			
		||||
			wantCode:     http.StatusSeeOther,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:         "Missing token",
 | 
			
		||||
			userName:     validName,
 | 
			
		||||
			userEmail:    validEmail,
 | 
			
		||||
			userPassword: validPassword,
 | 
			
		||||
			wantCode:     http.StatusBadRequest,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:         "Empty name",
 | 
			
		||||
			userName:     "",
 | 
			
		||||
			userEmail:    validEmail,
 | 
			
		||||
			userPassword: validPassword,
 | 
			
		||||
			csrfToken:    validCSRFToken,
 | 
			
		||||
			wantCode:     http.StatusUnprocessableEntity,
 | 
			
		||||
			wantFormTag:  formTag,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:         "Empty email",
 | 
			
		||||
			userName:     validName,
 | 
			
		||||
			userEmail:    "",
 | 
			
		||||
			userPassword: validPassword,
 | 
			
		||||
			csrfToken:    validCSRFToken,
 | 
			
		||||
			wantCode:     http.StatusUnprocessableEntity,
 | 
			
		||||
			wantFormTag:  formTag,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:         "Empty password",
 | 
			
		||||
			userName:     validName,
 | 
			
		||||
			userEmail:    validEmail,
 | 
			
		||||
			userPassword: "",
 | 
			
		||||
			csrfToken:    validCSRFToken,
 | 
			
		||||
			wantCode:     http.StatusUnprocessableEntity,
 | 
			
		||||
			wantFormTag:  formTag,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:         "Invalid email",
 | 
			
		||||
			userName:     validName,
 | 
			
		||||
			userEmail:    "asdfasdf",
 | 
			
		||||
			userPassword: validPassword,
 | 
			
		||||
			csrfToken:    validCSRFToken,
 | 
			
		||||
			wantCode:     http.StatusUnprocessableEntity,
 | 
			
		||||
			wantFormTag:  formTag,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:         "Invalid password",
 | 
			
		||||
			userName:     validName,
 | 
			
		||||
			userEmail:    validEmail,
 | 
			
		||||
			userPassword: "asdfasd",
 | 
			
		||||
			csrfToken:    validCSRFToken,
 | 
			
		||||
			wantCode:     http.StatusUnprocessableEntity,
 | 
			
		||||
			wantFormTag:  formTag,
 | 
			
		||||
		},
 | 
			
		||||
		{
 | 
			
		||||
			name:         "Duplicate email",
 | 
			
		||||
			userName:     validName,
 | 
			
		||||
			userEmail:    "dupe@example.com",
 | 
			
		||||
			userPassword: validPassword,
 | 
			
		||||
			csrfToken:    validCSRFToken,
 | 
			
		||||
			wantCode:     http.StatusUnprocessableEntity,
 | 
			
		||||
			wantFormTag:  formTag,
 | 
			
		||||
		},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, tt := range tests {
 | 
			
		||||
		t.Run(tt.name, func(*testing.T) {
 | 
			
		||||
			form := url.Values{}
 | 
			
		||||
			form.Add("username", tt.userName)
 | 
			
		||||
			form.Add("email", tt.userEmail)
 | 
			
		||||
			form.Add("password", tt.userPassword)
 | 
			
		||||
			form.Add("csrf_token", tt.csrfToken)
 | 
			
		||||
			code, _, body := ts.postForm(t, "/users/register", form)
 | 
			
		||||
			assert.Equal(t, code, tt.wantCode)
 | 
			
		||||
			if tt.wantFormTag != "" {
 | 
			
		||||
				assert.StringContains(t, body, tt.wantFormTag)
 | 
			
		||||
			}
 | 
			
		||||
		})
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
@ -21,9 +21,9 @@ import (
 | 
			
		||||
type application struct {
 | 
			
		||||
	sequence          uint16
 | 
			
		||||
	logger            *slog.Logger
 | 
			
		||||
	websites          *models.WebsiteModel
 | 
			
		||||
	users             *models.UserModel
 | 
			
		||||
	guestbookComments *models.GuestbookCommentModel
 | 
			
		||||
	websites          models.WebsiteModelInterface
 | 
			
		||||
	users             models.UserModelInterface
 | 
			
		||||
	guestbookComments models.GuestbookCommentModelInterface
 | 
			
		||||
	sessionManager    *scs.SessionManager
 | 
			
		||||
	formDecoder       *schema.Decoder
 | 
			
		||||
	debug             bool
 | 
			
		||||
 | 
			
		||||
@ -11,6 +11,8 @@ func (app *application) routes() http.Handler {
 | 
			
		||||
	mux := http.NewServeMux()
 | 
			
		||||
	mux.Handle("GET /static/", http.FileServerFS(ui.Files))
 | 
			
		||||
 | 
			
		||||
	mux.HandleFunc("GET /ping", ping)
 | 
			
		||||
 | 
			
		||||
	dynamic := alice.New(app.sessionManager.LoadAndSave, noSurf, app.authenticate)
 | 
			
		||||
	standard := alice.New(app.recoverPanic, app.logRequest, commonHeaders)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										96
									
								
								cmd/web/testutils_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								cmd/web/testutils_test.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,96 @@
 | 
			
		||||
package main
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"html"
 | 
			
		||||
	"io"
 | 
			
		||||
	"log/slog"
 | 
			
		||||
	"net/http"
 | 
			
		||||
	"net/http/cookiejar"
 | 
			
		||||
	"net/http/httptest"
 | 
			
		||||
	"net/url"
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"testing"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.32bit.cafe/32bitcafe/guestbook/internal/models/mocks"
 | 
			
		||||
	"github.com/alexedwards/scs/v2"
 | 
			
		||||
	"github.com/gorilla/schema"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func newTestApplication(t *testing.T) *application {
 | 
			
		||||
	formDecoder := schema.NewDecoder()
 | 
			
		||||
	formDecoder.IgnoreUnknownKeys(true)
 | 
			
		||||
 | 
			
		||||
	sessionManager := scs.New()
 | 
			
		||||
	sessionManager.Lifetime = 12 * time.Hour
 | 
			
		||||
	sessionManager.Cookie.Secure = true
 | 
			
		||||
 | 
			
		||||
	return &application{
 | 
			
		||||
		logger:            slog.New(slog.NewTextHandler(io.Discard, nil)),
 | 
			
		||||
		sessionManager:    sessionManager,
 | 
			
		||||
		websites:          &mocks.WebsiteModel{},
 | 
			
		||||
		users:             &mocks.UserModel{},
 | 
			
		||||
		guestbookComments: &mocks.GuestbookCommentModel{},
 | 
			
		||||
		formDecoder:       formDecoder,
 | 
			
		||||
		timezones:         getAvailableTimezones(),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type testServer struct {
 | 
			
		||||
	*httptest.Server
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newTestServer(t *testing.T, h http.Handler) *testServer {
 | 
			
		||||
	ts := httptest.NewTLSServer(h)
 | 
			
		||||
	jar, err := cookiejar.New(nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	ts.Client().Jar = jar
 | 
			
		||||
	ts.Client().CheckRedirect = func(req *http.Request, via []*http.Request) error {
 | 
			
		||||
		return http.ErrUseLastResponse
 | 
			
		||||
	}
 | 
			
		||||
	return &testServer{ts}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ts *testServer) get(t *testing.T, urlPath string) (int, http.Header, string) {
 | 
			
		||||
	rs, err := ts.Client().Get(ts.URL + urlPath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer rs.Body.Close()
 | 
			
		||||
	body, err := io.ReadAll(rs.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	body = bytes.TrimSpace(body)
 | 
			
		||||
 | 
			
		||||
	return rs.StatusCode, rs.Header, string(body)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ts *testServer) postForm(t *testing.T, urlPath string, form url.Values) (int, http.Header, string) {
 | 
			
		||||
	rs, err := ts.Client().PostForm(ts.URL+urlPath, form)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	defer rs.Body.Close()
 | 
			
		||||
	body, err := io.ReadAll(rs.Body)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	body = bytes.TrimSpace(body)
 | 
			
		||||
	return rs.StatusCode, rs.Header, string(body)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var csrfTokenRX = regexp.MustCompile(`<input type="hidden" name="csrf_token" value="(.+?)">`)
 | 
			
		||||
 | 
			
		||||
func extractCSRFToken(t *testing.T, body string) string {
 | 
			
		||||
	matches := csrfTokenRX.FindStringSubmatch(body)
 | 
			
		||||
	if len(matches) < 2 {
 | 
			
		||||
		t.Fatal("no csrf token found in body")
 | 
			
		||||
	}
 | 
			
		||||
	return html.UnescapeString(matches[1])
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										22
									
								
								internal/assert/assert.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								internal/assert/assert.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
			
		||||
package assert
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"testing"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func Equal[T comparable](t *testing.T, actual, expected T) {
 | 
			
		||||
	t.Helper()
 | 
			
		||||
 | 
			
		||||
	if actual != expected {
 | 
			
		||||
		t.Errorf("got: %v; want %v", actual, expected)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func StringContains(t *testing.T, actual, expectedSubstring string) {
 | 
			
		||||
	t.Helper()
 | 
			
		||||
 | 
			
		||||
	if !strings.Contains(actual, expectedSubstring) {
 | 
			
		||||
		t.Errorf("got: %q; expected to contain: %q", actual, expectedSubstring)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@ -24,6 +24,15 @@ type GuestbookCommentModel struct {
 | 
			
		||||
	DB *sql.DB
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type GuestbookCommentModelInterface interface {
 | 
			
		||||
	Insert(shortId uint64, guestbookId, parentId int64, authorName, authorEmail, authorSite, commentText, pageUrl string, isPublished bool) (int64, error)
 | 
			
		||||
	Get(shortId uint64) (GuestbookComment, error)
 | 
			
		||||
	GetAll(guestbookId int64) ([]GuestbookComment, error)
 | 
			
		||||
	GetDeleted(guestbookId int64) ([]GuestbookComment, error)
 | 
			
		||||
	GetUnpublished(guestbookId int64) ([]GuestbookComment, error)
 | 
			
		||||
	UpdateComment(comment *GuestbookComment) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookCommentModel) Insert(shortId uint64, guestbookId, parentId int64, authorName,
 | 
			
		||||
	authorEmail, authorSite, commentText, pageUrl string, isPublished bool) (int64, error) {
 | 
			
		||||
	stmt := `INSERT INTO guestbook_comments (ShortId, GuestbookId, ParentId, AuthorName,
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										64
									
								
								internal/models/mocks/guestbookcomment.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								internal/models/mocks/guestbookcomment.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,64 @@
 | 
			
		||||
package mocks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.32bit.cafe/32bitcafe/guestbook/internal/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var mockGuestbookComment = models.GuestbookComment{
 | 
			
		||||
	ID:          1,
 | 
			
		||||
	ShortId:     1,
 | 
			
		||||
	GuestbookId: 1,
 | 
			
		||||
	AuthorName:  "John Test",
 | 
			
		||||
	AuthorEmail: "test@example.com",
 | 
			
		||||
	AuthorSite:  "example.com",
 | 
			
		||||
	CommentText: "Hello, world",
 | 
			
		||||
	Created:     time.Now(),
 | 
			
		||||
	IsPublished: true,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type GuestbookCommentModel struct{}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookCommentModel) Insert(shortId uint64, guestbookId, parentId int64, authorName,
 | 
			
		||||
	authorEmail, authorSite, commentText, pageUrl string, isPublished bool) (int64, error) {
 | 
			
		||||
	return 2, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookCommentModel) Get(shortId uint64) (models.GuestbookComment, error) {
 | 
			
		||||
	switch shortId {
 | 
			
		||||
	case 1:
 | 
			
		||||
		return mockGuestbookComment, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return models.GuestbookComment{}, models.ErrNoRecord
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookCommentModel) GetAll(guestbookId int64) ([]models.GuestbookComment, error) {
 | 
			
		||||
	switch guestbookId {
 | 
			
		||||
	case 1:
 | 
			
		||||
		return []models.GuestbookComment{mockGuestbookComment}, nil
 | 
			
		||||
	case 2:
 | 
			
		||||
		return []models.GuestbookComment{}, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return []models.GuestbookComment{}, models.ErrNoRecord
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookCommentModel) GetDeleted(guestbookId int64) ([]models.GuestbookComment, error) {
 | 
			
		||||
	switch guestbookId {
 | 
			
		||||
	default:
 | 
			
		||||
		return []models.GuestbookComment{}, models.ErrNoRecord
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookCommentModel) GetUnpublished(guestbookId int64) ([]models.GuestbookComment, error) {
 | 
			
		||||
	switch guestbookId {
 | 
			
		||||
	default:
 | 
			
		||||
		return []models.GuestbookComment{}, models.ErrNoRecord
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *GuestbookCommentModel) UpdateComment(comment *models.GuestbookComment) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										89
									
								
								internal/models/mocks/users.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								internal/models/mocks/users.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,89 @@
 | 
			
		||||
package mocks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.32bit.cafe/32bitcafe/guestbook/internal/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var mockUser = models.User{
 | 
			
		||||
	ID:       1,
 | 
			
		||||
	ShortId:  1,
 | 
			
		||||
	Username: "tester",
 | 
			
		||||
	Email:    "test@example.com",
 | 
			
		||||
	Deleted:  false,
 | 
			
		||||
	IsBanned: false,
 | 
			
		||||
	Created:  time.Now(),
 | 
			
		||||
	Settings: mockUserSettings,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var mockUserSettings = models.UserSettings{
 | 
			
		||||
	LocalTimezone: time.UTC,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UserModel struct {
 | 
			
		||||
	Settings map[string]models.Setting
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) InitializeSettingsMap() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) Insert(shortId uint64, username string, email string, password string, settings models.UserSettings) error {
 | 
			
		||||
	switch email {
 | 
			
		||||
	case "dupe@example.com":
 | 
			
		||||
		return models.ErrDuplicateEmail
 | 
			
		||||
	default:
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) Get(shortId uint64) (models.User, error) {
 | 
			
		||||
	switch shortId {
 | 
			
		||||
	case 1:
 | 
			
		||||
		return mockUser, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return models.User{}, models.ErrNoRecord
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) GetById(id int64) (models.User, error) {
 | 
			
		||||
	switch id {
 | 
			
		||||
	case 1:
 | 
			
		||||
		return mockUser, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return models.User{}, models.ErrNoRecord
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) GetAll() ([]models.User, error) {
 | 
			
		||||
	return []models.User{mockUser}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) Authenticate(email, password string) (int64, error) {
 | 
			
		||||
	if email == "test@example.com" && password == "password" {
 | 
			
		||||
		return 1, nil
 | 
			
		||||
	}
 | 
			
		||||
	return 0, models.ErrInvalidCredentials
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) Exists(id int64) (bool, error) {
 | 
			
		||||
	switch id {
 | 
			
		||||
	case 1:
 | 
			
		||||
		return true, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) GetSettings(userId int64) (models.UserSettings, error) {
 | 
			
		||||
	return mockUserSettings, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) UpdateUserSettings(userId int64, settings models.UserSettings) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) UpdateSetting(userId int64, setting models.Setting, value string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										77
									
								
								internal/models/mocks/website.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								internal/models/mocks/website.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,77 @@
 | 
			
		||||
package mocks
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"git.32bit.cafe/32bitcafe/guestbook/internal/models"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var mockGuestbook = models.Guestbook{
 | 
			
		||||
	ID:        1,
 | 
			
		||||
	ShortId:   1,
 | 
			
		||||
	UserId:    1,
 | 
			
		||||
	WebsiteId: 1,
 | 
			
		||||
	Created:   time.Now(),
 | 
			
		||||
	IsActive:  true,
 | 
			
		||||
	Settings: models.GuestbookSettings{
 | 
			
		||||
		IsCommentingEnabled:   true,
 | 
			
		||||
		IsVisible:             true,
 | 
			
		||||
		FilteredWords:         make([]string, 0),
 | 
			
		||||
		AllowRemoteHostAccess: true,
 | 
			
		||||
	},
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var mockWebsite = models.Website{
 | 
			
		||||
	ID:         1,
 | 
			
		||||
	ShortId:    1,
 | 
			
		||||
	Name:       "Example",
 | 
			
		||||
	SiteUrl:    "example.com",
 | 
			
		||||
	AuthorName: "John Test",
 | 
			
		||||
	UserId:     1,
 | 
			
		||||
	Created:    time.Now(),
 | 
			
		||||
	Guestbook:  mockGuestbook,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type WebsiteModel struct{}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) Insert(shortId uint64, userId int64, siteName, siteUrl, authorName string) (int64, error) {
 | 
			
		||||
	return 2, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) Get(shortId uint64) (models.Website, error) {
 | 
			
		||||
	switch shortId {
 | 
			
		||||
	case 1:
 | 
			
		||||
		return mockWebsite, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return models.Website{}, models.ErrNoRecord
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) GetAllUser(userId int64) ([]models.Website, error) {
 | 
			
		||||
	return []models.Website{mockWebsite}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) GetById(id int64) (models.Website, error) {
 | 
			
		||||
	switch id {
 | 
			
		||||
	case 1:
 | 
			
		||||
		return mockWebsite, nil
 | 
			
		||||
	default:
 | 
			
		||||
		return models.Website{}, models.ErrNoRecord
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) GetAll() ([]models.Website, error) {
 | 
			
		||||
	return []models.Website{mockWebsite}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) InitializeSettingsMap() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) UpdateGuestbookSettings(guestbookId int64, settings models.GuestbookSettings) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) UpdateSetting(guestbookId int64, setting models.Setting, value string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@ -35,6 +35,18 @@ type UserModel struct {
 | 
			
		||||
	Settings map[string]Setting
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type UserModelInterface interface {
 | 
			
		||||
	InitializeSettingsMap() error
 | 
			
		||||
	Insert(shortId uint64, username string, email string, password string, settings UserSettings) error
 | 
			
		||||
	Get(shortId uint64) (User, error)
 | 
			
		||||
	GetById(id int64) (User, error)
 | 
			
		||||
	GetAll() ([]User, error)
 | 
			
		||||
	Authenticate(email, password string) (int64, error)
 | 
			
		||||
	Exists(id int64) (bool, error)
 | 
			
		||||
	UpdateUserSettings(userId int64, settings UserSettings) error
 | 
			
		||||
	UpdateSetting(userId int64, setting Setting, value string) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *UserModel) InitializeSettingsMap() error {
 | 
			
		||||
	if m.Settings == nil {
 | 
			
		||||
		m.Settings = make(map[string]Setting)
 | 
			
		||||
 | 
			
		||||
@ -90,6 +90,16 @@ func (m *WebsiteModel) InitializeSettingsMap() error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type WebsiteModelInterface interface {
 | 
			
		||||
	Insert(shortId uint64, userId int64, siteName, siteUrl, authorName string) (int64, error)
 | 
			
		||||
	Get(shortId uint64) (Website, error)
 | 
			
		||||
	GetAllUser(userId int64) ([]Website, error)
 | 
			
		||||
	GetAll() ([]Website, error)
 | 
			
		||||
	InitializeSettingsMap() error
 | 
			
		||||
	UpdateGuestbookSettings(guestbookId int64, settings GuestbookSettings) error
 | 
			
		||||
	UpdateSetting(guestbookId int64, setting Setting, value string) error
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (m *WebsiteModel) Insert(shortId uint64, userId int64, siteName, siteUrl, authorName string) (int64, error) {
 | 
			
		||||
	stmt := `INSERT INTO websites (ShortId, Name, SiteUrl, AuthorName, UserId, Created)
 | 
			
		||||
			VALUES (?, ?, ?, ?, ?, ?)`
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user