add end-to-end test for guestbook view and user registration
This commit is contained in:
		
							parent
							
								
									1983662216
								
							
						
					
					
						commit
						d574dab3a7
					
				| @ -1,6 +1,7 @@ | |||||||
| package main | package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
|  | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"testing" | 	"testing" | ||||||
| 
 | 
 | ||||||
| @ -17,3 +18,43 @@ func TestPing(t *testing.T) { | |||||||
| 	assert.Equal(t, code, http.StatusOK) | 	assert.Equal(t, code, http.StatusOK) | ||||||
| 	assert.Equal(t, body, "OK") | 	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) | ||||||
|  | 			} | ||||||
|  | 		}) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | } | ||||||
| @ -2,17 +2,39 @@ package main | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"bytes" | 	"bytes" | ||||||
|  | 	"html" | ||||||
| 	"io" | 	"io" | ||||||
| 	"log/slog" | 	"log/slog" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/http/cookiejar" | 	"net/http/cookiejar" | ||||||
| 	"net/http/httptest" | 	"net/http/httptest" | ||||||
|  | 	"net/url" | ||||||
|  | 	"regexp" | ||||||
| 	"testing" | 	"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 { | 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{ | 	return &application{ | ||||||
| 		logger:            slog.New(slog.NewTextHandler(io.Discard, nil)), | 		logger:            slog.New(slog.NewTextHandler(io.Discard, nil)), | ||||||
|  | 		sessionManager:    sessionManager, | ||||||
|  | 		websites:          &mocks.WebsiteModel{}, | ||||||
|  | 		guestbooks:        &mocks.GuestbookModel{}, | ||||||
|  | 		users:             &mocks.UserModel{}, | ||||||
|  | 		guestbookComments: &mocks.GuestbookCommentModel{}, | ||||||
|  | 		formDecoder:       formDecoder, | ||||||
|  | 		timezones:         getAvailableTimezones(), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -48,3 +70,28 @@ func (ts *testServer) get(t *testing.T, urlPath string) (int, http.Header, strin | |||||||
| 
 | 
 | ||||||
| 	return rs.StatusCode, rs.Header, string(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 | 	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, | func (m *GuestbookCommentModel) Insert(shortId uint64, guestbookId, parentId int64, authorName, | ||||||
| 	authorEmail, authorSite, commentText, pageUrl string, isPublished bool) (int64, error) { | 	authorEmail, authorSite, commentText, pageUrl string, isPublished bool) (int64, error) { | ||||||
| 	stmt := `INSERT INTO guestbook_comments (ShortId, GuestbookId, ParentId, AuthorName, | 	stmt := `INSERT INTO guestbook_comments (ShortId, GuestbookId, ParentId, AuthorName, | ||||||
|  | |||||||
							
								
								
									
										66
									
								
								internal/models/mocks/guestbook.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								internal/models/mocks/guestbook.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,66 @@ | |||||||
|  | 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, | ||||||
|  | 	}, | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type GuestbookModel struct{} | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) InitializeSettingsMap() error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) Insert(shortId uint64, userId int64, websiteId int64, settings models.GuestbookSettings) (int64, error) { | ||||||
|  | 	return 2, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) Get(shortId uint64) (models.Guestbook, error) { | ||||||
|  | 	switch shortId { | ||||||
|  | 	case 1: | ||||||
|  | 		return mockGuestbook, nil | ||||||
|  | 	default: | ||||||
|  | 		return models.Guestbook{}, models.ErrNoRecord | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) GetAll(userId int64) ([]models.Guestbook, error) { | ||||||
|  | 	switch userId { | ||||||
|  | 	case 1: | ||||||
|  | 		return []models.Guestbook{mockGuestbook}, nil | ||||||
|  | 	default: | ||||||
|  | 		return []models.Guestbook{}, models.ErrNoRecord | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) UpdateGuestbookSettings(guestbookId int64, settings models.GuestbookSettings) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) UpdateSetting(guestbookId int64, setting models.Setting, value string) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) AddFilteredWord(guestbookId int64, word string) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *GuestbookModel) RemoveFilteredWord(guestbookId int64, word string) error { | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
							
								
								
									
										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 | ||||||
|  | } | ||||||
							
								
								
									
										50
									
								
								internal/models/mocks/website.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								internal/models/mocks/website.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,50 @@ | |||||||
|  | package mocks | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"git.32bit.cafe/32bitcafe/guestbook/internal/models" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | 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 | ||||||
|  | } | ||||||
| @ -35,6 +35,19 @@ type UserModel struct { | |||||||
| 	Settings map[string]Setting | 	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) | ||||||
|  | 	GetSettings(userId int64) (UserSettings, error) | ||||||
|  | 	UpdateUserSettings(userId int64, settings UserSettings) error | ||||||
|  | 	UpdateSetting(userId int64, setting Setting, value string) error | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (m *UserModel) InitializeSettingsMap() error { | func (m *UserModel) InitializeSettingsMap() error { | ||||||
| 	if m.Settings == nil { | 	if m.Settings == nil { | ||||||
| 		m.Settings = make(map[string]Setting) | 		m.Settings = make(map[string]Setting) | ||||||
|  | |||||||
| @ -90,6 +90,14 @@ func (m *WebsiteModel) InitializeSettingsMap() error { | |||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | type WebsiteModelInterface interface { | ||||||
|  | 	Insert(shortId uint64, userId int64, siteName, siteUrl, authorName string) (int64, error) | ||||||
|  | 	Get(shortId uint64) (Website, error) | ||||||
|  | 	GetById(id int64) (Website, error) | ||||||
|  | 	GetAllUser(userId int64) ([]Website, error) | ||||||
|  | 	GetAll() ([]Website, error) | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (m *WebsiteModel) Insert(shortId uint64, userId int64, siteName, siteUrl, authorName string) (int64, 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) | 	stmt := `INSERT INTO websites (ShortId, Name, SiteUrl, AuthorName, UserId, Created) | ||||||
| 			VALUES (?, ?, ?, ?, ?, ?)` | 			VALUES (?, ?, ?, ?, ?, ?)` | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user