package app import ( "context" "errors" "net/http" "net/http/httptest" "strings" "testing" "evening_detective_server/internal/repos/teams_repo" "evening_detective_server/internal/services/game_service" ) func TestTeamRegistrationHandler(t *testing.T) { valid := `{"name":"Команда","email":"captain@example.org","participantsCount":5}` for _, tc := range []struct { name, token, auth, gameID, deliveryID, body string serviceErr error want int called bool }{ {"success", "secret", "Bearer secret", "42", "delivery", valid, nil, 200, true}, {"disabled", "", "Bearer secret", "42", "delivery", valid, nil, 503, false}, {"no token", "secret", "", "42", "delivery", valid, nil, 401, false}, {"wrong token", "secret", "Bearer wrong", "42", "delivery", valid, nil, 401, false}, {"bad game", "secret", "Bearer secret", "0", "delivery", valid, nil, 400, false}, {"no delivery", "secret", "Bearer secret", "42", "", valid, nil, 400, false}, {"malformed", "secret", "Bearer secret", "42", "delivery", `{`, nil, 400, false}, {"extra object", "secret", "Bearer secret", "42", "delivery", valid + ` {}`, nil, 400, false}, {"unknown field", "secret", "Bearer secret", "42", "delivery", `{"extra":1}`, nil, 400, false}, {"too large", "secret", "Bearer secret", "42", "delivery", `{"name":"` + strings.Repeat("a", 17000) + `"}`, nil, 400, false}, {"invalid fields", "secret", "Bearer secret", "42", "delivery", valid, game_service.ErrInvalidRegistration, 400, true}, {"closed", "secret", "Bearer secret", "42", "delivery", valid, teams_repo.ErrRegistrationClosed, 409, true}, {"internal", "secret", "Bearer secret", "42", "delivery", valid, errors.New("private database detail"), 500, true}, } { t.Run(tc.name, func(t *testing.T) { called := false mux := http.NewServeMux() mux.Handle("POST /api/games/{id}/registrations", TeamRegistrationHandler(tc.token, func(ctx context.Context, gameID int, deliveryID string, registration game_service.Registration) (int, error) { called = true if gameID != 42 || deliveryID != "delivery" || registration.Name != "Команда" || registration.Email != "captain@example.org" || registration.ParticipantsCount != 5 { t.Fatal("incorrect registration passed to service") } if _, ok := ctx.Deadline(); !ok { t.Fatal("missing deadline") } return 7, tc.serviceErr })) req := httptest.NewRequest(http.MethodPost, "/api/games/"+tc.gameID+"/registrations", strings.NewReader(tc.body)) req.Header.Set("Authorization", tc.auth) req.Header.Set("X-Delivery-Id", tc.deliveryID) res := httptest.NewRecorder() mux.ServeHTTP(res, req) if res.Code != tc.want || called != tc.called { t.Fatalf("status=%d called=%v body=%s", res.Code, called, res.Body.String()) } if tc.want == 200 && res.Body.String() != "{\"id\":7}\n" { t.Fatal(res.Body.String()) } if strings.Contains(res.Body.String(), "private database detail") { t.Fatal("private error exposed") } if tc.name == "malformed" && !strings.Contains(res.Body.String(), "unexpected EOF") { t.Fatalf("malformed JSON reason missing: %s", res.Body.String()) } if tc.name == "unknown field" && !strings.Contains(res.Body.String(), "unknown field") { t.Fatalf("unknown field reason missing: %s", res.Body.String()) } if tc.name == "extra object" && !strings.Contains(res.Body.String(), "лишние данные") { t.Fatalf("extra data reason missing: %s", res.Body.String()) } }) } } func TestCaptainContactOnlyInGameDetails(t *testing.T) { team := &game_service.Team{ID: 1, CaptainEmail: "captain@example.org", ParticipantsCount: 5} details := mapGame(&game_service.Game{Teams: []*game_service.Team{team}}) if details.Teams[0].CaptainEmail != team.CaptainEmail || details.Teams[0].ParticipantsCount != 5 { t.Fatal("registration details missing from organizer view") } if mapTeam(team).CaptainEmail != "" { t.Fatal("captain email exposed in shared team/results mapping") } }