aboutsummaryrefslogtreecommitdiffhomepage
path: root/backend/api/handler_test.go
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-02-20 23:32:22 +0900
committernsfisis <nsfisis@gmail.com>2026-02-20 23:32:22 +0900
commit8e73d12a703e90ad908962143951178c13d0d6fe (patch)
tree8bed43aa4b115f8bc50ed258aa192a94b6d2903e /backend/api/handler_test.go
parentaa07ba2e0a40b0097a4f9aee3c06dcbd9a749105 (diff)
downloadphperkaigi-2026-albatross-8e73d12a703e90ad908962143951178c13d0d6fe.tar.gz
phperkaigi-2026-albatross-8e73d12a703e90ad908962143951178c13d0d6fe.tar.zst
phperkaigi-2026-albatross-8e73d12a703e90ad908962143951178c13d0d6fe.zip
feat: add user submission history page
Allow users to view their own past submissions (code, size, status, timestamp) for each game. Adds API endpoint, backend handler, SQL query, and frontend page with expandable code display. 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.go166
1 files changed, 155 insertions, 11 deletions
diff --git a/backend/api/handler_test.go b/backend/api/handler_test.go
index 2415710..a28d605 100644
--- a/backend/api/handler_test.go
+++ b/backend/api/handler_test.go
@@ -16,17 +16,18 @@ import (
// mockQuerier implements db.Querier for testing.
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)
- getTournamentByIDFunc func(ctx context.Context, tournamentID int32) (db.Tournament, error)
- listTournamentEntriesFunc func(ctx context.Context, tournamentID int32) ([]db.ListTournamentEntriesRow, error)
- listTournamentMatchesFunc func(ctx context.Context, tournamentID int32) ([]db.TournamentMatch, error)
+ 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)
+ getTournamentByIDFunc func(ctx context.Context, tournamentID int32) (db.Tournament, error)
+ listTournamentEntriesFunc func(ctx context.Context, tournamentID int32) ([]db.ListTournamentEntriesRow, error)
+ listTournamentMatchesFunc func(ctx context.Context, tournamentID int32) ([]db.TournamentMatch, error)
+ getSubmissionsByGameIDAndUserIDFunc func(ctx context.Context, arg db.GetSubmissionsByGameIDAndUserIDParams) ([]db.Submission, error)
}
func (m *mockQuerier) GetGameByID(ctx context.Context, gameID int32) (db.GetGameByIDRow, error) {
@@ -85,6 +86,13 @@ func (m *mockQuerier) GetLatestStatesOfMainPlayers(ctx context.Context, gameID i
return nil, nil
}
+func (m *mockQuerier) GetSubmissionsByGameIDAndUserID(ctx context.Context, arg db.GetSubmissionsByGameIDAndUserIDParams) ([]db.Submission, error) {
+ if m.getSubmissionsByGameIDAndUserIDFunc != nil {
+ return m.getSubmissionsByGameIDAndUserIDFunc(ctx, arg)
+ }
+ return nil, nil
+}
+
func (m *mockQuerier) GetTournamentByID(ctx context.Context, tournamentID int32) (db.Tournament, error) {
if m.getTournamentByIDFunc != nil {
return m.getTournamentByIDFunc(ctx, tournamentID)
@@ -137,6 +145,142 @@ func (m *mockAuthenticator) Login(_ context.Context, _, _ string) (int, error) {
return m.loginResult, m.loginErr
}
+func TestGetGamePlaySubmissions_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.GetGamePlaySubmissions(context.Background(), GetGamePlaySubmissionsRequestObject{
+ GameID: 999,
+ }, user)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ if _, ok := resp.(GetGamePlaySubmissions404JSONResponse); !ok {
+ t.Errorf("expected 404 response, got %T", resp)
+ }
+}
+
+func TestGetGamePlaySubmissions_Empty(t *testing.T) {
+ h := Handler{
+ q: &mockQuerier{
+ getGameByIDFunc: func(_ context.Context, _ int32) (db.GetGameByIDRow, error) {
+ return db.GetGameByIDRow{
+ GameID: 1,
+ Language: "php",
+ }, nil
+ },
+ },
+ txm: &mockTxManager{},
+ hub: &mockGameHub{},
+ auth: &mockAuthenticator{},
+ conf: &config.Config{},
+ }
+ user := &db.User{UserID: 1}
+ resp, err := h.GetGamePlaySubmissions(context.Background(), GetGamePlaySubmissionsRequestObject{
+ GameID: 1,
+ }, user)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ okResp, ok := resp.(GetGamePlaySubmissions200JSONResponse)
+ if !ok {
+ t.Fatalf("expected 200 response, got %T", resp)
+ }
+ if len(okResp.Submissions) != 0 {
+ t.Errorf("expected 0 submissions, got %d", len(okResp.Submissions))
+ }
+}
+
+func TestGetGamePlaySubmissions_WithSubmissions(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",
+ }, nil
+ },
+ getSubmissionsByGameIDAndUserIDFunc: func(_ context.Context, arg db.GetSubmissionsByGameIDAndUserIDParams) ([]db.Submission, error) {
+ if arg.GameID != 1 || arg.UserID != 42 {
+ t.Errorf("unexpected query params: game_id=%d, user_id=%d", arg.GameID, arg.UserID)
+ }
+ return []db.Submission{
+ {
+ SubmissionID: 10,
+ GameID: 1,
+ UserID: 42,
+ Code: "<?php echo 1;",
+ CodeSize: 14,
+ Status: "success",
+ CreatedAt: pgtype.Timestamp{Time: now, Valid: true},
+ },
+ {
+ SubmissionID: 9,
+ GameID: 1,
+ UserID: 42,
+ Code: "<?php echo 'hello';",
+ CodeSize: 20,
+ Status: "wrong_answer",
+ CreatedAt: pgtype.Timestamp{Time: now.Add(-5 * time.Minute), Valid: true},
+ },
+ }, nil
+ },
+ },
+ txm: &mockTxManager{},
+ hub: &mockGameHub{},
+ auth: &mockAuthenticator{},
+ conf: &config.Config{},
+ }
+ user := &db.User{UserID: 42}
+ resp, err := h.GetGamePlaySubmissions(context.Background(), GetGamePlaySubmissionsRequestObject{
+ GameID: 1,
+ }, user)
+ if err != nil {
+ t.Fatalf("unexpected error: %v", err)
+ }
+ okResp, ok := resp.(GetGamePlaySubmissions200JSONResponse)
+ if !ok {
+ t.Fatalf("expected 200 response, got %T", resp)
+ }
+ if len(okResp.Submissions) != 2 {
+ t.Fatalf("expected 2 submissions, got %d", len(okResp.Submissions))
+ }
+
+ s0 := okResp.Submissions[0]
+ if s0.SubmissionID != 10 {
+ t.Errorf("expected submission_id 10, got %d", s0.SubmissionID)
+ }
+ if s0.GameID != 1 {
+ t.Errorf("expected game_id 1, got %d", s0.GameID)
+ }
+ if s0.Code != "<?php echo 1;" {
+ t.Errorf("expected code '<?php echo 1;', got %q", s0.Code)
+ }
+ if s0.CodeSize != 14 {
+ t.Errorf("expected code_size 14, got %d", s0.CodeSize)
+ }
+ if s0.Status != Success {
+ t.Errorf("expected status 'success', got %q", s0.Status)
+ }
+ if s0.CreatedAt != now.Unix() {
+ t.Errorf("expected created_at %d, got %d", now.Unix(), s0.CreatedAt)
+ }
+
+ s1 := okResp.Submissions[1]
+ if s1.SubmissionID != 9 {
+ t.Errorf("expected submission_id 9, got %d", s1.SubmissionID)
+ }
+ if s1.Status != WrongAnswer {
+ t.Errorf("expected status 'wrong_answer', got %q", s1.Status)
+ }
+}
+
func TestPostGamePlaySubmit_GameNotFound(t *testing.T) {
h := Handler{
q: &mockQuerier{},