Unit testing #23
| @ -1,6 +1,7 @@ | ||||
| package main | ||||
| 
 | ||||
| import ( | ||||
| 	"fmt" | ||||
| 	"net/http" | ||||
| 	"testing" | ||||
| 
 | ||||
| @ -17,3 +18,43 @@ func TestPing(t *testing.T) { | ||||
| 	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) | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| 
 | ||||
| } | ||||
| @ -2,17 +2,39 @@ 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)), | ||||
| 		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) | ||||
| } | ||||
| 
 | ||||
| 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, | ||||
|  | ||||
							
								
								
									
										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 | ||||
| } | ||||
| 
 | ||||
| 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 { | ||||
| 	if m.Settings == nil { | ||||
| 		m.Settings = make(map[string]Setting) | ||||
|  | ||||
| @ -90,6 +90,14 @@ 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) | ||||
| 	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) { | ||||
| 	stmt := `INSERT INTO websites (ShortId, Name, SiteUrl, AuthorName, UserId, Created) | ||||
| 			VALUES (?, ?, ?, ?, ?, ?)` | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user