From d1bda5b32134ffcf7fb7cff8871dc96983fe3fb6 Mon Sep 17 00:00:00 2001 From: George Date: Sun, 30 Jul 2023 22:09:28 +0900 Subject: [PATCH 1/7] =?UTF-8?q?test:=20=EA=B5=AC=EA=B0=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EC=97=90=20=EB=8C=80=ED=95=9C=20=EC=83=88=EB=A1=9C?= =?UTF-8?q?=EC=9A=B4=20=EC=9D=B8=EC=88=98=20=EC=A1=B0=EA=B1=B4=20=EB=8F=84?= =?UTF-8?q?=EC=B6=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 구간 추가에 대한 요구사항이 변경 및 추가되었습니다. 그에 따라서 불필요해진 테스트를 우선 주석 처리하고, 새로 작성해야하는 인수 테스트의 인수 조건을 도출하였습니다. --- .../section/SectionAcceptanceTest.java | 49 ++++++++++++++----- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/src/test/java/nextstep/subway/acceptance/section/SectionAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/section/SectionAcceptanceTest.java index d42acfe1bc..5c457a1b51 100644 --- a/src/test/java/nextstep/subway/acceptance/section/SectionAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/section/SectionAcceptanceTest.java @@ -35,6 +35,7 @@ void init() { CREATE_LINE_URL = createdResponse.header("Location"); } + // TODO: 구간 추가에 대한 상세 사항은 Policy 의 단위 테스트에서 검증한다. /** * When 지하철 구간을 추가하면 * Then 지하철 노선 조회시, 추가된 구간을 확인할 수 있다 @@ -54,26 +55,48 @@ void createSection() { * When 구간을 등록하면 * Then 에러가 발생한다 */ - @DisplayName("하행역이 이미 지하철 노선에 등록된 지하철 구간을 추가하면 에러가 발생한다.") - @Test - void createSection_already_register() { - CreateSectionRequest request = 하행역이_지하철_노선에_등록된_구간에_대한_요청이_존재한다(); - - 구간을_등록하면_에러가_발생한다(request); - } +// @DisplayName("하행역이 이미 지하철 노선에 등록된 지하철 구간을 추가하면 에러가 발생한다.") +// @Test +// void createSection_already_register() { +// CreateSectionRequest request = 하행역이_지하철_노선에_등록된_구간에_대한_요청이_존재한다(); +// +// 구간을_등록하면_에러가_발생한다(request); +// } /** * Given 상행역이 노선의 하행 종점역이 아닌 구간으로 * When 구간을 등록하면 * Then 에러가 발생한다 */ - @DisplayName("상행역이 노선의 하행 종점역이 아닌 지하철 구간을 추가하면 에러가 발생한다.") - @Test - void createSection_not_include() { - CreateSectionRequest request = 상행역이_노선의_하행_종점역이_아닌_구간에_대한_요청이_존재한다(); +// @DisplayName("상행역이 노선의 하행 종점역이 아닌 지하철 구간을 추가하면 에러가 발생한다.") +// @Test +// void createSection_not_include() { +// CreateSectionRequest request = 상행역이_노선의_하행_종점역이_아닌_구간에_대한_요청이_존재한다(); +// +// 구간을_등록하면_에러가_발생한다(request); +// } + + // TODO: 구현 필요 + /** + * Given 기존 역 사이 길이보다 크거나 같은 길이의 구간으로 + * When 구간을 등록하면 + * Then 에러가 발생한다 + */ + + // TODO: 구현 필요 + /** + * Given 상행역과 하행역이 모두 기존 노선에 등록되어 있는 구간으로 + * When 구간을 등록하면 + * Then 에러가 발생한다 + */ + + // TODO: 구현 필요 + /** + * Given 상행역과 하행역이 모두 기존 노선에 등록되어 있지 않은 구간으로 + * When 구간을 등록하면 + * Then 에러가 발생한다 + */ - 구간을_등록하면_에러가_발생한다(request); - } /** * Given 새로운 지하철 구간을 추가하고 From 5884eb7b31cb9c4ce92765c300767a9ccded37e3 Mon Sep 17 00:00:00 2001 From: George Date: Sun, 30 Jul 2023 22:55:02 +0900 Subject: [PATCH 2/7] =?UTF-8?q?feat:=20=EA=B5=AC=EA=B0=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EC=8B=9C=20=EA=B8=B8=EC=9D=B4=20=EB=B9=84=EA=B5=90?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 구간 추가시 기존 노선의 구간의 길이와 비교하는 요구사항이 추가되었습니다. 이를 검증하기 위해, 테스트를 먼저 작성하고 구현을 완성하였습니다. 기존에 작성해놓은 모든 테스트가 통과하는 것을 확인했습니다. --- .../section/policy/AddSectionPolicy.java | 15 ++++---- .../subway/section/repository/Sections.java | 12 ++++-- .../unit => }/fixture/LineFixture.java | 6 +-- .../unit => }/fixture/SectionFixture.java | 2 +- .../unit => }/fixture/StationFixture.java | 2 +- .../section/policy/AddSectionPolicyTest.java | 37 +++++++++++++++++++ .../subway/unit/LineServiceMockTest.java | 6 +-- .../nextstep/subway/unit/LineServiceTest.java | 6 +-- .../nextstep/subway/unit/SectionsTest.java | 6 +-- .../subway/unit/fake/FakeLineRepository.java | 4 +- .../subway/unit/fake/FakeStationFindable.java | 2 +- 11 files changed, 70 insertions(+), 28 deletions(-) rename src/test/java/nextstep/{subway/unit => }/fixture/LineFixture.java (63%) rename src/test/java/nextstep/{subway/unit => }/fixture/SectionFixture.java (94%) rename src/test/java/nextstep/{subway/unit => }/fixture/StationFixture.java (92%) create mode 100644 src/test/java/nextstep/subway/section/policy/AddSectionPolicyTest.java diff --git a/src/main/java/nextstep/subway/section/policy/AddSectionPolicy.java b/src/main/java/nextstep/subway/section/policy/AddSectionPolicy.java index 6392bf9b6b..cb133b993e 100644 --- a/src/main/java/nextstep/subway/section/policy/AddSectionPolicy.java +++ b/src/main/java/nextstep/subway/section/policy/AddSectionPolicy.java @@ -5,17 +5,18 @@ import nextstep.subway.station.repository.Station; import java.util.Objects; +import java.util.Optional; public class AddSectionPolicy { public static void validate(Sections sections, Section section) { - if (!Objects.equals(sections.getDownEndStation(), section.getUpStation())) { - System.out.println(sections.getDownEndStation()); - System.out.println(section.getUpStation()); - throw new RuntimeException("section's upStation is not line's downEndStation"); - } + Optional
sectionEqualUpStation = sections.getSectionByUpStation(section.getUpStation()); + Optional
sectionEqualDownStation = sections.getSectionByDownStation(section.getDownStation()); - if (sections.getAllStation().contains(section.getDownStation())) { - throw new RuntimeException("section's downStation is already included in line"); + if ( + (sectionEqualUpStation.isPresent() && (sectionEqualUpStation.get().getDistance() <= section.getDistance())) + ||(sectionEqualDownStation.isPresent() && (sectionEqualDownStation.get().getDistance() <= section.getDistance())) + ) { + throw new RuntimeException("Section's distance too long"); } } } diff --git a/src/main/java/nextstep/subway/section/repository/Sections.java b/src/main/java/nextstep/subway/section/repository/Sections.java index f9af0272d0..1eb88e31c9 100644 --- a/src/main/java/nextstep/subway/section/repository/Sections.java +++ b/src/main/java/nextstep/subway/section/repository/Sections.java @@ -10,9 +10,7 @@ import javax.persistence.Embeddable; import javax.persistence.JoinColumn; import javax.persistence.OneToMany; -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.stream.Collectors; @Embeddable @@ -40,6 +38,14 @@ public Section getLastSection() { return this.sections.get(this.sections.size() - 1); } + public Optional
getSectionByUpStation(Station upStation) { + return this.sections.stream().filter(section -> Objects.equals(section.getUpStation(), upStation)).findFirst(); + } + + public Optional
getSectionByDownStation(Station downStation) { + return this.sections.stream().filter(section -> Objects.equals(section.getDownStation(), downStation)).findFirst(); + } + public Station getDownEndStation() { return this.sections.get(this.sections.size() - 1).getDownStation(); } diff --git a/src/test/java/nextstep/subway/unit/fixture/LineFixture.java b/src/test/java/nextstep/fixture/LineFixture.java similarity index 63% rename from src/test/java/nextstep/subway/unit/fixture/LineFixture.java rename to src/test/java/nextstep/fixture/LineFixture.java index 65ce21d7be..11da1c76fb 100644 --- a/src/test/java/nextstep/subway/unit/fixture/LineFixture.java +++ b/src/test/java/nextstep/fixture/LineFixture.java @@ -1,9 +1,7 @@ -package nextstep.subway.unit.fixture; +package nextstep.fixture; import nextstep.subway.line.repository.Line; -import static nextstep.subway.unit.fixture.SectionFixture.강남역_TO_신논현역; - public class LineFixture { public static final Long 신분당선_ID = 1L; @@ -12,7 +10,7 @@ public class LineFixture { .builder() .name("신분당선") .color("bg-red-600") - .initSection(강남역_TO_신논현역()) + .initSection(SectionFixture.강남역_TO_신논현역()) .build(); } } diff --git a/src/test/java/nextstep/subway/unit/fixture/SectionFixture.java b/src/test/java/nextstep/fixture/SectionFixture.java similarity index 94% rename from src/test/java/nextstep/subway/unit/fixture/SectionFixture.java rename to src/test/java/nextstep/fixture/SectionFixture.java index 9e8cb72ee3..ccca9bba0e 100644 --- a/src/test/java/nextstep/subway/unit/fixture/SectionFixture.java +++ b/src/test/java/nextstep/fixture/SectionFixture.java @@ -1,4 +1,4 @@ -package nextstep.subway.unit.fixture; +package nextstep.fixture; import nextstep.subway.section.repository.Section; diff --git a/src/test/java/nextstep/subway/unit/fixture/StationFixture.java b/src/test/java/nextstep/fixture/StationFixture.java similarity index 92% rename from src/test/java/nextstep/subway/unit/fixture/StationFixture.java rename to src/test/java/nextstep/fixture/StationFixture.java index d45700ebb2..3bcc7b4219 100644 --- a/src/test/java/nextstep/subway/unit/fixture/StationFixture.java +++ b/src/test/java/nextstep/fixture/StationFixture.java @@ -1,4 +1,4 @@ -package nextstep.subway.unit.fixture; +package nextstep.fixture; import nextstep.subway.station.repository.Station; diff --git a/src/test/java/nextstep/subway/section/policy/AddSectionPolicyTest.java b/src/test/java/nextstep/subway/section/policy/AddSectionPolicyTest.java new file mode 100644 index 0000000000..6c3441975b --- /dev/null +++ b/src/test/java/nextstep/subway/section/policy/AddSectionPolicyTest.java @@ -0,0 +1,37 @@ +package nextstep.subway.section.policy; + +import nextstep.fixture.StationFixture; +import nextstep.subway.section.repository.Section; +import nextstep.subway.section.repository.Sections; +import nextstep.subway.station.repository.Station; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.List; + +import static nextstep.fixture.SectionFixture.강남역_TO_신논현역; +import static nextstep.fixture.SectionFixture.신논현역_TO_논현역; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + + +class AddSectionPolicyTest { + + @DisplayName("기존 역 사이 길이보다 추가하려는 구간의 길이가 크거나 같으면 validate 실패한다.") + @Test + void validate_fail_too_long() { + // given + Sections sections = new Sections(List.of(강남역_TO_신논현역(), 신논현역_TO_논현역())); + Section section = Section.builder() + .upStation(StationFixture.신논현역()) + .downStation(new Station("판교역")) + .distance(신논현역_TO_논현역().getDistance() + 1L) + .build(); + + // when + // then + assertThatThrownBy(() -> AddSectionPolicy.validate(sections, section)) + .isInstanceOf(RuntimeException.class) + .hasMessage("Section's distance too long"); + } + +} diff --git a/src/test/java/nextstep/subway/unit/LineServiceMockTest.java b/src/test/java/nextstep/subway/unit/LineServiceMockTest.java index 92665d4810..90d858336f 100644 --- a/src/test/java/nextstep/subway/unit/LineServiceMockTest.java +++ b/src/test/java/nextstep/subway/unit/LineServiceMockTest.java @@ -12,9 +12,9 @@ import java.util.Optional; -import static nextstep.subway.unit.fixture.LineFixture.신분당선; -import static nextstep.subway.unit.fixture.LineFixture.신분당선_ID; -import static nextstep.subway.unit.fixture.StationFixture.*; +import static nextstep.fixture.LineFixture.신분당선; +import static nextstep.fixture.LineFixture.신분당선_ID; +import static nextstep.fixture.StationFixture.*; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; diff --git a/src/test/java/nextstep/subway/unit/LineServiceTest.java b/src/test/java/nextstep/subway/unit/LineServiceTest.java index 7a984c5ed5..e491f52727 100644 --- a/src/test/java/nextstep/subway/unit/LineServiceTest.java +++ b/src/test/java/nextstep/subway/unit/LineServiceTest.java @@ -9,9 +9,9 @@ import nextstep.subway.unit.fake.FakeStationFindable; import org.junit.jupiter.api.Test; -import static nextstep.subway.unit.fixture.LineFixture.신분당선_ID; -import static nextstep.subway.unit.fixture.StationFixture.논현역_ID; -import static nextstep.subway.unit.fixture.StationFixture.신논현역_ID; +import static nextstep.fixture.LineFixture.신분당선_ID; +import static nextstep.fixture.StationFixture.논현역_ID; +import static nextstep.fixture.StationFixture.신논현역_ID; import static org.assertj.core.api.Assertions.assertThat; public class LineServiceTest { diff --git a/src/test/java/nextstep/subway/unit/SectionsTest.java b/src/test/java/nextstep/subway/unit/SectionsTest.java index ccf1381e1d..05112473f5 100644 --- a/src/test/java/nextstep/subway/unit/SectionsTest.java +++ b/src/test/java/nextstep/subway/unit/SectionsTest.java @@ -7,9 +7,9 @@ import java.util.ArrayList; import java.util.List; -import static nextstep.subway.unit.fixture.SectionFixture.강남역_TO_신논현역; -import static nextstep.subway.unit.fixture.SectionFixture.신논현역_TO_논현역; -import static nextstep.subway.unit.fixture.StationFixture.*; +import static nextstep.fixture.SectionFixture.강남역_TO_신논현역; +import static nextstep.fixture.SectionFixture.신논현역_TO_논현역; +import static nextstep.fixture.StationFixture.*; import static org.assertj.core.api.Assertions.assertThat; class SectionsTest { diff --git a/src/test/java/nextstep/subway/unit/fake/FakeLineRepository.java b/src/test/java/nextstep/subway/unit/fake/FakeLineRepository.java index 8ca23a424b..dd6ca63772 100644 --- a/src/test/java/nextstep/subway/unit/fake/FakeLineRepository.java +++ b/src/test/java/nextstep/subway/unit/fake/FakeLineRepository.java @@ -5,8 +5,8 @@ import java.util.*; -import static nextstep.subway.unit.fixture.LineFixture.신분당선; -import static nextstep.subway.unit.fixture.LineFixture.신분당선_ID; +import static nextstep.fixture.LineFixture.신분당선; +import static nextstep.fixture.LineFixture.신분당선_ID; public class FakeLineRepository implements LineRepository { diff --git a/src/test/java/nextstep/subway/unit/fake/FakeStationFindable.java b/src/test/java/nextstep/subway/unit/fake/FakeStationFindable.java index 2c2ece2ab5..6d5062d04f 100644 --- a/src/test/java/nextstep/subway/unit/fake/FakeStationFindable.java +++ b/src/test/java/nextstep/subway/unit/fake/FakeStationFindable.java @@ -5,7 +5,7 @@ import java.util.List; -import static nextstep.subway.unit.fixture.StationFixture.*; +import static nextstep.fixture.StationFixture.*; public class FakeStationFindable implements StationFindable { @Override From 9e57e9f8322df9ad6c124d3fa17c0ab40fa53fb2 Mon Sep 17 00:00:00 2001 From: George Date: Sun, 30 Jul 2023 23:05:00 +0900 Subject: [PATCH 3/7] =?UTF-8?q?test:=20=EA=B5=AC=EA=B0=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=EC=97=90=20=EB=8C=80=ED=95=B4=20=EC=B6=94=EA=B0=80?= =?UTF-8?q?=EB=90=9C=20=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=EC=9D=98=20?= =?UTF-8?q?=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=9E=91=EC=84=B1(=EC=8B=A4?= =?UTF-8?q?=ED=8C=A8=20=EC=83=81=ED=83=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 구간 추가에 대해 새로운 요구사항이 추가되었습니다. 그에 대한 새로운 테스트 케이스를 먼저 정의하여 추가하였습니다. --- .../section/policy/AddSectionPolicyTest.java | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/src/test/java/nextstep/subway/section/policy/AddSectionPolicyTest.java b/src/test/java/nextstep/subway/section/policy/AddSectionPolicyTest.java index 6c3441975b..b66374a722 100644 --- a/src/test/java/nextstep/subway/section/policy/AddSectionPolicyTest.java +++ b/src/test/java/nextstep/subway/section/policy/AddSectionPolicyTest.java @@ -16,7 +16,7 @@ class AddSectionPolicyTest { - @DisplayName("기존 역 사이 길이보다 추가하려는 구간의 길이가 크거나 같으면 validate 실패한다.") + @DisplayName("기존 구간의 길이보다 추가하려는 구간의 길이가 크거나 같으면 validate 실패한다.") @Test void validate_fail_too_long() { // given @@ -34,4 +34,35 @@ void validate_fail_too_long() { .hasMessage("Section's distance too long"); } + @DisplayName("추가하려는 구간의 상행역,하행역이 이미 모두 노선 내 구간에 등록되어 있다면 validate 실패한다.") + @Test + void validate_fail_already_register() { + // given + Sections sections = new Sections(List.of(강남역_TO_신논현역(), 신논현역_TO_논현역())); + Section section = 강남역_TO_신논현역(); + + // when + // then + assertThatThrownBy(() -> AddSectionPolicy.validate(sections, section)) + .isInstanceOf(RuntimeException.class) + .hasMessage("Section's stations already registered"); + } + + @DisplayName("추가하려는 구간의 상행역,하행역이 모두 노선 내 구간에 없다면 validate 실패한다.") + @Test + void validate_fail_not_exist() { + // given + Sections sections = new Sections(List.of(강남역_TO_신논현역(), 신논현역_TO_논현역())); + Section section = Section.builder() + .upStation(new Station("판교역")) + .downStation(new Station("광교역")) + .distance(7L) + .build(); + + // when + // then + assertThatThrownBy(() -> AddSectionPolicy.validate(sections, section)) + .isInstanceOf(RuntimeException.class) + .hasMessage("Section's stations not exist in sections"); + } } From 0ade11e79ae6972eedd964466508a436b778946c Mon Sep 17 00:00:00 2001 From: George Date: Mon, 31 Jul 2023 17:22:39 +0900 Subject: [PATCH 4/7] =?UTF-8?q?feat:=20=EA=B5=AC=EA=B0=84=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EC=A0=95=EC=B1=85=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 구간 추가시, 추가하려는 구간의 상행역, 하행역이 이미 모두 노선 내 구간에 등록되어 있는지 확인하는 로직과, 모두 노선 내 없는지 확인하는 로직을 구현하고 모두 테스트를 통과하는지 확인하였습니다. --- .../section/policy/AddSectionPolicy.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/src/main/java/nextstep/subway/section/policy/AddSectionPolicy.java b/src/main/java/nextstep/subway/section/policy/AddSectionPolicy.java index cb133b993e..95c8dd3824 100644 --- a/src/main/java/nextstep/subway/section/policy/AddSectionPolicy.java +++ b/src/main/java/nextstep/subway/section/policy/AddSectionPolicy.java @@ -9,9 +9,21 @@ public class AddSectionPolicy { public static void validate(Sections sections, Section section) { + Optional upStation = sections.getAllStation().stream() + .filter(station -> Objects.equals(station, section.getUpStation())) + .findFirst(); + Optional downStation = sections.getAllStation().stream() + .filter(station -> Objects.equals(station, section.getDownStation())) + .findFirst(); Optional
sectionEqualUpStation = sections.getSectionByUpStation(section.getUpStation()); Optional
sectionEqualDownStation = sections.getSectionByDownStation(section.getDownStation()); + checkRegisteredStation(upStation, downStation); + checkNotRegisteredStation(upStation, downStation); + checkSectionDistance(section, sectionEqualUpStation, sectionEqualDownStation); + } + + private static void checkSectionDistance(Section section, Optional
sectionEqualUpStation, Optional
sectionEqualDownStation) { if ( (sectionEqualUpStation.isPresent() && (sectionEqualUpStation.get().getDistance() <= section.getDistance())) ||(sectionEqualDownStation.isPresent() && (sectionEqualDownStation.get().getDistance() <= section.getDistance())) @@ -19,4 +31,16 @@ public static void validate(Sections sections, Section section) { throw new RuntimeException("Section's distance too long"); } } + + private static void checkNotRegisteredStation(Optional upStation, Optional downStation) { + if (upStation.isEmpty() && downStation.isEmpty()) { + throw new RuntimeException("Section's stations not exist in sections"); + } + } + + private static void checkRegisteredStation(Optional upStation, Optional downStation) { + if (upStation.isPresent() && downStation.isPresent()) { + throw new RuntimeException("Section's stations already registered"); + } + } } From 4f91e65a027ebaa464e5cf158ee2b50224bea02e Mon Sep 17 00:00:00 2001 From: George Date: Mon, 31 Jul 2023 18:56:33 +0900 Subject: [PATCH 5/7] =?UTF-8?q?feat:=20=EC=97=AD=20=EC=A1=B0=ED=9A=8C?= =?UTF-8?q?=EC=8B=9C=20=EC=88=9C=EC=B0=A8=EC=A0=81=EC=9C=BC=EB=A1=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 역을 조회시, 이제 중간에도 구간을 삽입할 수 있기에 로직 수정이 필요했습니다. 첫번째 역id, 마지막 역id 를 저장하여 순차적으로 역을 가져올 수 있도록 수정하였습니다. 테스트가 통과하는 것도 확인하였습니다. --- .../subway/section/repository/Sections.java | 28 +++++++++++++++++-- .../nextstep/subway/unit/SectionsTest.java | 28 +++++++++++++++++-- 2 files changed, 51 insertions(+), 5 deletions(-) diff --git a/src/main/java/nextstep/subway/section/repository/Sections.java b/src/main/java/nextstep/subway/section/repository/Sections.java index 1eb88e31c9..f27ea1a4db 100644 --- a/src/main/java/nextstep/subway/section/repository/Sections.java +++ b/src/main/java/nextstep/subway/section/repository/Sections.java @@ -20,6 +20,10 @@ public class Sections { @JoinColumn(name = "line_id") private List
sections = new ArrayList<>(); + private Long firstStationId; + + private Long lastStationId; + public void addSection(Section section) { AddSectionPolicy.validate(this, section); this.sections.add(section); @@ -55,9 +59,23 @@ public Long getTotalDistance() { } public List getAllStation() { - List totalStation = this.sections.stream().map(Section::getUpStation).collect(Collectors.toList()); - totalStation.add(this.sections.get(this.sections.size() - 1).getDownStation()); - return Collections.unmodifiableList(totalStation); + List result = new ArrayList<>(); + Long targetStationId = firstStationId; + + while (targetStationId != null && !targetStationId.equals(lastStationId)) { + for (Section section : sections) { + if (Objects.equals(section.getUpStation().getId(), targetStationId)) { + result.add(section.getUpStation()); + targetStationId = section.getDownStation().getId(); + } + if (targetStationId.equals(lastStationId)) { + result.add(section.getDownStation()); + break; + } + } + } + + return Collections.unmodifiableList(result); } public Sections(List
sections) { @@ -65,5 +83,9 @@ public Sections(List
sections) { throw new RuntimeException("sections: at least one section is required"); } this.sections = sections; + this.firstStationId = sections.get(0).getUpStation().getId(); + this.lastStationId = sections.get(sections.size() - 1).getDownStation().getId(); } + + } diff --git a/src/test/java/nextstep/subway/unit/SectionsTest.java b/src/test/java/nextstep/subway/unit/SectionsTest.java index 05112473f5..dac5303c2e 100644 --- a/src/test/java/nextstep/subway/unit/SectionsTest.java +++ b/src/test/java/nextstep/subway/unit/SectionsTest.java @@ -1,8 +1,12 @@ package nextstep.subway.unit; +import nextstep.fixture.StationFixture; +import nextstep.subway.section.repository.Section; import nextstep.subway.section.repository.Sections; import nextstep.subway.station.repository.Station; import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.junit.jupiter.MockitoExtension; import java.util.ArrayList; import java.util.List; @@ -11,7 +15,10 @@ import static nextstep.fixture.SectionFixture.신논현역_TO_논현역; import static nextstep.fixture.StationFixture.*; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +@ExtendWith(MockitoExtension.class) class SectionsTest { @Test void addSection() { @@ -28,13 +35,30 @@ void addSection() { @Test void getAllStation() { // given - Sections sections = new Sections(new ArrayList<>(List.of(강남역_TO_신논현역()))); + Station 강남역 = mock(Station.class); + Station 신논현역 = mock(Station.class); + Station 논현역 = mock(Station.class); + given(강남역.getId()).willReturn(1L); + given(신논현역.getId()).willReturn(2L); + given(논현역.getId()).willReturn(3L); + Sections sections = new Sections(new ArrayList<>(List.of( + Section.builder() + .upStation(강남역) + .downStation(신논현역) + .distance(10L) + .build(), + Section.builder() + .upStation(신논현역) + .downStation(논현역) + .distance(5L) + .build() + ))); // when List stations = sections.getAllStation(); // then - assertThat(stations).isEqualTo(List.of(강남역(), 신논현역())); + assertThat(stations).isEqualTo(List.of(강남역, 신논현역, 논현역)); } @Test From 15425d03c9592a0abcbad85526bb01f80fb8d1b5 Mon Sep 17 00:00:00 2001 From: George Date: Wed, 2 Aug 2023 18:09:44 +0900 Subject: [PATCH 6/7] =?UTF-8?q?test:=20=EA=B5=AC=EA=B0=84=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EA=B4=80=EB=A0=A8=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80(=EC=8B=A4=ED=8C=A8=20=EC=83=81=ED=83=9C)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 구간 추가시 firstStationId, lastStationId를 수정해야하는 케이스가 존재합니다. 이를 테스트하기 위해 테스트 코드를 작성하였습니다. --- .../subway/section/repository/Sections.java | 3 + .../nextstep/subway/unit/SectionsTest.java | 86 +++++++++++++++++-- 2 files changed, 81 insertions(+), 8 deletions(-) diff --git a/src/main/java/nextstep/subway/section/repository/Sections.java b/src/main/java/nextstep/subway/section/repository/Sections.java index f27ea1a4db..6214639860 100644 --- a/src/main/java/nextstep/subway/section/repository/Sections.java +++ b/src/main/java/nextstep/subway/section/repository/Sections.java @@ -1,6 +1,7 @@ package nextstep.subway.section.repository; import lombok.AccessLevel; +import lombok.Getter; import lombok.NoArgsConstructor; import nextstep.subway.section.policy.AddSectionPolicy; import nextstep.subway.section.policy.DeleteSectionPolicy; @@ -20,8 +21,10 @@ public class Sections { @JoinColumn(name = "line_id") private List
sections = new ArrayList<>(); + @Getter private Long firstStationId; + @Getter private Long lastStationId; public void addSection(Section section) { diff --git a/src/test/java/nextstep/subway/unit/SectionsTest.java b/src/test/java/nextstep/subway/unit/SectionsTest.java index dac5303c2e..58777f5d3b 100644 --- a/src/test/java/nextstep/subway/unit/SectionsTest.java +++ b/src/test/java/nextstep/subway/unit/SectionsTest.java @@ -1,6 +1,5 @@ package nextstep.subway.unit; -import nextstep.fixture.StationFixture; import nextstep.subway.section.repository.Section; import nextstep.subway.section.repository.Sections; import nextstep.subway.station.repository.Station; @@ -13,7 +12,7 @@ import static nextstep.fixture.SectionFixture.강남역_TO_신논현역; import static nextstep.fixture.SectionFixture.신논현역_TO_논현역; -import static nextstep.fixture.StationFixture.*; +import static nextstep.fixture.StationFixture.논현역; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.BDDMockito.given; import static org.mockito.Mockito.mock; @@ -23,17 +22,60 @@ class SectionsTest { @Test void addSection() { // given - Sections sections = new Sections(new ArrayList<>(List.of(강남역_TO_신논현역()))); + Station 강남역 = mock(Station.class); + Station 신논현역 = mock(Station.class); + Station 논현역 = mock(Station.class); + given(강남역.getId()).willReturn(1L); + given(신논현역.getId()).willReturn(2L); + Sections sections = new Sections(new ArrayList<>(List.of( + Section.builder() + .upStation(강남역) + .downStation(신논현역) + .distance(10L) + .build() + ))); // when - sections.addSection(신논현역_TO_논현역()); + sections.addSection(Section.builder() + .upStation(신논현역) + .downStation(논현역) + .distance(5L) + .build()); // then assertThat(sections.size()).isEqualTo(2); } @Test - void getAllStation() { + void addSection_change_firstStationId() { + // given + Station 강남역 = mock(Station.class); + Station 신논현역 = mock(Station.class); + Station 논현역 = mock(Station.class); + given(강남역.getId()).willReturn(1L); + given(신논현역.getId()).willReturn(2L); + given(논현역.getId()).willReturn(3L); + Sections sections = new Sections(new ArrayList<>(List.of( + Section.builder() + .upStation(신논현역) + .downStation(논현역) + .distance(5L) + .build() + ))); + + // when + sections.addSection(Section.builder() + .upStation(강남역) + .downStation(신논현역) + .distance(10L) + .build()); + + // then + assertThat(sections.getFirstStationId()).isEqualTo(1L); + } + + @Test + void addSection_change_lastStationId() { // given Station 강남역 = mock(Station.class); Station 신논현역 = mock(Station.class); @@ -46,12 +88,40 @@ void getAllStation() { .upStation(강남역) .downStation(신논현역) .distance(10L) - .build(), - Section.builder() + .build() + ))); + + // when + sections.addSection(Section.builder() .upStation(신논현역) .downStation(논현역) .distance(5L) - .build() + .build()); + + // then + assertThat(sections.getLastStationId()).isEqualTo(3L); + } + + @Test + void getAllStation() { + // given + Station 강남역 = mock(Station.class); + Station 신논현역 = mock(Station.class); + Station 논현역 = mock(Station.class); + given(강남역.getId()).willReturn(1L); + given(신논현역.getId()).willReturn(2L); + given(논현역.getId()).willReturn(3L); + Sections sections = new Sections(new ArrayList<>(List.of( + Section.builder() + .upStation(강남역) + .downStation(신논현역) + .distance(10L) + .build(), + Section.builder() + .upStation(신논현역) + .downStation(논현역) + .distance(5L) + .build() ))); // when From 593afd767742d28b517310510c14d271c4c5005e Mon Sep 17 00:00:00 2001 From: George Date: Thu, 3 Aug 2023 20:34:46 +0900 Subject: [PATCH 7/7] =?UTF-8?q?feat:=20=EA=B5=AC=EA=B0=84=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EA=B4=80=EB=A0=A8=20=EC=88=98=EC=A0=95=EB=90=9C=20?= =?UTF-8?q?=EC=9A=94=EA=B5=AC=EC=82=AC=ED=95=AD=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 구간 추가시에 대한 요구사항이 수정되었습니다. 단순한 추가 시나리오에 대해서는 인수테스트를 작성하였고, 좀 더 세부적인 요구사항에 대한 케이스는 단위테스트로 작성하였습니다. 모든 테스트가 통과하는 것을 확인했습니다. --- .../line/controller/LineController.java | 1 + .../subway/line/service/LineService.java | 9 ++- .../subway/section/config/BeanConfig.java | 19 ++++++ .../section/policy/AddSectionPolicy.java | 46 ------------- .../section/policy/add/AddSectionPolicy.java | 8 +++ .../policy/add/CheckDistancePolicy.java | 19 ++++++ .../policy/add/CheckNotRegisterPolicy.java | 24 +++++++ .../policy/add/CheckRegisterPolicy.java | 24 +++++++ .../add/IntegrationAddSectionPolicy.java | 16 +++++ .../{ => delete}/DeleteSectionPolicy.java | 2 +- .../subway/section/repository/Section.java | 5 +- .../subway/section/repository/Sections.java | 26 +++++-- .../section/SectionAcceptanceTest.java | 52 +------------- .../section/policy/AddSectionPolicyTest.java | 68 ------------------- .../policy/add/CheckDistancePolicyTest.java | 49 +++++++++++++ .../add/CheckNotRegisterPolicyTest.java | 50 ++++++++++++++ .../policy/add/CheckRegisterPolicyTest.java | 48 +++++++++++++ .../subway/unit/LineServiceMockTest.java | 6 +- .../nextstep/subway/unit/LineServiceTest.java | 11 ++- .../nextstep/subway/unit/SectionsTest.java | 10 ++- 20 files changed, 309 insertions(+), 184 deletions(-) create mode 100644 src/main/java/nextstep/subway/section/config/BeanConfig.java delete mode 100644 src/main/java/nextstep/subway/section/policy/AddSectionPolicy.java create mode 100644 src/main/java/nextstep/subway/section/policy/add/AddSectionPolicy.java create mode 100644 src/main/java/nextstep/subway/section/policy/add/CheckDistancePolicy.java create mode 100644 src/main/java/nextstep/subway/section/policy/add/CheckNotRegisterPolicy.java create mode 100644 src/main/java/nextstep/subway/section/policy/add/CheckRegisterPolicy.java create mode 100644 src/main/java/nextstep/subway/section/policy/add/IntegrationAddSectionPolicy.java rename src/main/java/nextstep/subway/section/policy/{ => delete}/DeleteSectionPolicy.java (91%) delete mode 100644 src/test/java/nextstep/subway/section/policy/AddSectionPolicyTest.java create mode 100644 src/test/java/nextstep/subway/section/policy/add/CheckDistancePolicyTest.java create mode 100644 src/test/java/nextstep/subway/section/policy/add/CheckNotRegisterPolicyTest.java create mode 100644 src/test/java/nextstep/subway/section/policy/add/CheckRegisterPolicyTest.java diff --git a/src/main/java/nextstep/subway/line/controller/LineController.java b/src/main/java/nextstep/subway/line/controller/LineController.java index 211f86710d..b545df0a04 100644 --- a/src/main/java/nextstep/subway/line/controller/LineController.java +++ b/src/main/java/nextstep/subway/line/controller/LineController.java @@ -4,6 +4,7 @@ import nextstep.subway.line.dto.CreateLineRequest; import nextstep.subway.line.dto.LineResponse; import nextstep.subway.line.dto.UpdateLineRequest; +import nextstep.subway.line.repository.Line; import nextstep.subway.line.service.LineService; import nextstep.subway.section.dto.CreateSectionRequest; import org.springframework.http.ResponseEntity; diff --git a/src/main/java/nextstep/subway/line/service/LineService.java b/src/main/java/nextstep/subway/line/service/LineService.java index a871c21c55..3564149ffd 100644 --- a/src/main/java/nextstep/subway/line/service/LineService.java +++ b/src/main/java/nextstep/subway/line/service/LineService.java @@ -6,6 +6,7 @@ import nextstep.subway.line.repository.Line; import nextstep.subway.line.repository.LineRepository; import nextstep.subway.section.dto.CreateSectionRequest; +import nextstep.subway.section.policy.add.AddSectionPolicy; import nextstep.subway.section.repository.Section; import nextstep.subway.station.repository.Station; import nextstep.subway.station.service.StationFindable; @@ -21,6 +22,7 @@ class LineService implements LineFindable, LineSavable, LineUpdatable, LineDeletable { private final LineRepository lineRepository; private final StationFindable stationFindable; + private final AddSectionPolicy addSectionPolicy; @Transactional public Line saveLine(CreateLineRequest request) { @@ -56,9 +58,10 @@ public void deleteLineById(Long id) { @Transactional public Line addSection(Long id, CreateSectionRequest request) { Line line = findLineById(id); - System.out.println(line.getSections().size()); - line.getSections().addSection(createSection(request.getUpStationId(), request.getDownStationId(), request.getDistance())); - System.out.println(line.getSections().size()); + line.getSections().addSection( + createSection(request.getUpStationId(), request.getDownStationId(), request.getDistance()), + addSectionPolicy + ); return line; } diff --git a/src/main/java/nextstep/subway/section/config/BeanConfig.java b/src/main/java/nextstep/subway/section/config/BeanConfig.java new file mode 100644 index 0000000000..40f78fb3d4 --- /dev/null +++ b/src/main/java/nextstep/subway/section/config/BeanConfig.java @@ -0,0 +1,19 @@ +package nextstep.subway.section.config; + +import nextstep.subway.section.policy.add.*; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.List; + +@Configuration +public class BeanConfig { + @Bean + AddSectionPolicy addSectionPolicy() { + return new IntegrationAddSectionPolicy(List.of( + new CheckRegisterPolicy(), + new CheckNotRegisterPolicy(), + new CheckDistancePolicy() + )); + } +} diff --git a/src/main/java/nextstep/subway/section/policy/AddSectionPolicy.java b/src/main/java/nextstep/subway/section/policy/AddSectionPolicy.java deleted file mode 100644 index 95c8dd3824..0000000000 --- a/src/main/java/nextstep/subway/section/policy/AddSectionPolicy.java +++ /dev/null @@ -1,46 +0,0 @@ -package nextstep.subway.section.policy; - -import nextstep.subway.section.repository.Section; -import nextstep.subway.section.repository.Sections; -import nextstep.subway.station.repository.Station; - -import java.util.Objects; -import java.util.Optional; - -public class AddSectionPolicy { - public static void validate(Sections sections, Section section) { - Optional upStation = sections.getAllStation().stream() - .filter(station -> Objects.equals(station, section.getUpStation())) - .findFirst(); - Optional downStation = sections.getAllStation().stream() - .filter(station -> Objects.equals(station, section.getDownStation())) - .findFirst(); - Optional
sectionEqualUpStation = sections.getSectionByUpStation(section.getUpStation()); - Optional
sectionEqualDownStation = sections.getSectionByDownStation(section.getDownStation()); - - checkRegisteredStation(upStation, downStation); - checkNotRegisteredStation(upStation, downStation); - checkSectionDistance(section, sectionEqualUpStation, sectionEqualDownStation); - } - - private static void checkSectionDistance(Section section, Optional
sectionEqualUpStation, Optional
sectionEqualDownStation) { - if ( - (sectionEqualUpStation.isPresent() && (sectionEqualUpStation.get().getDistance() <= section.getDistance())) - ||(sectionEqualDownStation.isPresent() && (sectionEqualDownStation.get().getDistance() <= section.getDistance())) - ) { - throw new RuntimeException("Section's distance too long"); - } - } - - private static void checkNotRegisteredStation(Optional upStation, Optional downStation) { - if (upStation.isEmpty() && downStation.isEmpty()) { - throw new RuntimeException("Section's stations not exist in sections"); - } - } - - private static void checkRegisteredStation(Optional upStation, Optional downStation) { - if (upStation.isPresent() && downStation.isPresent()) { - throw new RuntimeException("Section's stations already registered"); - } - } -} diff --git a/src/main/java/nextstep/subway/section/policy/add/AddSectionPolicy.java b/src/main/java/nextstep/subway/section/policy/add/AddSectionPolicy.java new file mode 100644 index 0000000000..afc9453154 --- /dev/null +++ b/src/main/java/nextstep/subway/section/policy/add/AddSectionPolicy.java @@ -0,0 +1,8 @@ +package nextstep.subway.section.policy.add; + +import nextstep.subway.section.repository.Section; +import nextstep.subway.section.repository.Sections; + +public interface AddSectionPolicy { + void validate(Sections sections, Section section); +} diff --git a/src/main/java/nextstep/subway/section/policy/add/CheckDistancePolicy.java b/src/main/java/nextstep/subway/section/policy/add/CheckDistancePolicy.java new file mode 100644 index 0000000000..b801a4d251 --- /dev/null +++ b/src/main/java/nextstep/subway/section/policy/add/CheckDistancePolicy.java @@ -0,0 +1,19 @@ +package nextstep.subway.section.policy.add; + +import nextstep.subway.section.repository.Section; +import nextstep.subway.section.repository.Sections; + +import java.util.Optional; + +public class CheckDistancePolicy implements AddSectionPolicy { + @Override + public void validate(Sections sections, Section section) { + Optional
sectionEqualUpStation = sections.getSectionByUpStation(section.getUpStation()); + Optional
sectionEqualDownStation = sections.getSectionByDownStation(section.getDownStation()); + + if ((sectionEqualUpStation.isPresent() && (sectionEqualUpStation.get().getDistance() <= section.getDistance())) + || (sectionEqualDownStation.isPresent() && (sectionEqualDownStation.get().getDistance() <= section.getDistance()))) { + throw new RuntimeException("Section's distance too long"); + } + } +} diff --git a/src/main/java/nextstep/subway/section/policy/add/CheckNotRegisterPolicy.java b/src/main/java/nextstep/subway/section/policy/add/CheckNotRegisterPolicy.java new file mode 100644 index 0000000000..1fb606dbf9 --- /dev/null +++ b/src/main/java/nextstep/subway/section/policy/add/CheckNotRegisterPolicy.java @@ -0,0 +1,24 @@ +package nextstep.subway.section.policy.add; + +import nextstep.subway.section.repository.Section; +import nextstep.subway.section.repository.Sections; +import nextstep.subway.station.repository.Station; + +import java.util.Objects; +import java.util.Optional; + +public class CheckNotRegisterPolicy implements AddSectionPolicy { + @Override + public void validate(Sections sections, Section section) { + Optional upStation = sections.getAllStation().stream() + .filter(station -> Objects.equals(station, section.getUpStation())) + .findFirst(); + Optional downStation = sections.getAllStation().stream() + .filter(station -> Objects.equals(station, section.getDownStation())) + .findFirst(); + + if (upStation.isEmpty() && downStation.isEmpty()) { + throw new RuntimeException("Section's stations not exist in sections"); + } + } +} diff --git a/src/main/java/nextstep/subway/section/policy/add/CheckRegisterPolicy.java b/src/main/java/nextstep/subway/section/policy/add/CheckRegisterPolicy.java new file mode 100644 index 0000000000..cebf8eb2ec --- /dev/null +++ b/src/main/java/nextstep/subway/section/policy/add/CheckRegisterPolicy.java @@ -0,0 +1,24 @@ +package nextstep.subway.section.policy.add; + +import nextstep.subway.section.repository.Section; +import nextstep.subway.section.repository.Sections; +import nextstep.subway.station.repository.Station; + +import java.util.Objects; +import java.util.Optional; + +public class CheckRegisterPolicy implements AddSectionPolicy { + @Override + public void validate(Sections sections, Section section) { + Optional upStation = sections.getAllStation().stream() + .filter(station -> Objects.equals(station, section.getUpStation())) + .findFirst(); + Optional downStation = sections.getAllStation().stream() + .filter(station -> Objects.equals(station, section.getDownStation())) + .findFirst(); + + if (upStation.isPresent() && downStation.isPresent()) { + throw new RuntimeException("Section's stations already registered"); + } + } +} diff --git a/src/main/java/nextstep/subway/section/policy/add/IntegrationAddSectionPolicy.java b/src/main/java/nextstep/subway/section/policy/add/IntegrationAddSectionPolicy.java new file mode 100644 index 0000000000..d52569872c --- /dev/null +++ b/src/main/java/nextstep/subway/section/policy/add/IntegrationAddSectionPolicy.java @@ -0,0 +1,16 @@ +package nextstep.subway.section.policy.add; + +import lombok.RequiredArgsConstructor; +import nextstep.subway.section.repository.Section; +import nextstep.subway.section.repository.Sections; + +import java.util.List; + +@RequiredArgsConstructor +public class IntegrationAddSectionPolicy implements AddSectionPolicy { + private final List policyList; + + public void validate(Sections sections, Section section) { + policyList.forEach(addSectionPolicy -> addSectionPolicy.validate(sections, section)); + } +} diff --git a/src/main/java/nextstep/subway/section/policy/DeleteSectionPolicy.java b/src/main/java/nextstep/subway/section/policy/delete/DeleteSectionPolicy.java similarity index 91% rename from src/main/java/nextstep/subway/section/policy/DeleteSectionPolicy.java rename to src/main/java/nextstep/subway/section/policy/delete/DeleteSectionPolicy.java index 036a533f1c..9e0087ed95 100644 --- a/src/main/java/nextstep/subway/section/policy/DeleteSectionPolicy.java +++ b/src/main/java/nextstep/subway/section/policy/delete/DeleteSectionPolicy.java @@ -1,4 +1,4 @@ -package nextstep.subway.section.policy; +package nextstep.subway.section.policy.delete; import nextstep.subway.section.repository.Sections; import nextstep.subway.station.repository.Station; diff --git a/src/main/java/nextstep/subway/section/repository/Section.java b/src/main/java/nextstep/subway/section/repository/Section.java index 82e6a342df..6b791dc649 100644 --- a/src/main/java/nextstep/subway/section/repository/Section.java +++ b/src/main/java/nextstep/subway/section/repository/Section.java @@ -1,9 +1,6 @@ package nextstep.subway.section.repository; -import lombok.AccessLevel; -import lombok.Builder; -import lombok.Getter; -import lombok.NoArgsConstructor; +import lombok.*; import nextstep.subway.station.repository.Station; import javax.persistence.*; diff --git a/src/main/java/nextstep/subway/section/repository/Sections.java b/src/main/java/nextstep/subway/section/repository/Sections.java index 6214639860..2afdb9f12f 100644 --- a/src/main/java/nextstep/subway/section/repository/Sections.java +++ b/src/main/java/nextstep/subway/section/repository/Sections.java @@ -3,8 +3,8 @@ import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; -import nextstep.subway.section.policy.AddSectionPolicy; -import nextstep.subway.section.policy.DeleteSectionPolicy; +import nextstep.subway.section.policy.add.AddSectionPolicy; +import nextstep.subway.section.policy.delete.DeleteSectionPolicy; import nextstep.subway.station.repository.Station; import javax.persistence.CascadeType; @@ -12,12 +12,11 @@ import javax.persistence.JoinColumn; import javax.persistence.OneToMany; import java.util.*; -import java.util.stream.Collectors; @Embeddable @NoArgsConstructor(access = AccessLevel.PROTECTED) public class Sections { - @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.REMOVE}) + @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.REMOVE}, orphanRemoval = true) @JoinColumn(name = "line_id") private List
sections = new ArrayList<>(); @@ -27,13 +26,28 @@ public class Sections { @Getter private Long lastStationId; - public void addSection(Section section) { - AddSectionPolicy.validate(this, section); + public void addSection(Section section, AddSectionPolicy policy) { + policy.validate(this, section); + Optional upStation = getAllStation().stream() + .filter(station -> Objects.equals(station, section.getUpStation())) + .findFirst(); + Optional downStation = getAllStation().stream() + .filter(station -> Objects.equals(station, section.getDownStation())) + .findFirst(); + + if (upStation.isPresent() && upStation.get().getId().equals(lastStationId)) { + lastStationId = section.getDownStation().getId(); + } + if (downStation.isPresent() && downStation.get().getId().equals(firstStationId)) { + firstStationId = section.getUpStation().getId(); + } + this.sections.add(section); } public void deleteSectionByLastStation(Station station) { DeleteSectionPolicy.validate(this, station); + this.lastStationId = getLastSection().getUpStation().getId(); this.sections.remove(getLastSection()); } diff --git a/src/test/java/nextstep/subway/acceptance/section/SectionAcceptanceTest.java b/src/test/java/nextstep/subway/acceptance/section/SectionAcceptanceTest.java index 5c457a1b51..2af2d0da1b 100644 --- a/src/test/java/nextstep/subway/acceptance/section/SectionAcceptanceTest.java +++ b/src/test/java/nextstep/subway/acceptance/section/SectionAcceptanceTest.java @@ -36,6 +36,7 @@ void init() { } // TODO: 구간 추가에 대한 상세 사항은 Policy 의 단위 테스트에서 검증한다. + /** * When 지하철 구간을 추가하면 * Then 지하철 노선 조회시, 추가된 구간을 확인할 수 있다 @@ -50,54 +51,6 @@ void createSection() { 추가한_구간이_노선에_포함된다(createdResponse, selectedResponse); } - /** - * Given 하행역이 지하철 노선에 등록된 구간으로 - * When 구간을 등록하면 - * Then 에러가 발생한다 - */ -// @DisplayName("하행역이 이미 지하철 노선에 등록된 지하철 구간을 추가하면 에러가 발생한다.") -// @Test -// void createSection_already_register() { -// CreateSectionRequest request = 하행역이_지하철_노선에_등록된_구간에_대한_요청이_존재한다(); -// -// 구간을_등록하면_에러가_발생한다(request); -// } - - /** - * Given 상행역이 노선의 하행 종점역이 아닌 구간으로 - * When 구간을 등록하면 - * Then 에러가 발생한다 - */ -// @DisplayName("상행역이 노선의 하행 종점역이 아닌 지하철 구간을 추가하면 에러가 발생한다.") -// @Test -// void createSection_not_include() { -// CreateSectionRequest request = 상행역이_노선의_하행_종점역이_아닌_구간에_대한_요청이_존재한다(); -// -// 구간을_등록하면_에러가_발생한다(request); -// } - - // TODO: 구현 필요 - /** - * Given 기존 역 사이 길이보다 크거나 같은 길이의 구간으로 - * When 구간을 등록하면 - * Then 에러가 발생한다 - */ - - // TODO: 구현 필요 - /** - * Given 상행역과 하행역이 모두 기존 노선에 등록되어 있는 구간으로 - * When 구간을 등록하면 - * Then 에러가 발생한다 - */ - - // TODO: 구현 필요 - /** - * Given 상행역과 하행역이 모두 기존 노선에 등록되어 있지 않은 구간으로 - * When 구간을 등록하면 - * Then 에러가 발생한다 - */ - - /** * Given 새로운 지하철 구간을 추가하고 * When 해당 노선의 구간을 제거하면 @@ -106,11 +59,8 @@ void createSection() { @DisplayName("지하철 구간을 삭제한다.") @Test void deleteSection() { - ExtractableResponse selectedResponse = 지하철_노선을_조회한다(); ExtractableResponse createdResponse = 지하철_구간을_추가한다(UP_STATION_ID, DOWN_STATION_ID, DISTANCE); - 지하철_구간을_삭제한다(createdResponse.header("Location") + "/sections?stationId=" + DOWN_STATION_ID); - 삭제한_구간의_역이_노선에서_제외된다(); } diff --git a/src/test/java/nextstep/subway/section/policy/AddSectionPolicyTest.java b/src/test/java/nextstep/subway/section/policy/AddSectionPolicyTest.java deleted file mode 100644 index b66374a722..0000000000 --- a/src/test/java/nextstep/subway/section/policy/AddSectionPolicyTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package nextstep.subway.section.policy; - -import nextstep.fixture.StationFixture; -import nextstep.subway.section.repository.Section; -import nextstep.subway.section.repository.Sections; -import nextstep.subway.station.repository.Station; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import java.util.List; - -import static nextstep.fixture.SectionFixture.강남역_TO_신논현역; -import static nextstep.fixture.SectionFixture.신논현역_TO_논현역; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - - -class AddSectionPolicyTest { - - @DisplayName("기존 구간의 길이보다 추가하려는 구간의 길이가 크거나 같으면 validate 실패한다.") - @Test - void validate_fail_too_long() { - // given - Sections sections = new Sections(List.of(강남역_TO_신논현역(), 신논현역_TO_논현역())); - Section section = Section.builder() - .upStation(StationFixture.신논현역()) - .downStation(new Station("판교역")) - .distance(신논현역_TO_논현역().getDistance() + 1L) - .build(); - - // when - // then - assertThatThrownBy(() -> AddSectionPolicy.validate(sections, section)) - .isInstanceOf(RuntimeException.class) - .hasMessage("Section's distance too long"); - } - - @DisplayName("추가하려는 구간의 상행역,하행역이 이미 모두 노선 내 구간에 등록되어 있다면 validate 실패한다.") - @Test - void validate_fail_already_register() { - // given - Sections sections = new Sections(List.of(강남역_TO_신논현역(), 신논현역_TO_논현역())); - Section section = 강남역_TO_신논현역(); - - // when - // then - assertThatThrownBy(() -> AddSectionPolicy.validate(sections, section)) - .isInstanceOf(RuntimeException.class) - .hasMessage("Section's stations already registered"); - } - - @DisplayName("추가하려는 구간의 상행역,하행역이 모두 노선 내 구간에 없다면 validate 실패한다.") - @Test - void validate_fail_not_exist() { - // given - Sections sections = new Sections(List.of(강남역_TO_신논현역(), 신논현역_TO_논현역())); - Section section = Section.builder() - .upStation(new Station("판교역")) - .downStation(new Station("광교역")) - .distance(7L) - .build(); - - // when - // then - assertThatThrownBy(() -> AddSectionPolicy.validate(sections, section)) - .isInstanceOf(RuntimeException.class) - .hasMessage("Section's stations not exist in sections"); - } -} diff --git a/src/test/java/nextstep/subway/section/policy/add/CheckDistancePolicyTest.java b/src/test/java/nextstep/subway/section/policy/add/CheckDistancePolicyTest.java new file mode 100644 index 0000000000..2ca11cffb0 --- /dev/null +++ b/src/test/java/nextstep/subway/section/policy/add/CheckDistancePolicyTest.java @@ -0,0 +1,49 @@ +package nextstep.subway.section.policy.add; + +import nextstep.subway.section.repository.Section; +import nextstep.subway.section.repository.Sections; +import nextstep.subway.station.repository.Station; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +class CheckDistancePolicyTest { + + private AddSectionPolicy addSectionPolicy = new CheckDistancePolicy(); + + @DisplayName("기존 구간의 길이보다 추가하려는 구간의 길이가 크거나 같으면 validate 실패한다.") + @Test + void validate() { + // given + Station 강남역 = mock(Station.class); + Station 신논현역 = mock(Station.class); + Station 논현역 = mock(Station.class); + given(강남역.getId()).willReturn(1L); + given(신논현역.getId()).willReturn(2L); + given(논현역.getId()).willReturn(3L); + Sections sections = new Sections(new ArrayList<>(List.of( + Section.builder() + .upStation(강남역) + .downStation(신논현역) + .distance(10L) + .build() + ))); + Section section = Section.builder() + .upStation(논현역) + .downStation(신논현역) + .distance(11L) + .build(); + + // when + // then + assertThatThrownBy(() -> addSectionPolicy.validate(sections, section)) + .isInstanceOf(RuntimeException.class) + .hasMessage("Section's distance too long"); + } +} diff --git a/src/test/java/nextstep/subway/section/policy/add/CheckNotRegisterPolicyTest.java b/src/test/java/nextstep/subway/section/policy/add/CheckNotRegisterPolicyTest.java new file mode 100644 index 0000000000..88d6fd37e2 --- /dev/null +++ b/src/test/java/nextstep/subway/section/policy/add/CheckNotRegisterPolicyTest.java @@ -0,0 +1,50 @@ +package nextstep.subway.section.policy.add; + +import nextstep.subway.section.repository.Section; +import nextstep.subway.section.repository.Sections; +import nextstep.subway.station.repository.Station; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +class CheckNotRegisterPolicyTest { + + private AddSectionPolicy addSectionPolicy = new CheckNotRegisterPolicy(); + + @DisplayName("추가하려는 구간의 상행역,하행역이 모두 노선 내 구간에 없다면 validate 실패한다.") + @Test + void validate() { + // given + Station 강남역 = mock(Station.class); + Station 신논현역 = mock(Station.class); + Station 논현역 = mock(Station.class); + Station 판교역 = mock(Station.class); + given(강남역.getId()).willReturn(1L); + given(신논현역.getId()).willReturn(2L); + given(논현역.getId()).willReturn(3L); + given(판교역.getId()).willReturn(4L); + Sections sections = new Sections(new ArrayList<>(List.of( + Section.builder() + .upStation(강남역) + .downStation(신논현역) + .distance(10L) + .build() + ))); + Section section = Section.builder() + .upStation(논현역) + .downStation(판교역) + .distance(11L) + .build(); + // when + // then + assertThatThrownBy(() -> addSectionPolicy.validate(sections, section)) + .isInstanceOf(RuntimeException.class) + .hasMessage("Section's stations not exist in sections"); + } +} diff --git a/src/test/java/nextstep/subway/section/policy/add/CheckRegisterPolicyTest.java b/src/test/java/nextstep/subway/section/policy/add/CheckRegisterPolicyTest.java new file mode 100644 index 0000000000..9d28147812 --- /dev/null +++ b/src/test/java/nextstep/subway/section/policy/add/CheckRegisterPolicyTest.java @@ -0,0 +1,48 @@ +package nextstep.subway.section.policy.add; + +import nextstep.subway.section.repository.Section; +import nextstep.subway.section.repository.Sections; +import nextstep.subway.station.repository.Station; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.util.ArrayList; +import java.util.List; + +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; + +class CheckRegisterPolicyTest { + + private AddSectionPolicy addSectionPolicy = new CheckRegisterPolicy(); + + @DisplayName("추가하려는 구간의 상행역,하행역이 이미 모두 노선 내 구간에 등록되어 있다면 validate 실패한다.") + @Test + void validate() { + // given + Station 강남역 = mock(Station.class); + Station 신논현역 = mock(Station.class); + given(강남역.getId()).willReturn(1L); + given(신논현역.getId()).willReturn(2L); + Sections sections = new Sections(new ArrayList<>(List.of( + Section.builder() + .upStation(강남역) + .downStation(신논현역) + .distance(10L) + .build() + ))); + Section section = Section.builder() + .upStation(강남역) + .downStation(신논현역) + .distance(11L) + .build(); + + // when + // then + assertThatThrownBy(() -> addSectionPolicy.validate(sections, section)) + .isInstanceOf(RuntimeException.class) + .hasMessage("Section's stations already registered"); + } +} diff --git a/src/test/java/nextstep/subway/unit/LineServiceMockTest.java b/src/test/java/nextstep/subway/unit/LineServiceMockTest.java index 90d858336f..a8e07714d4 100644 --- a/src/test/java/nextstep/subway/unit/LineServiceMockTest.java +++ b/src/test/java/nextstep/subway/unit/LineServiceMockTest.java @@ -3,6 +3,7 @@ import nextstep.subway.line.repository.LineRepository; import nextstep.subway.line.service.LineService; import nextstep.subway.section.dto.CreateSectionRequest; +import nextstep.subway.section.policy.add.AddSectionPolicy; import nextstep.subway.station.service.StationFindable; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; @@ -16,6 +17,7 @@ import static nextstep.fixture.LineFixture.신분당선_ID; import static nextstep.fixture.StationFixture.*; import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; @ExtendWith(MockitoExtension.class) @@ -24,12 +26,14 @@ public class LineServiceMockTest { private LineRepository lineRepository; @Mock private StationFindable stationFindable; + @Mock + private AddSectionPolicy addSectionPolicy; private LineService lineService; @BeforeEach void init() { - lineService = new LineService(lineRepository, stationFindable); + lineService = new LineService(lineRepository, stationFindable, addSectionPolicy); } @Test diff --git a/src/test/java/nextstep/subway/unit/LineServiceTest.java b/src/test/java/nextstep/subway/unit/LineServiceTest.java index e491f52727..28d2b771d9 100644 --- a/src/test/java/nextstep/subway/unit/LineServiceTest.java +++ b/src/test/java/nextstep/subway/unit/LineServiceTest.java @@ -4,6 +4,9 @@ import nextstep.subway.line.repository.LineRepository; import nextstep.subway.line.service.LineService; import nextstep.subway.section.dto.CreateSectionRequest; +import nextstep.subway.section.policy.add.AddSectionPolicy; +import nextstep.subway.section.repository.Section; +import nextstep.subway.section.repository.Sections; import nextstep.subway.station.service.StationFindable; import nextstep.subway.unit.fake.FakeLineRepository; import nextstep.subway.unit.fake.FakeStationFindable; @@ -17,7 +20,13 @@ public class LineServiceTest { private final LineRepository lineRepository = new FakeLineRepository(); private final StationFindable stationFindable = new FakeStationFindable(); - private final LineService lineService = new LineService(lineRepository, stationFindable); + private final AddSectionPolicy addSectionPolicy = new AddSectionPolicy() { + @Override + public void validate(Sections sections, Section section) { + // nothing + } + }; + private final LineService lineService = new LineService(lineRepository, stationFindable, addSectionPolicy); @Test void addSection() { diff --git a/src/test/java/nextstep/subway/unit/SectionsTest.java b/src/test/java/nextstep/subway/unit/SectionsTest.java index 58777f5d3b..6481b250fe 100644 --- a/src/test/java/nextstep/subway/unit/SectionsTest.java +++ b/src/test/java/nextstep/subway/unit/SectionsTest.java @@ -1,5 +1,6 @@ package nextstep.subway.unit; +import nextstep.subway.section.policy.add.AddSectionPolicy; import nextstep.subway.section.repository.Section; import nextstep.subway.section.repository.Sections; import nextstep.subway.station.repository.Station; @@ -19,6 +20,9 @@ @ExtendWith(MockitoExtension.class) class SectionsTest { + + private AddSectionPolicy addSectionPolicy = mock(AddSectionPolicy.class); + @Test void addSection() { // given @@ -40,7 +44,7 @@ void addSection() { .upStation(신논현역) .downStation(논현역) .distance(5L) - .build()); + .build(), addSectionPolicy); // then assertThat(sections.size()).isEqualTo(2); @@ -68,7 +72,7 @@ void addSection_change_firstStationId() { .upStation(강남역) .downStation(신논현역) .distance(10L) - .build()); + .build(), addSectionPolicy); // then assertThat(sections.getFirstStationId()).isEqualTo(1L); @@ -96,7 +100,7 @@ void addSection_change_lastStationId() { .upStation(신논현역) .downStation(논현역) .distance(5L) - .build()); + .build(), addSectionPolicy); // then assertThat(sections.getLastStationId()).isEqualTo(3L);