From 071e7cc78d3f13fa782dbc6ca5fcec3a37263a4d Mon Sep 17 00:00:00 2001 From: nsfisis Date: Mon, 16 Feb 2026 20:05:39 +0900 Subject: test(backend): add unit tests for auth, config, ratelimit, game, and api Cover previously untested logic: session ID generation/hashing, password authentication, IP rate limiting, game state helpers, handler endpoints, task enqueue/result processing, and config loading. Co-Authored-By: Claude Opus 4.6 --- backend/auth/auth_test.go | 125 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 backend/auth/auth_test.go (limited to 'backend/auth/auth_test.go') 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) + } +} -- cgit v1.3.1