summaryrefslogtreecommitdiff
path: root/internal/auth/auth_test.go
diff options
context:
space:
mode:
authorPeter Stone <thepeterstone@gmail.com>2026-01-20 15:18:57 -1000
committerPeter Stone <thepeterstone@gmail.com>2026-01-20 15:18:57 -1000
commit78e8f597ff28f1b8406f5cfbf934adc22abdf85b (patch)
treef3b7dfff2c460e2d8752b61c131e80a73fa6b08d /internal/auth/auth_test.go
parent08bbcf18b1207153983261652b4a43a9b36f386c (diff)
Add CSRF protection and auth unit tests
Add CSRF token middleware for state-changing request protection, integrate tokens into templates and HTMX headers, and add unit tests for authentication service and handlers. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Diffstat (limited to 'internal/auth/auth_test.go')
-rw-r--r--internal/auth/auth_test.go108
1 files changed, 108 insertions, 0 deletions
diff --git a/internal/auth/auth_test.go b/internal/auth/auth_test.go
new file mode 100644
index 0000000..505efe3
--- /dev/null
+++ b/internal/auth/auth_test.go
@@ -0,0 +1,108 @@
+package auth
+
+import (
+ "database/sql"
+ "errors"
+ "testing"
+ "time"
+
+ "github.com/DATA-DOG/go-sqlmock"
+ "golang.org/x/crypto/bcrypt"
+)
+
+func TestAuthenticate(t *testing.T) {
+ db, mock, err := sqlmock.New()
+ if err != nil {
+ t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+ }
+ defer db.Close()
+
+ service := NewService(db)
+
+ password := "secret"
+ hash, _ := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost)
+
+ rows := sqlmock.NewRows([]string{"id", "username", "password_hash", "created_at"}).
+ AddRow(1, "testuser", string(hash), time.Now())
+
+ mock.ExpectQuery("SELECT id, username, password_hash, created_at FROM users WHERE username = ?").
+ WithArgs("testuser").
+ WillReturnRows(rows)
+
+ user, err := service.Authenticate("testuser", password)
+ if err != nil {
+ t.Errorf("expected no error, got %v", err)
+ }
+ if user == nil {
+ t.Errorf("expected user, got nil")
+ }
+ if user.Username != "testuser" {
+ t.Errorf("expected username testuser, got %s", user.Username)
+ }
+
+ if err := mock.ExpectationsWereMet(); err != nil {
+ t.Errorf("there were unfulfilled expectations: %s", err)
+ }
+}
+
+func TestAuthenticate_InvalidCredentials(t *testing.T) {
+ db, mock, err := sqlmock.New()
+ if err != nil {
+ t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+ }
+ defer db.Close()
+
+ service := NewService(db)
+
+ mock.ExpectQuery("SELECT id, username, password_hash, created_at FROM users WHERE username = ?").
+ WithArgs("nonexistent").
+ WillReturnError(sql.ErrNoRows)
+
+ _, err = service.Authenticate("nonexistent", "password")
+ if !errors.Is(err, ErrInvalidCredentials) {
+ t.Errorf("expected ErrInvalidCredentials, got %v", err)
+ }
+
+ if err := mock.ExpectationsWereMet(); err != nil {
+ t.Errorf("there were unfulfilled expectations: %s", err)
+ }
+}
+
+func TestCreateUser(t *testing.T) {
+ db, mock, err := sqlmock.New()
+ if err != nil {
+ t.Fatalf("an error '%s' was not expected when opening a stub database connection", err)
+ }
+ defer db.Close()
+
+ service := NewService(db)
+
+ // Expect check if user exists
+ mock.ExpectQuery("SELECT id, username, password_hash, created_at FROM users WHERE username = ?").
+ WithArgs("newuser").
+ WillReturnError(sql.ErrNoRows)
+
+ // Expect insert
+ mock.ExpectExec("INSERT INTO users").
+ WithArgs("newuser", sqlmock.AnyArg()).
+ WillReturnResult(sqlmock.NewResult(1, 1))
+
+ // Expect retrieve created user
+ rows := sqlmock.NewRows([]string{"id", "username", "password_hash", "created_at"}).
+ AddRow(1, "newuser", "hashedpassword", time.Now())
+ mock.ExpectQuery("SELECT id, username, password_hash, created_at FROM users WHERE id = ?").
+ WithArgs(1).
+ WillReturnRows(rows)
+
+ user, err := service.CreateUser("newuser", "password")
+ if err != nil {
+ t.Errorf("expected no error, got %v", err)
+ }
+ if user.Username != "newuser" {
+ t.Errorf("expected username newuser, got %s", user.Username)
+ }
+
+ if err := mock.ExpectationsWereMet(); err != nil {
+ t.Errorf("there were unfulfilled expectations: %s", err)
+ }
+}