diff options
| author | nsfisis <nsfisis@gmail.com> | 2026-02-16 22:02:58 +0900 |
|---|---|---|
| committer | nsfisis <nsfisis@gmail.com> | 2026-02-16 22:02:58 +0900 |
| commit | db87f85aa7055e597800481b8cc6d006c70bcc88 (patch) | |
| tree | 57630fde35a39e445c177a278cacf243b7fb0d52 /backend/api/handler_test.go | |
| parent | 08c121c21a7e429e43e2d51fa4a3d8bd945c5d01 (diff) | |
| download | phperkaigi-2026-albatross-db87f85aa7055e597800481b8cc6d006c70bcc88.tar.gz phperkaigi-2026-albatross-db87f85aa7055e597800481b8cc6d006c70bcc88.tar.zst phperkaigi-2026-albatross-db87f85aa7055e597800481b8cc6d006c70bcc88.zip | |
test(backend): add unit tests for auth_middleware, fortee, processor, account, and more handlers
Cover previously untested code: SessionCookieMiddleware, context helpers,
downloadFile, addAcceptHeader, doProcessTaskRunTestcase, updateSubmissionAndGameState,
PostLogout, GetGames, PostGamePlayCode, GetGameWatchRanking, GetGameWatchLatestStates.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Diffstat (limited to 'backend/api/handler_test.go')
| -rw-r--r-- | backend/api/handler_test.go | 354 |
1 files changed, 354 insertions, 0 deletions
diff --git a/backend/api/handler_test.go b/backend/api/handler_test.go index a68dfa0..2340d33 100644 --- a/backend/api/handler_test.go +++ b/backend/api/handler_test.go @@ -18,6 +18,12 @@ type mockQuerier struct { db.Querier getGameByIDFunc func(ctx context.Context, gameID int32) (db.GetGameByIDRow, error) listMainPlayersFunc func(ctx context.Context, gameIDs []int32) ([]db.ListMainPlayersRow, error) + listPublicGamesFunc func(ctx context.Context) ([]db.ListPublicGamesRow, error) + deleteSessionFunc func(ctx context.Context, sessionID string) error + getLatestStateFunc func(ctx context.Context, arg db.GetLatestStateParams) (db.GetLatestStateRow, error) + updateCodeFunc func(ctx context.Context, arg db.UpdateCodeParams) error + getRankingFunc func(ctx context.Context, gameID int32) ([]db.GetRankingRow, error) + getLatestStatesFunc func(ctx context.Context, gameID int32) ([]db.GetLatestStatesOfMainPlayersRow, error) } func (m *mockQuerier) GetGameByID(ctx context.Context, gameID int32) (db.GetGameByIDRow, error) { @@ -34,6 +40,48 @@ func (m *mockQuerier) ListMainPlayers(ctx context.Context, gameIDs []int32) ([]d return nil, nil } +func (m *mockQuerier) ListPublicGames(ctx context.Context) ([]db.ListPublicGamesRow, error) { + if m.listPublicGamesFunc != nil { + return m.listPublicGamesFunc(ctx) + } + return nil, nil +} + +func (m *mockQuerier) DeleteSession(ctx context.Context, sessionID string) error { + if m.deleteSessionFunc != nil { + return m.deleteSessionFunc(ctx, sessionID) + } + return nil +} + +func (m *mockQuerier) GetLatestState(ctx context.Context, arg db.GetLatestStateParams) (db.GetLatestStateRow, error) { + if m.getLatestStateFunc != nil { + return m.getLatestStateFunc(ctx, arg) + } + return db.GetLatestStateRow{}, pgx.ErrNoRows +} + +func (m *mockQuerier) UpdateCode(ctx context.Context, arg db.UpdateCodeParams) error { + if m.updateCodeFunc != nil { + return m.updateCodeFunc(ctx, arg) + } + return nil +} + +func (m *mockQuerier) GetRanking(ctx context.Context, gameID int32) ([]db.GetRankingRow, error) { + if m.getRankingFunc != nil { + return m.getRankingFunc(ctx, gameID) + } + return nil, nil +} + +func (m *mockQuerier) GetLatestStatesOfMainPlayers(ctx context.Context, gameID int32) ([]db.GetLatestStatesOfMainPlayersRow, error) { + if m.getLatestStatesFunc != nil { + return m.getLatestStatesFunc(ctx, gameID) + } + return nil, nil +} + // mockTxManager implements db.TxManager for testing. type mockTxManager struct{} @@ -371,3 +419,309 @@ func TestPostLogin_AuthFailure(t *testing.T) { t.Errorf("expected 401 response, got %T", resp) } } + +func TestPostLogout(t *testing.T) { + h := Handler{ + q: &mockQuerier{}, + txm: &mockTxManager{}, + hub: &mockGameHub{}, + auth: &mockAuthenticator{}, + conf: &config.Config{BasePath: "/"}, + } + user := &db.User{UserID: 1} + // Set session ID in context + ctx := context.WithValue(context.Background(), sessionIDContextKey{}, "hashed-session") + resp, err := h.PostLogout(ctx, PostLogoutRequestObject{}, user) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if _, ok := resp.(postLogoutCookieResponse); !ok { + t.Errorf("expected postLogoutCookieResponse, got %T", resp) + } +} + +func TestGetGames_Empty(t *testing.T) { + h := Handler{ + q: &mockQuerier{}, + txm: &mockTxManager{}, + hub: &mockGameHub{}, + auth: &mockAuthenticator{}, + conf: &config.Config{}, + } + user := &db.User{UserID: 1} + resp, err := h.GetGames(context.Background(), GetGamesRequestObject{}, user) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + okResp, ok := resp.(GetGames200JSONResponse) + if !ok { + t.Fatalf("expected 200 response, got %T", resp) + } + if len(okResp.Games) != 0 { + t.Errorf("expected 0 games, got %d", len(okResp.Games)) + } +} + +func TestGetGames_WithGames(t *testing.T) { + now := time.Now() + h := Handler{ + q: &mockQuerier{ + listPublicGamesFunc: func(_ context.Context) ([]db.ListPublicGamesRow, error) { + return []db.ListPublicGamesRow{ + { + GameID: 1, + GameType: "golf", + IsPublic: true, + DisplayName: "Game 1", + DurationSeconds: 300, + StartedAt: pgtype.Timestamp{Time: now, Valid: true}, + ProblemID: 10, + Title: "Problem 1", + Description: "desc", + Language: "php", + SampleCode: "<?php", + }, + }, nil + }, + listMainPlayersFunc: func(_ context.Context, _ []int32) ([]db.ListMainPlayersRow, error) { + return nil, nil + }, + }, + txm: &mockTxManager{}, + hub: &mockGameHub{}, + auth: &mockAuthenticator{}, + conf: &config.Config{}, + } + user := &db.User{UserID: 1} + resp, err := h.GetGames(context.Background(), GetGamesRequestObject{}, user) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + okResp, ok := resp.(GetGames200JSONResponse) + if !ok { + t.Fatalf("expected 200 response, got %T", resp) + } + if len(okResp.Games) != 1 { + t.Fatalf("expected 1 game, got %d", len(okResp.Games)) + } + if okResp.Games[0].DisplayName != "Game 1" { + t.Errorf("expected display name 'Game 1', got %q", okResp.Games[0].DisplayName) + } + if okResp.Games[0].StartedAt == nil { + t.Error("expected non-nil StartedAt") + } +} + +func TestGetGamePlayLatestState_NoState(t *testing.T) { + h := Handler{ + q: &mockQuerier{}, + txm: &mockTxManager{}, + hub: &mockGameHub{}, + auth: &mockAuthenticator{}, + conf: &config.Config{}, + } + user := &db.User{UserID: 1} + resp, err := h.GetGamePlayLatestState(context.Background(), GetGamePlayLatestStateRequestObject{GameID: 1}, user) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + okResp, ok := resp.(GetGamePlayLatestState200JSONResponse) + if !ok { + t.Fatalf("expected 200 response, got %T", resp) + } + if okResp.State.Code != "" { + t.Errorf("expected empty code, got %q", okResp.State.Code) + } + if okResp.State.Status != None { + t.Errorf("expected status 'none', got %q", okResp.State.Status) + } +} + +func TestPostGamePlayCode_GameNotFound(t *testing.T) { + h := Handler{ + q: &mockQuerier{}, + txm: &mockTxManager{}, + hub: &mockGameHub{}, + auth: &mockAuthenticator{}, + conf: &config.Config{}, + } + user := &db.User{UserID: 1} + resp, err := h.PostGamePlayCode(context.Background(), PostGamePlayCodeRequestObject{ + GameID: 999, + Body: &PostGamePlayCodeJSONRequestBody{Code: "test"}, + }, user) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if _, ok := resp.(PostGamePlayCode404JSONResponse); !ok { + t.Errorf("expected 404 response, got %T", resp) + } +} + +func TestPostGamePlayCode_GameNotRunning(t *testing.T) { + h := Handler{ + q: &mockQuerier{ + getGameByIDFunc: func(_ context.Context, _ int32) (db.GetGameByIDRow, error) { + return db.GetGameByIDRow{ + GameID: 1, + Language: "php", + StartedAt: pgtype.Timestamp{ + Valid: false, + }, + }, nil + }, + }, + txm: &mockTxManager{}, + hub: &mockGameHub{}, + auth: &mockAuthenticator{}, + conf: &config.Config{}, + } + user := &db.User{UserID: 1} + resp, err := h.PostGamePlayCode(context.Background(), PostGamePlayCodeRequestObject{ + GameID: 1, + Body: &PostGamePlayCodeJSONRequestBody{Code: "<?php echo 1;"}, + }, user) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if _, ok := resp.(PostGamePlayCode403JSONResponse); !ok { + t.Errorf("expected 403 response, got %T", resp) + } +} + +func TestPostGamePlayCode_Success(t *testing.T) { + now := time.Now() + var updatedCode string + h := Handler{ + q: &mockQuerier{ + getGameByIDFunc: func(_ context.Context, _ int32) (db.GetGameByIDRow, error) { + return db.GetGameByIDRow{ + GameID: 1, + Language: "php", + StartedAt: pgtype.Timestamp{Time: now, Valid: true}, + DurationSeconds: 600, + }, nil + }, + updateCodeFunc: func(_ context.Context, arg db.UpdateCodeParams) error { + updatedCode = arg.Code + return nil + }, + }, + txm: &mockTxManager{}, + hub: &mockGameHub{}, + auth: &mockAuthenticator{}, + conf: &config.Config{}, + } + user := &db.User{UserID: 1} + resp, err := h.PostGamePlayCode(context.Background(), PostGamePlayCodeRequestObject{ + GameID: 1, + Body: &PostGamePlayCodeJSONRequestBody{Code: "<?php echo 42;"}, + }, user) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if _, ok := resp.(PostGamePlayCode200Response); !ok { + t.Errorf("expected 200 response, got %T", resp) + } + if updatedCode != "<?php echo 42;" { + t.Errorf("expected code '<?php echo 42;', got %q", updatedCode) + } +} + +func TestGetGameWatchRanking_NotFound(t *testing.T) { + h := Handler{ + q: &mockQuerier{}, + txm: &mockTxManager{}, + hub: &mockGameHub{}, + auth: &mockAuthenticator{}, + conf: &config.Config{}, + } + user := &db.User{UserID: 1} + resp, err := h.GetGameWatchRanking(context.Background(), GetGameWatchRankingRequestObject{GameID: 999}, user) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if _, ok := resp.(GetGameWatchRanking404JSONResponse); !ok { + t.Errorf("expected 404 response, got %T", resp) + } +} + +func TestGetGameWatchRanking_EmptyRanking(t *testing.T) { + now := time.Now() + h := Handler{ + q: &mockQuerier{ + getGameByIDFunc: func(_ context.Context, _ int32) (db.GetGameByIDRow, error) { + return db.GetGameByIDRow{ + GameID: 1, + Language: "php", + StartedAt: pgtype.Timestamp{Time: now.Add(-10 * time.Minute), Valid: true}, + DurationSeconds: 300, + }, nil + }, + getRankingFunc: func(_ context.Context, _ int32) ([]db.GetRankingRow, error) { + return nil, nil + }, + }, + txm: &mockTxManager{}, + hub: &mockGameHub{}, + auth: &mockAuthenticator{}, + conf: &config.Config{}, + } + user := &db.User{UserID: 1} + resp, err := h.GetGameWatchRanking(context.Background(), GetGameWatchRankingRequestObject{GameID: 1}, user) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + okResp, ok := resp.(GetGameWatchRanking200JSONResponse) + if !ok { + t.Fatalf("expected 200 response, got %T", resp) + } + if len(okResp.Ranking) != 0 { + t.Errorf("expected empty ranking, got %d entries", len(okResp.Ranking)) + } +} + +func TestGetGameWatchLatestStates_Empty(t *testing.T) { + h := Handler{ + q: &mockQuerier{}, + txm: &mockTxManager{}, + hub: &mockGameHub{}, + auth: &mockAuthenticator{}, + conf: &config.Config{}, + } + user := &db.User{UserID: 1} + resp, err := h.GetGameWatchLatestStates(context.Background(), GetGameWatchLatestStatesRequestObject{GameID: 1}, user) + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + okResp, ok := resp.(GetGameWatchLatestStates200JSONResponse) + if !ok { + t.Fatalf("expected 200 response, got %T", resp) + } + if len(okResp.States) != 0 { + t.Errorf("expected 0 states, got %d", len(okResp.States)) + } +} + +func TestToNullableWith(t *testing.T) { + t.Run("nil value", func(t *testing.T) { + result := toNullableWith[int, string](nil, func(_ int) string { return "x" }) + if !result.IsNull() { + t.Error("expected null for nil input") + } + }) + t.Run("non-nil value", func(t *testing.T) { + x := 42 + result := toNullableWith(&x, func(_ int) string { return "hello" }) + if result.IsNull() { + t.Error("expected non-null for non-nil input") + } + v, err := result.Get() + if err != nil { + t.Fatalf("unexpected error: %v", err) + } + if v != "hello" { + t.Errorf("expected 'hello', got %q", v) + } + }) +} |
