diff options
Diffstat (limited to 'backend/auth')
| -rw-r--r-- | backend/auth/auth_test.go | 125 | ||||
| -rw-r--r-- | backend/auth/session_test.go | 47 |
2 files changed, 172 insertions, 0 deletions
diff --git a/backend/auth/auth_test.go b/backend/auth/auth_test.go new file mode 100644 index 0000000..00d4527 --- /dev/null +++ b/backend/auth/auth_test.go @@ -0,0 +1,125 @@ +package auth + +import ( + "context" + "errors" + "testing" + + "github.com/jackc/pgx/v5" + "golang.org/x/crypto/bcrypt" + + "albatross-2026-backend/db" +) + +type mockQuerier struct { + db.Querier + getUserAuthByUsernameFunc func(ctx context.Context, username string) (db.GetUserAuthByUsernameRow, error) +} + +func (m *mockQuerier) GetUserAuthByUsername(ctx context.Context, username string) (db.GetUserAuthByUsernameRow, error) { + if m.getUserAuthByUsernameFunc != nil { + return m.getUserAuthByUsernameFunc(ctx, username) + } + return db.GetUserAuthByUsernameRow{}, pgx.ErrNoRows +} + +type mockTxManager struct{} + +func (m *mockTxManager) RunInTx(_ context.Context, fn func(q db.Querier) error) error { + return fn(&mockQuerier{}) +} + +func TestLogin_PasswordAuth_Success(t *testing.T) { + hash, err := bcrypt.GenerateFromPassword([]byte("correct-password"), bcrypt.MinCost) + if err != nil { + t.Fatalf("failed to generate hash: %v", err) + } + hashStr := string(hash) + + a := NewAuthenticator( + &mockQuerier{ + getUserAuthByUsernameFunc: func(_ context.Context, _ string) (db.GetUserAuthByUsernameRow, error) { + return db.GetUserAuthByUsernameRow{ + UserID: 42, + AuthType: "password", + PasswordHash: &hashStr, + }, nil + }, + }, + &mockTxManager{}, + ) + + userID, err := a.Login(context.Background(), "testuser", "correct-password") + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if userID != 42 { + t.Errorf("expected userID 42, got %d", userID) + } +} + +func TestLogin_PasswordAuth_WrongPassword(t *testing.T) { + hash, err := bcrypt.GenerateFromPassword([]byte("correct-password"), bcrypt.MinCost) + if err != nil { + t.Fatalf("failed to generate hash: %v", err) + } + hashStr := string(hash) + + a := NewAuthenticator( + &mockQuerier{ + getUserAuthByUsernameFunc: func(_ context.Context, _ string) (db.GetUserAuthByUsernameRow, error) { + return db.GetUserAuthByUsernameRow{ + UserID: 42, + AuthType: "password", + PasswordHash: &hashStr, + }, nil + }, + }, + &mockTxManager{}, + ) + + _, err = a.Login(context.Background(), "testuser", "wrong-password") + if err == nil { + t.Fatal("expected error for wrong password, got nil") + } +} + +func TestLogin_PasswordAuth_NilHash(t *testing.T) { + a := NewAuthenticator( + &mockQuerier{ + getUserAuthByUsernameFunc: func(_ context.Context, _ string) (db.GetUserAuthByUsernameRow, error) { + return db.GetUserAuthByUsernameRow{ + UserID: 42, + AuthType: "password", + PasswordHash: nil, + }, nil + }, + }, + &mockTxManager{}, + ) + + _, err := a.Login(context.Background(), "testuser", "any") + if err == nil { + t.Fatal("expected error for nil password hash, got nil") + } + if err.Error() != "inconsistent data: password auth type but no password hash" { + t.Errorf("unexpected error message: %s", err.Error()) + } +} + +func TestLogin_DBError(t *testing.T) { + dbErr := errors.New("database connection failed") + a := NewAuthenticator( + &mockQuerier{ + getUserAuthByUsernameFunc: func(_ context.Context, _ string) (db.GetUserAuthByUsernameRow, error) { + return db.GetUserAuthByUsernameRow{}, dbErr + }, + }, + &mockTxManager{}, + ) + + _, err := a.Login(context.Background(), "testuser", "any") + if !errors.Is(err, dbErr) { + t.Errorf("expected db error, got: %v", err) + } +} diff --git a/backend/auth/session_test.go b/backend/auth/session_test.go new file mode 100644 index 0000000..2a47739 --- /dev/null +++ b/backend/auth/session_test.go @@ -0,0 +1,47 @@ +package auth + +import ( + "testing" +) + +func TestGenerateSessionID(t *testing.T) { + id, err := GenerateSessionID() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + // 32 bytes → 64 hex characters + if len(id) != 64 { + t.Errorf("expected session ID length 64, got %d", len(id)) + } +} + +func TestGenerateSessionID_Unique(t *testing.T) { + id1, err := GenerateSessionID() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + id2, err := GenerateSessionID() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if id1 == id2 { + t.Error("expected unique session IDs, got identical values") + } +} + +func TestHashSessionID(t *testing.T) { + raw := "abc123" + hashed := HashSessionID(raw) + // SHA-256 produces 32 bytes → 64 hex characters + if len(hashed) != 64 { + t.Errorf("expected hash length 64, got %d", len(hashed)) + } + // Same input should produce same hash + if hashed != HashSessionID(raw) { + t.Error("expected deterministic hash") + } + // Different input should produce different hash + if hashed == HashSessionID("different") { + t.Error("expected different hashes for different inputs") + } +} |
