diff --git a/src/docs/asciidoc/api.adoc b/src/docs/asciidoc/api.adoc index 8dab180a..265fe641 100644 --- a/src/docs/asciidoc/api.adoc +++ b/src/docs/asciidoc/api.adoc @@ -160,6 +160,10 @@ operation::timeline-controller-test/교체_타임라인을_생성한다[snippets operation::timeline-controller-test/게임_진행_변경_타임라인을_생성한다[snippets='http-request,path-parameters,request-fields,http-response'] +=== 승부차기 타임라인 생성 + +operation::timeline-controller-test/게임_승부차기_타임라인을_생성한다[snippets='http-request,path-parameters,request-fields,http-response'] + === 게임의 타임라인 조회 operation::timeline-query-controller-test/타임라인을_조회한다[snippets='http-request,path-parameters,http-response,response-fields'] diff --git a/src/main/java/com/sports/server/command/game/domain/Game.java b/src/main/java/com/sports/server/command/game/domain/Game.java index 08df3588..984be266 100644 --- a/src/main/java/com/sports/server/command/game/domain/Game.java +++ b/src/main/java/com/sports/server/command/game/domain/Game.java @@ -1,6 +1,5 @@ package com.sports.server.command.game.domain; -import com.sports.server.command.game.dto.GameRequestDto; import com.sports.server.command.league.domain.League; import com.sports.server.command.league.domain.Round; import com.sports.server.command.member.domain.Member; @@ -24,8 +23,8 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import org.springframework.util.StringUtils; import org.springframework.http.HttpStatus; +import org.springframework.util.StringUtils; @Entity @Getter @@ -103,6 +102,15 @@ public void score(LineupPlayer scorer) { scoredTeam.score(); } + public void scoreInPk(LineupPlayer scorer) { + GameTeam scoredTeam = teams.stream() + .filter(scorer::isInTeam) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("참여하지 않는 선수는 승부차기에서 득점할 수 없습니다.")); + + scoredTeam.scoreInPk(); + } + public boolean isMangedBy(Member member) { return manager.equals(member); } @@ -116,6 +124,15 @@ public void cancelScore(LineupPlayer scorer) { scoredTeam.cancelScore(); } + public void cancelPkScore(LineupPlayer scorer) { + GameTeam scoredTeam = teams.stream() + .filter(scorer::isInTeam) + .findAny() + .orElseThrow(() -> new IllegalArgumentException("참여하지 않는 선수는 득점을 취소할 수 없습니다.")); + + scoredTeam.cancelPkScore(); + } + public void updateName(String name) { if (StringUtils.hasText(name)) { this.name = name; diff --git a/src/main/java/com/sports/server/command/game/domain/GameTeam.java b/src/main/java/com/sports/server/command/game/domain/GameTeam.java index a62c2c28..4a46b646 100644 --- a/src/main/java/com/sports/server/command/game/domain/GameTeam.java +++ b/src/main/java/com/sports/server/command/game/domain/GameTeam.java @@ -29,6 +29,7 @@ public class GameTeam extends BaseEntity { private static final int MAXIMUM_OF_TOTAL_CHEER_COUNT = 100_000_000; private static final int MINIMUM_OF_CHEER_COUNT = 0; private static final int SCORE_VALUE = 1; + private static final int PK_SCORE_VALUE = 1; @ManyToOne(fetch = FetchType.LAZY) @JoinColumn(name = "game_id") @@ -47,6 +48,9 @@ public class GameTeam extends BaseEntity { @Column(name = "score", nullable = false) private int score; + @Column(name = "pk_score", nullable = false) + private int pkScore; + public void validateCheerCountOfGameTeam(final int cheerCount) { if (cheerCount >= MAXIMUM_OF_CHEER_COUNT || cheerCount <= MINIMUM_OF_CHEER_COUNT) { throw new CustomException(HttpStatus.BAD_REQUEST, "잘못된 범위의 응원 요청 횟수입니다."); @@ -78,12 +82,22 @@ public void score() { this.score += SCORE_VALUE; } + public void scoreInPk() { + this.pkScore += PK_SCORE_VALUE; + } + public void cancelScore() { if (this.score > 0) { this.score -= SCORE_VALUE; } } + public void cancelPkScore() { + if (this.pkScore > 0) { + this.pkScore -= PK_SCORE_VALUE; + } + } + public GameTeam(Game game, LeagueTeam leagueTeam) { this.game = game; this.leagueTeam = leagueTeam; diff --git a/src/main/java/com/sports/server/command/timeline/domain/PKTimeline.java b/src/main/java/com/sports/server/command/timeline/domain/PKTimeline.java index a345fa0c..731c26f6 100644 --- a/src/main/java/com/sports/server/command/timeline/domain/PKTimeline.java +++ b/src/main/java/com/sports/server/command/timeline/domain/PKTimeline.java @@ -1,10 +1,20 @@ package com.sports.server.command.timeline.domain; +import com.sports.server.command.game.domain.Game; import com.sports.server.command.game.domain.LineupPlayer; -import jakarta.persistence.*; +import com.sports.server.command.sport.domain.Quarter; +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.AccessLevel; import lombok.Getter; +import lombok.NoArgsConstructor; @Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) @DiscriminatorValue("PK") @Getter public class PKTimeline extends Timeline { @@ -21,11 +31,21 @@ public TimelineType getType() { return TimelineType.PK; } + public PKTimeline(Game game, + Quarter recordedQuarter, Integer recordedAt, + LineupPlayer scorer, Boolean isSuccess) { + super(game, recordedQuarter, recordedAt); + this.scorer = scorer; + this.isSuccess = isSuccess; + } + @Override public void apply() { + game.scoreInPk(scorer); } @Override public void rollback() { + game.cancelPkScore(scorer); } } diff --git a/src/main/java/com/sports/server/command/timeline/dto/TimelineRequest.java b/src/main/java/com/sports/server/command/timeline/dto/TimelineRequest.java index 7026ee32..ae6bcd6d 100644 --- a/src/main/java/com/sports/server/command/timeline/dto/TimelineRequest.java +++ b/src/main/java/com/sports/server/command/timeline/dto/TimelineRequest.java @@ -80,4 +80,31 @@ public TimelineType getType() { return TimelineType.GAME_PROGRESS; } } + + @Getter + public static class RegisterPk extends TimelineRequest { + private final Long gameTeamId; + private final Long scorerId; + private final Boolean isSuccess; + + public RegisterPk( + Integer recordedAt, + Long recordedQuarterId, + Long gameTeamId, + Long scorerId, + boolean isSuccess + ) { + super(recordedQuarterId, recordedAt); + this.gameTeamId = gameTeamId; + this.scorerId = scorerId; + this.isSuccess = isSuccess; + } + + @Override + public TimelineType getType() { + return TimelineType.PK; + } + } + + } diff --git a/src/main/java/com/sports/server/command/timeline/mapper/TimelineMapper.java b/src/main/java/com/sports/server/command/timeline/mapper/TimelineMapper.java index 7aadb65e..c4950c5c 100644 --- a/src/main/java/com/sports/server/command/timeline/mapper/TimelineMapper.java +++ b/src/main/java/com/sports/server/command/timeline/mapper/TimelineMapper.java @@ -3,14 +3,18 @@ import com.sports.server.command.game.domain.Game; import com.sports.server.command.game.domain.LineupPlayer; import com.sports.server.command.sport.domain.Quarter; -import com.sports.server.command.timeline.domain.*; +import com.sports.server.command.timeline.domain.GameProgressTimeline; +import com.sports.server.command.timeline.domain.PKTimeline; +import com.sports.server.command.timeline.domain.ReplacementTimeline; +import com.sports.server.command.timeline.domain.ScoreTimeline; +import com.sports.server.command.timeline.domain.Timeline; +import com.sports.server.command.timeline.domain.TimelineType; import com.sports.server.command.timeline.dto.TimelineRequest; import com.sports.server.common.application.EntityUtils; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Component; - import java.util.Map; import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Component; @Component @RequiredArgsConstructor @@ -20,7 +24,8 @@ public class TimelineMapper { private final Map suppliers = Map.of( TimelineType.SCORE, (g, r) -> toScoreTimeline(g, (TimelineRequest.RegisterScore) r), TimelineType.REPLACEMENT, (g, r) -> toReplacementTimeline(g, (TimelineRequest.RegisterReplacement) r), - TimelineType.GAME_PROGRESS, (g, r) -> toProgressTimeline(g, (TimelineRequest.RegisterProgress) r) + TimelineType.GAME_PROGRESS, (g, r) -> toProgressTimeline(g, (TimelineRequest.RegisterProgress) r), + TimelineType.PK, (g, r) -> toPkTimeline(g, (TimelineRequest.RegisterPk) r) ); public Timeline toEntity(Game game, TimelineRequest request) { @@ -60,6 +65,17 @@ private Timeline toProgressTimeline(Game game, ); } + private PKTimeline toPkTimeline(Game game, + TimelineRequest.RegisterPk pkRequest) { + return new PKTimeline( + game, + getQuarter(pkRequest.getRecordedQuarterId()), + pkRequest.getRecordedAt(), + getPlayer(pkRequest.getScorerId()), + pkRequest.getIsSuccess() + ); + } + private Quarter getQuarter(Long quarterId) { return entityUtils.getEntity(quarterId, Quarter.class); } diff --git a/src/main/java/com/sports/server/command/timeline/presentation/TimelineController.java b/src/main/java/com/sports/server/command/timeline/presentation/TimelineController.java index 4e18e5d3..0df841af 100644 --- a/src/main/java/com/sports/server/command/timeline/presentation/TimelineController.java +++ b/src/main/java/com/sports/server/command/timeline/presentation/TimelineController.java @@ -1,14 +1,18 @@ package com.sports.server.command.timeline.presentation; import com.sports.server.command.member.domain.Member; -import com.sports.server.command.timeline.dto.TimelineRequest; import com.sports.server.command.timeline.application.TimelineService; +import com.sports.server.command.timeline.dto.TimelineRequest; +import java.net.URI; import lombok.RequiredArgsConstructor; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.*; - -import java.net.URI; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/games/{gameId}/timelines") @@ -40,6 +44,14 @@ public ResponseEntity createProgressTimeline(@PathVariable Long gameId, return ResponseEntity.created(URI.create("")).build(); } + @PostMapping("/pk") + public ResponseEntity createPkTimeline(@PathVariable Long gameId, + @RequestBody TimelineRequest.RegisterPk request, + Member member) { + timelineService.register(member, gameId, request); + return ResponseEntity.created(URI.create("")).build(); + } + @DeleteMapping("/{timelineId}") public ResponseEntity deleteTimeline(@PathVariable Long gameId, @PathVariable Long timelineId, diff --git a/src/main/resources/db/migration/V18__update-game-teams.sql b/src/main/resources/db/migration/V18__update-game-teams.sql new file mode 100644 index 00000000..755f38bc --- /dev/null +++ b/src/main/resources/db/migration/V18__update-game-teams.sql @@ -0,0 +1,2 @@ +ALTER TABLE game_teams + ADD COLUMN pk_score INT NOT NULL DEFAULT 0; diff --git a/src/main/resources/static/docs/api.html b/src/main/resources/static/docs/api.html index 79634676..10ec587f 100644 --- a/src/main/resources/static/docs/api.html +++ b/src/main/resources/static/docs/api.html @@ -1765,7 +1765,7 @@

@@ -1936,18 +1936,66 @@

라인업 선수 주장으로 등록

Path parameters

-
-

Snippet path-parameters not found for operation::game-controller-test/라인업_선수를_주장으로_등록한다

-
+ + ++++ + + + + + + + + + + + + + + + + + + + + +
Table 1. /games/{gameId}/{gameTeamId}/lineup-players/{lineupPlayerId}/captain/register
ParameterDescription

gameId

게임의 ID

gameTeamId

게임팀의 ID

lineupPlayerId

라인업 선수의 ID

라인업 선수 주장에서 해제

Path parameters

-
-

Snippet path-parameters not found for operation::game-controller-test/라인업_선수를_주장에서_해제한다

-
+ + ++++ + + + + + + + + + + + + + + + + + + + + +
Table 1. /games/{gameId}/{gameTeamId}/lineup-players/{lineupPlayerId}/captain/revoke
ParameterDescription

gameId

게임의 ID

gameTeamId

게임팀의 ID

lineupPlayerId

라인업 선수의 ID

@@ -2085,15 +2133,16 @@

POST /leagues HTTP/1.1
 Content-Type: application/json
-Content-Length: 149
+Content-Length: 179
 Host: www.api.hufstreaming.site
 Cookie: HCC_SES=temp-cookie
 
 {
+  "organizationId" : 1,
   "name" : "우물정 제기차기 대회",
-  "maxRound" : 4,
-  "startAt" : "2024-09-23T15:31:01.385003",
-  "endAt" : "2024-09-23T15:31:01.38501"
+  "maxRound" : "4강",
+  "startAt" : "2024-09-18T18:41:01.840974",
+  "endAt" : "2024-09-18T18:41:01.840975"
 }
@@ -2115,14 +2164,19 @@

organizationId

+

Number

+

조직 id

+ +

name

String

대회 이름

maxRound

-

Number

-

대회 진행 라운드 수. 결승은 2

+

String

+

대회 진행 라운드 수

startAt

@@ -2157,14 +2211,14 @@

PUT /leagues/5124 HTTP/1.1
 Content-Type: application/json
-Content-Length: 165
+Content-Length: 166
 Host: www.api.hufstreaming.site
 Cookie: HCC_SES=temp-cookie
 
 {
   "name" : "훕치치배 망고 빨리먹기 대회",
-  "startAt" : "2024-09-23T15:31:01.43312",
-  "endAt" : "2024-09-23T15:31:01.433126",
+  "startAt" : "2024-09-18T18:41:01.856257",
+  "endAt" : "2024-09-18T18:41:01.856261",
   "maxRound" : "16강"
 }
@@ -3036,7 +3090,7 @@

@@ -3981,6 +4035,107 @@

+

승부차기 타임라인 생성

+
+

HTTP request

+
+
+
POST /games/1/timelines/pk HTTP/1.1
+Content-Type: application/json
+Content-Length: 110
+Host: www.api.hufstreaming.site
+Cookie: HCC_SES=temp-cookie
+
+{
+  "recordedQuarterId" : 2,
+  "recordedAt" : 10,
+  "gameTeamId" : 1,
+  "scorerId" : 1,
+  "isSuccess" : true
+}
+
+
+
+
+

Path parameters

+ + ++++ + + + + + + + + + + + + +
Table 1. /games/{gameId}/timelines/pk
ParameterDescription

gameId

경기의 ID

+
+
+

Request fields

+ +++++ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
PathTypeDescription

gameTeamId

Number

경기 팀의 Id

recordedQuarterId

Number

쿼터 Id

scorerId

Number

승부차기 득점 선수 Id

recordedAt

Number

득점 시간

isSuccess

Boolean

승부차기 득점 성공 여부

+
+
+

HTTP response

+
+
+
HTTP/1.1 201 Created
+Vary: Origin
+Vary: Access-Control-Request-Method
+Vary: Access-Control-Request-Headers
+Location:
+
+
+
+ +

게임의 타임라인 조회

diff --git a/src/test/java/com/sports/server/command/game/domain/GameTest.java b/src/test/java/com/sports/server/command/game/domain/GameTest.java index aec1331a..dc02879a 100644 --- a/src/test/java/com/sports/server/command/game/domain/GameTest.java +++ b/src/test/java/com/sports/server/command/game/domain/GameTest.java @@ -3,6 +3,7 @@ import static com.sports.server.support.fixture.FixtureMonkeyUtils.entityBuilder; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; import com.sports.server.common.exception.CustomException; import java.util.ArrayList; @@ -24,6 +25,7 @@ public void setUp() { game.addTeam(entityBuilder(GameTeam.class) .set("game", game) .set("score", 0) + .set("pkScore", 0) .sample()); } } @@ -42,8 +44,10 @@ class ScoreTest { game.score(scorer); // then - assertThat(game.getTeam1().getScore()).isEqualTo(1); - assertThat(game.getTeam2().getScore()).isEqualTo(0); + assertAll( + () -> assertThat(game.getTeam1().getScore()).isEqualTo(1), + () -> assertThat(game.getTeam2().getScore()).isEqualTo(0) + ); } @Test @@ -57,8 +61,10 @@ class ScoreTest { game.score(scorer); // then - assertThat(game.getTeam1().getScore()).isEqualTo(0); - assertThat(game.getTeam2().getScore()).isEqualTo(1); + assertAll( + () -> assertThat(game.getTeam1().getScore()).isEqualTo(0), + () -> assertThat(game.getTeam2().getScore()).isEqualTo(1) + ); } @Test @@ -75,6 +81,40 @@ class ScoreTest { assertThatThrownBy(() -> game.score(scorer)) .isInstanceOf(IllegalArgumentException.class); } + + @Test + void team1이_승부차기에서_득점한다() { + // given + LineupPlayer scorer = entityBuilder(LineupPlayer.class) + .set("gameTeam", game.getTeam1()) + .sample(); + + // when + game.scoreInPk(scorer); + + // then + assertAll( + () -> assertThat(game.getTeam1().getPkScore()).isEqualTo(1), + () -> assertThat(game.getTeam2().getPkScore()).isEqualTo(0) + ); + } + + @Test + void team2가_승부차기에서_득점한다() { + // given + LineupPlayer scorer = entityBuilder(LineupPlayer.class) + .set("gameTeam", game.getTeam2()) + .sample(); + + // when + game.scoreInPk(scorer); + + // then + assertAll( + () -> assertThat(game.getTeam1().getPkScore()).isEqualTo(0), + () -> assertThat(game.getTeam2().getPkScore()).isEqualTo(1) + ); + } } @Nested @@ -103,8 +143,40 @@ void setUp() { game.cancelScore(team1Player); // then - assertThat(game.getTeam1().getScore()).isEqualTo(0); - assertThat(game.getTeam2().getScore()).isEqualTo(0); + assertAll( + () -> assertThat(game.getTeam1().getScore()).isEqualTo(0), + () -> assertThat(game.getTeam2().getScore()).isEqualTo(0) + ); + } + + @Test + void team1의_승부차기_득점을_취소한다() { + // given + game.scoreInPk(team1Player); + + // when + game.cancelPkScore(team1Player); + + // then + assertAll( + () -> assertThat(game.getTeam1().getPkScore()).isEqualTo(0), + () -> assertThat(game.getTeam2().getPkScore()).isEqualTo(0) + ); + } + + @Test + void team2의__승부차기_득점을_취소한다() { + // given + game.scoreInPk(team2Player); + + // when + game.cancelPkScore(team2Player); + + // then + assertAll( + () -> assertThat(game.getTeam1().getPkScore()).isEqualTo(0), + () -> assertThat(game.getTeam2().getPkScore()).isEqualTo(0) + ); } @Test @@ -116,8 +188,10 @@ void setUp() { game.cancelScore(team2Player); // then - assertThat(game.getTeam1().getScore()).isEqualTo(0); - assertThat(game.getTeam2().getScore()).isEqualTo(0); + assertAll( + () -> assertThat(game.getTeam1().getScore()).isEqualTo(0), + () -> assertThat(game.getTeam2().getScore()).isEqualTo(0) + ); } @Test @@ -130,8 +204,10 @@ void setUp() { game.cancelScore(team1Player); // then - assertThat(game.getTeam1().getScore()).isEqualTo(0); - assertThat(game.getTeam2().getScore()).isEqualTo(1); + assertAll( + () -> assertThat(game.getTeam1().getScore()).isEqualTo(0), + () -> assertThat(game.getTeam2().getScore()).isEqualTo(1) + ); } @Test @@ -148,6 +224,21 @@ void setUp() { assertThatThrownBy(() -> game.cancelScore(scorer)) .isInstanceOf(IllegalArgumentException.class); } + + @Test + void 참여하지_않는_선수는_승부차기_득점을_취소할_수_없다() { + // given + GameTeam otherTeam = entityBuilder(GameTeam.class) + .sample(); + + LineupPlayer scorer = entityBuilder(LineupPlayer.class) + .set("gameTeam", otherTeam) + .sample(); + + // when then + assertThatThrownBy(() -> game.cancelPkScore(scorer)) + .isInstanceOf(IllegalArgumentException.class); + } } @@ -168,4 +259,5 @@ void setUp() { } + } diff --git a/src/test/java/com/sports/server/command/timeline/acceptance/TimelineAcceptanceTest.java b/src/test/java/com/sports/server/command/timeline/acceptance/TimelineAcceptanceTest.java index cd701bb4..b275ce2e 100644 --- a/src/test/java/com/sports/server/command/timeline/acceptance/TimelineAcceptanceTest.java +++ b/src/test/java/com/sports/server/command/timeline/acceptance/TimelineAcceptanceTest.java @@ -1,5 +1,7 @@ package com.sports.server.command.timeline.acceptance; +import static org.assertj.core.api.Assertions.assertThat; + import com.sports.server.command.timeline.domain.GameProgressType; import com.sports.server.command.timeline.dto.TimelineRequest; import com.sports.server.support.AcceptanceTest; @@ -12,8 +14,6 @@ import org.springframework.http.MediaType; import org.springframework.test.context.jdbc.Sql; -import static org.assertj.core.api.Assertions.assertThat; - @Sql(scripts = "/timeline-fixture.sql") public class TimelineAcceptanceTest extends AcceptanceTest { private final Long gameId = 1L; @@ -97,4 +97,29 @@ void configureAuth() { // then assertThat(response.statusCode()).isEqualTo(HttpStatus.CREATED.value()); } + + @Test + void 승부차기_타임라인을_생성한다() { + // given + TimelineRequest.RegisterPk request = new TimelineRequest.RegisterPk( + 10, + quarterId, + team1Id, + team1PlayerId, + true + ); + + // when + ExtractableResponse response = RestAssured.given().log().all() + .when() + .cookie(COOKIE_NAME, mockToken) + .contentType(MediaType.APPLICATION_JSON_VALUE) + .body(request) + .post("/games/{gameId}/timelines/pk", gameId) + .then().log().all() + .extract(); + + // then + assertThat(response.statusCode()).isEqualTo(HttpStatus.CREATED.value()); + } } diff --git a/src/test/java/com/sports/server/command/timeline/application/TimelineServiceTest.java b/src/test/java/com/sports/server/command/timeline/application/TimelineServiceTest.java index 22ed6006..014510d0 100644 --- a/src/test/java/com/sports/server/command/timeline/application/TimelineServiceTest.java +++ b/src/test/java/com/sports/server/command/timeline/application/TimelineServiceTest.java @@ -7,6 +7,7 @@ import com.sports.server.command.member.domain.Member; import com.sports.server.command.member.domain.MemberRepository; import com.sports.server.command.timeline.TimelineFixtureRepository; +import com.sports.server.command.timeline.domain.PKTimeline; import com.sports.server.command.timeline.domain.ReplacementTimeline; import com.sports.server.command.timeline.domain.ScoreTimeline; import com.sports.server.command.timeline.dto.TimelineRequest; @@ -190,6 +191,42 @@ class CreateReplacementTest { } } + @DisplayName("승부차기 타임라인을") + @Nested + class PkTest { + + @Test + void 생성한다() { + // given + Long teamId = 1L; + Long teamPlayerId = 1L; + int recordedAt = 10; + + TimelineRequest.RegisterPk request = new TimelineRequest.RegisterPk( + recordedAt, + quarterId, + teamId, + teamPlayerId, + true + ); + + // when + timelineService.register(manager, gameId, request); + + // then + PKTimeline actual = (PKTimeline) timelineFixtureRepository.findAllLatest(gameId) + .get(0); + + assertAll( + () -> assertThat(actual.getScorer().getId()).isEqualTo(teamPlayerId), + () -> assertThat(actual.getRecordedQuarter().getId()).isEqualTo(quarterId), + () -> assertThat(actual.getRecordedAt()).isEqualTo(recordedAt), + () -> assertThat(actual.getIsSuccess()).isEqualTo(true) + ); + + } + } + @DisplayName("타임라인을 삭제할 때") @Nested class DeleteTest { diff --git a/src/test/java/com/sports/server/command/timeline/presentation/TimelineControllerTest.java b/src/test/java/com/sports/server/command/timeline/presentation/TimelineControllerTest.java index b2846e58..30b2294a 100644 --- a/src/test/java/com/sports/server/command/timeline/presentation/TimelineControllerTest.java +++ b/src/test/java/com/sports/server/command/timeline/presentation/TimelineControllerTest.java @@ -1,14 +1,5 @@ package com.sports.server.command.timeline.presentation; -import com.sports.server.command.timeline.domain.GameProgressType; -import com.sports.server.command.timeline.dto.TimelineRequest; -import com.sports.server.support.DocumentationTest; -import jakarta.servlet.http.Cookie; -import org.junit.jupiter.api.Test; -import org.springframework.http.MediaType; -import org.springframework.restdocs.payload.JsonFieldType; -import org.springframework.test.web.servlet.ResultActions; - import static org.springframework.restdocs.cookies.CookieDocumentation.cookieWithName; import static org.springframework.restdocs.cookies.CookieDocumentation.requestCookies; import static org.springframework.restdocs.mockmvc.RestDocumentationRequestBuilders.delete; @@ -19,6 +10,15 @@ import static org.springframework.restdocs.request.RequestDocumentation.pathParameters; import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; +import com.sports.server.command.timeline.domain.GameProgressType; +import com.sports.server.command.timeline.dto.TimelineRequest; +import com.sports.server.support.DocumentationTest; +import jakarta.servlet.http.Cookie; +import org.junit.jupiter.api.Test; +import org.springframework.http.MediaType; +import org.springframework.restdocs.payload.JsonFieldType; +import org.springframework.test.web.servlet.ResultActions; + public class TimelineControllerTest extends DocumentationTest { @Test void 득점_타임라인을_생성한다() throws Exception { @@ -82,8 +82,10 @@ public class TimelineControllerTest extends DocumentationTest { requestFields( fieldWithPath("gameTeamId").type(JsonFieldType.NUMBER).description("경기 팀의 Id"), fieldWithPath("recordedQuarterId").type(JsonFieldType.NUMBER).description("쿼터 Id"), - fieldWithPath("originLineupPlayerId").type(JsonFieldType.NUMBER).description("기존 선수 Id"), - fieldWithPath("replacementLineupPlayerId").type(JsonFieldType.NUMBER).description("교체 선수 Id"), + fieldWithPath("originLineupPlayerId").type(JsonFieldType.NUMBER) + .description("기존 선수 Id"), + fieldWithPath("replacementLineupPlayerId").type(JsonFieldType.NUMBER) + .description("교체 선수 Id"), fieldWithPath("recordedAt").type(JsonFieldType.NUMBER).description("교체 시간") ), requestCookies( @@ -123,6 +125,43 @@ public class TimelineControllerTest extends DocumentationTest { )); } + @Test + void 게임_승부차기_타임라인을_생성한다() throws Exception { + // given + TimelineRequest.RegisterPk request = new TimelineRequest.RegisterPk( + 10, + 2L, + 1L, + 1L, + true + ); + + // when + ResultActions result = mockMvc.perform(post("/games/{gameId}/timelines/pk", 1) + .contentType(MediaType.APPLICATION_JSON) + .content(objectMapper.writeValueAsString(request)) + .cookie(new Cookie(COOKIE_NAME, "temp-cookie")) + ); + + // then + result.andExpect(status().isCreated()) + .andDo(restDocsHandler.document( + pathParameters( + parameterWithName("gameId").description("경기의 ID") + ), + requestFields( + fieldWithPath("gameTeamId").type(JsonFieldType.NUMBER).description("경기 팀의 Id"), + fieldWithPath("recordedQuarterId").type(JsonFieldType.NUMBER).description("쿼터 Id"), + fieldWithPath("scorerId").type(JsonFieldType.NUMBER).description("승부차기 득점 선수 Id"), + fieldWithPath("recordedAt").type(JsonFieldType.NUMBER).description("득점 시간"), + fieldWithPath("isSuccess").type(JsonFieldType.BOOLEAN).description("승부차기 득점 성공 여부") + ), + requestCookies( + cookieWithName(COOKIE_NAME).description("로그인을 통해 얻은 토큰") + ) + )); + } + @Test void 타임라인을_삭제한다() throws Exception { // when diff --git a/src/test/resources/timeline-fixture.sql b/src/test/resources/timeline-fixture.sql index bd7ebc6f..43a5b688 100644 --- a/src/test/resources/timeline-fixture.sql +++ b/src/test/resources/timeline-fixture.sql @@ -26,9 +26,9 @@ VALUES ('팀A', 'http://example.com/logo_a.png', 1, 1, 1), -- 경기의 팀 -- 농구 대전 (game_id = 1) A팀 vs B팀 -INSERT INTO game_teams (game_id, league_team_id, cheer_count, score) -VALUES (1, 1, 1, 15), -- 팀 A의 정보 - (1, 2, 2, 10); +INSERT INTO game_teams (game_id, league_team_id, cheer_count, score, pk_score) +VALUES (1, 1, 1, 15, 0), -- 팀 A의 정보 + (1, 2, 2, 10, 0); -- 팀 B의 정보 -- 농구 대전(game_id = 1) A팀(game_team_id = 1) 선수