From c30ed08a3351313fd33be5c044c694397f872087 Mon Sep 17 00:00:00 2001 From: IgorPolyakov Date: Thu, 11 Jul 2024 20:56:15 +0300 Subject: [PATCH] add crud team test & fix error - close #31 --- internal/app/db/game.go | 2 +- internal/app/db/team.go | 5 -- internal/app/repository/game.go | 53 ++++++++------- internal/app/view/game.go | 24 ++----- test/server_integration_test.go | 113 ++++++++++++++++++++++++++++++++ 5 files changed, 147 insertions(+), 50 deletions(-) diff --git a/internal/app/db/game.go b/internal/app/db/game.go index e27f48f..76f24d6 100644 --- a/internal/app/db/game.go +++ b/internal/app/db/game.go @@ -15,5 +15,5 @@ type Game struct { type GameDetails struct { Game - TeamDetails []*TeamDetails + Teams []*Team } diff --git a/internal/app/db/team.go b/internal/app/db/team.go index b2d786b..6e0764f 100644 --- a/internal/app/db/team.go +++ b/internal/app/db/team.go @@ -11,8 +11,3 @@ type Team struct { SocialLinks string `db:"social_links"` AvatarUrl string `db:"avatar_url"` } - -type TeamDetails struct { - Team - Members []*User -} diff --git a/internal/app/repository/game.go b/internal/app/repository/game.go index 6064ccc..09e9456 100644 --- a/internal/app/repository/game.go +++ b/internal/app/repository/game.go @@ -7,6 +7,7 @@ import ( models "ctf01d/internal/app/db" + "github.com/google/uuid" openapi_types "github.com/oapi-codegen/runtime/types" ) @@ -35,12 +36,10 @@ func (r *gameRepo) Create(ctx context.Context, game *models.Game) error { func (r *gameRepo) GetGameDetails(ctx context.Context, id openapi_types.UUID) (*models.GameDetails, error) { query := ` - SELECT g.id, g.start_time, g.end_time, g.description, t.id, t.name, t.description, u.id, u.user_name + SELECT g.id, g.start_time, g.end_time, g.description, t.id, t.name, t.description FROM games g - JOIN team_games tg ON g.id = tg.game_id - JOIN teams t ON tg.team_id = t.id - JOIN profiles tm ON t.id = tm.current_team_id - JOIN users u ON tm.user_id = u.id + LEFT JOIN team_games tg ON g.id = tg.game_id + LEFT JOIN teams t ON tg.team_id = t.id WHERE g.id = $1; ` rows, err := r.db.QueryContext(ctx, query, id) @@ -50,39 +49,43 @@ func (r *gameRepo) GetGameDetails(ctx context.Context, id openapi_types.UUID) (* defer rows.Close() gameDetails := &models.GameDetails{} - teams := map[openapi_types.UUID]*models.TeamDetails{} + teams := make(map[uuid.UUID]models.Team) for rows.Next() { var gameId openapi_types.UUID var startTime, endTime time.Time var description string - var teamId openapi_types.UUID - var teamName string - var teamDescription string - var userId openapi_types.UUID - var userName string + var teamId sql.NullString + var teamName sql.NullString + var teamDescription sql.NullString - err := rows.Scan(&gameId, &startTime, &endTime, &description, &teamId, &teamName, &teamDescription, &userId, &userName) + err := rows.Scan(&gameId, &startTime, &endTime, &description, &teamId, &teamName, &teamDescription) if err != nil { return nil, err } - if team, ok := teams[teamId]; ok { - team.Members = append(team.Members, &models.User{Id: userId, Username: userName}) - } else { - newTeam := &models.TeamDetails{ - Team: models.Team{ - Id: teamId, - Name: teamName, - Description: teamDescription, - }, - Members: []*models.User{{Id: userId, Username: userName}}, + gameDetails.Id = id + gameDetails.StartTime = startTime + gameDetails.EndTime = endTime + gameDetails.Description = description + if teamId.Valid { + teamUUID, err := uuid.Parse(teamId.String) + if err != nil { + return nil, err } - - teams[teamId] = newTeam - gameDetails.TeamDetails = append(gameDetails.TeamDetails, newTeam) + team := models.Team{ + Id: teamUUID, + Name: teamName.String, + Description: teamDescription.String, + } + teams[teamUUID] = team } } + + for _, team := range teams { + gameDetails.Teams = append(gameDetails.Teams, &team) + } + return gameDetails, nil } diff --git a/internal/app/view/game.go b/internal/app/view/game.go index 2b37e71..c0c842e 100644 --- a/internal/app/view/game.go +++ b/internal/app/view/game.go @@ -20,19 +20,13 @@ type GameDetails struct { StartTime time.Time `json:"start_time"` EndTime time.Time `json:"end_time"` Description string `json:"description,omitempty"` - Teams []TeamDetails `json:"team_details,omitempty"` + Teams []Teams `json:"team_details,omitempty"` } -type TeamDetails struct { +type Teams struct { Id openapi_types.UUID `json:"id"` Name string `json:"name"` Description string `json:"description"` - Members []Member `json:"members"` -} - -type Member struct { - Id openapi_types.UUID `json:"id"` - UserName string `json:"user_name"` } func NewGameFromModel(m *db.Game) *Game { @@ -45,20 +39,12 @@ func NewGameFromModel(m *db.Game) *Game { } func NewGameDetailsFromModel(m *db.GameDetails) *GameDetails { - teams := make([]TeamDetails, 0, len(m.TeamDetails)) - for _, t := range m.TeamDetails { - members := make([]Member, 0, len(t.Members)) - for _, u := range t.Members { - members = append(members, Member{ - Id: u.Id, - UserName: u.Username, - }) - } - teams = append(teams, TeamDetails{ + teams := make([]Teams, 0, len(m.Teams)) + for _, t := range m.Teams { + teams = append(teams, Teams{ Id: t.Id, Name: t.Name, Description: t.Description, - Members: members, }) } diff --git a/test/server_integration_test.go b/test/server_integration_test.go index ad0b1b4..ee22243 100644 --- a/test/server_integration_test.go +++ b/test/server_integration_test.go @@ -8,6 +8,7 @@ import ( "net/http/httptest" "os" "testing" + "time" "ctf01d/config" "ctf01d/internal/app/database" @@ -420,6 +421,118 @@ func TestTeamCRUD(t *testing.T) { }) } +func TestGameCRUD(t *testing.T) { + r, err := NewTestRouter() + if err != nil { + t.Fatalf("failed to initialize router: %v", err) + } + + var gameID string + faker := faker.New() + + // 1. Создание игры + t.Run("Create Game", func(t *testing.T) { + game := map[string]interface{}{ + "start_time": time.Now(), + "end_time": time.Now(), + "description": faker.Lorem().Sentence(10), + } + body, _ := json.Marshal(game) + req, _ := http.NewRequest("POST", "/api/v1/games", bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + rr := httptest.NewRecorder() + r.ServeHTTP(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("expected status code 200, got %v", rr.Code) + } + }) + + // 2. Получение всех игр и использование ID последней + t.Run("Get All Games", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/api/v1/games", nil) + rr := httptest.NewRecorder() + r.ServeHTTP(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("expected status code 200, got %v", rr.Code) + } + + var games []map[string]interface{} + if err := json.Unmarshal(rr.Body.Bytes(), &games); err != nil { + t.Fatalf("could not unmarshal response: %v", err) + } + + if len(games) == 0 { + t.Fatalf("expected at least one game") + } + + lastGame := games[len(games)-1] + gameID = lastGame["id"].(string) + if gameID == "" { + t.Fatalf("expected game ID in response") + } + }) + + // 3. Получение игры по ID + t.Run("Get Game by ID", func(t *testing.T) { + req, _ := http.NewRequest("GET", "/api/v1/games/"+gameID, nil) + rr := httptest.NewRecorder() + r.ServeHTTP(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("expected status code 200, got %v", rr.Code) + } + + var response map[string]interface{} + if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil { + t.Fatalf("could not unmarshal response: %v", err) + } + + if response["id"] != gameID { + t.Fatalf("expected game ID %v, got %v", gameID, response["id"]) + } + }) + + // 4. Обновление игры по ID + t.Run("Update Game by ID", func(t *testing.T) { + updatedGame := map[string]interface{}{ + "start_time": time.Now(), + "end_time": time.Now(), + "description": faker.Lorem().Sentence(10), + } + body, _ := json.Marshal(updatedGame) + req, _ := http.NewRequest("PUT", "/api/v1/games/"+gameID, bytes.NewBuffer(body)) + req.Header.Set("Content-Type", "application/json") + rr := httptest.NewRecorder() + r.ServeHTTP(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("expected status code 200, got %v", rr.Code) + } + + var response map[string]interface{} + if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil { + t.Fatalf("could not unmarshal response: %v", err) + } + + if response["data"] != "Game updated successfully" { + t.Fatalf("expected 'Game updated successfully', got %v", response["data"]) + } + }) + + // 5. Удаление игры по ID + t.Run("Delete Game by ID", func(t *testing.T) { + req, _ := http.NewRequest("DELETE", "/api/v1/games/"+gameID, nil) + rr := httptest.NewRecorder() + r.ServeHTTP(rr, req) + + if rr.Code != http.StatusOK { + t.Fatalf("expected status code 200, got %v", rr.Code) + } + }) +} + func TestAPIEndpoints(t *testing.T) { t.Skip() doc, err := loads.Spec("../api/openapi.yaml")