Skip to content
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
* 라이브러리 지시가 필요하면 **이름만**(예: “use Querydsl”).
* **사실 우선, 추측 금지.** 근거 파일이 없으면 “확인 불가”.
* 충돌 시 **`docs/specs/policy/application.md`** 우선.
* 응답이 필요한 경우 최대한 한글로 답변.

## 1) 스코프

Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ Build/Test 구성 근거: `build.gradle`의 `tasks.named('test')` 설정(Profile
- local: MySQL + JPA `ddl-auto: create`, JWT 시크릿/TTL 설정
- 근거: `application-local.yml`, Docker Compose: [compose.yaml](application/src/main/resources/compose.yaml)
- test: H2 메모리 + MySQL 호환 모드 + JPA `ddl-auto: create`
- 근거: `application-test.yml`
- 근거: `application-test.yml`

민감정보는 운영 환경에서 환경변수로 주입하는 것을 권장합니다(로컬에 예시 값 존재).

Expand All @@ -125,11 +125,11 @@ Build/Test 구성 근거: `build.gradle`의 `tasks.named('test')` 설정(Profile
- CI/CD, 코드 포매터, 마이그레이션 도구(Flyway/Liquibase)는 현재 문서/설정 부재로 "확인 불가" 상태입니다.
- TODO/메모: [docs/devlog/*](docs/devlog), [docs/todo.md](docs/todo.md)
- 권장 향후 작업
- prod 프로필 구성과 비밀 주입 전략 수립
- CI 파이프라인(.github/workflows) 도입
- DB 마이그레이션 도구 채택 및 규약 수립
- 인증/인가 정책 문서
구체화: [docs/specs/policy/authentication.md](docs/specs/policy/authentication.md), [docs/specs/policy/authorization.md](docs/specs/policy/authorization.md)
- prod 프로필 구성과 비밀 주입 전략 수립
- CI 파이프라인(.github/workflows) 도입
- DB 마이그레이션 도구 채택 및 규약 수립
- 인증/인가 정책 문서
구체화: [docs/specs/policy/authentication.md](docs/specs/policy/authentication.md), [docs/specs/policy/authorization.md](docs/specs/policy/authorization.md)

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public NullableQueryFilterBuilder whenHasText(@Nullable String value, Function<S
}

/**
* from, to 둘 다 존재할 때만 between 조건을 추가
* of, to 둘 다 존재할 때만 between 조건을 추가
*
* @param from 요청 구간 시작 (예: LocalDate, LocalDateTime)
* @param to 요청 구간 종료
Expand Down Expand Up @@ -69,7 +69,7 @@ public static NullableQueryFilterBuilder builder() {

// /**
// * 두 값이 모두 존재할 때만 mapper 적용
// * 예: whenBoth(from, to, (f, t) -> show.performanceDate.between(f, t))
// * 예: whenBoth(of, to, (f, t) -> show.performanceDate.between(f, t))
// */
// public <L, R> NullableQueryFilterBuilder whenBoth(@Nullable L left, @Nullable R right, BiFunction<L, R, Predicate> mapper) {
// if (left != null && right != null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,7 @@
class HallCommandRepository {
private final HallRepository jpaRepository;


public Hall insert(Hall hall) {
Hall insert(Hall hall) {
return jpaRepository.save(hall);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package org.mandarin.booking.app.member;

public interface MemberValidator {
interface MemberValidator {
void checkDuplicateEmail(String email);

void checkDuplicateUserId(String userId);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package org.mandarin.booking.adapter.security;

import static org.assertj.core.api.Assertions.assertThat;

import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
class BCryptSecurePasswordEncoderTest {
@Test
void encode(@Autowired BCryptSecurePasswordEncoder encoder) {
String rawPassword = "password123";
String encodedPassword = encoder.encode(rawPassword);
assertThat(encodedPassword).isNotEqualTo(rawPassword);
assertThat(encoder.matches(rawPassword, encodedPassword)).isTrue();
}

@Test
void matches(@Autowired BCryptSecurePasswordEncoder encoder) {
String rawPassword = "password123";
String encodedPassword = encoder.encode(rawPassword);
assertThat(encoder.matches(rawPassword, encodedPassword)).isTrue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ public Show insertDummyShow(LocalDate performanceStartDate, LocalDate performanc
"synopsis",
"https://example.com/poster.jpg",
performanceStartDate,
performanceEndDate
performanceEndDate,
"KRW",
List.of(new ShowRegisterRequest.GradeRequest("VIP", 100000, 100))
)
);
var show = Show.create(hall.getId(), command);
Expand Down Expand Up @@ -174,7 +176,9 @@ public void generateShows(int showCount, int before, int after) {
"공연 줄거리",
"https://example.com/poster.jpg",
LocalDate.now().minusDays(random.nextInt(before)),
LocalDate.now().plusDays(random.nextInt(after))
LocalDate.now().plusDays(random.nextInt(after)),
"KRW",
List.of(new ShowRegisterRequest.GradeRequest("VIP", 100000, 100))
);
var show = Show.create(hallId, ShowCreateCommand.from(request));
showInsert(show);
Expand All @@ -191,7 +195,9 @@ public Show generateShowWithNoSynopsis(int scheduleCount) {
null,
"https://example.com/poster.jpg",
LocalDate.now(),
LocalDate.now().plusDays(30)
LocalDate.now().plusDays(30),
"KRW",
List.of(new ShowRegisterRequest.GradeRequest("VIP", 100000, 100))
)));

for (int i = 0; i < scheduleCount; i++) {
Expand All @@ -215,6 +221,7 @@ public boolean existsHallName(String hallName) {
}

public void removeShows() {
entityManager.createQuery("DELETE FROM Grade ").executeUpdate();
entityManager.createQuery("DELETE FROM Show ").executeUpdate();
}

Expand All @@ -230,6 +237,16 @@ public Hall findHallById(Long hallId) {
.getSingleResult();
}

public Show findShowByTitle(String title) {
return entityManager.createQuery(
"SELECT s FROM Show s " +
"JOIN FETCH s.grades grade " +
"WHERE s.title = :title", Show.class)
.setParameter("title", title)
.getSingleResult();
}


public boolean isMatchingScheduleInShow(ShowScheduleResponse res, Show show) {
return !entityManager.createQuery(
"SELECT s FROM ShowSchedule s WHERE s.id = :scheduleId AND s.show.id = :showId", Object.class)
Expand All @@ -238,6 +255,12 @@ public boolean isMatchingScheduleInShow(ShowScheduleResponse res, Show show) {
.getResultList().isEmpty();
}

private Show generateShow(Long hallId) {
var request = validShowRegisterRequest(hallId, randomEnum(Type.class).name(), randomEnum(Rating.class).name());
var show = Show.create(hallId, ShowCreateCommand.from(request));
return showInsert(show);
}

private void generateShow(Long hallId, Type type) {
var request = validShowRegisterRequest(hallId, type.name(), randomEnum(Rating.class).name());
var show = Show.create(hallId, ShowCreateCommand.from(request));
Expand All @@ -253,7 +276,12 @@ private ShowRegisterRequest validShowRegisterRequest(Long hallId, String type, S
"공연 줄거리",
"https://example.com/poster.jpg",
LocalDate.now(),
LocalDate.now().plusDays(30)
LocalDate.now().plusDays(30),
"KRW",
List.of(
new ShowRegisterRequest.GradeRequest("VIP", 180000, 100),
new ShowRegisterRequest.GradeRequest("R", 150000, 30)
)
);
}

Expand All @@ -263,12 +291,6 @@ private void generateShow(Long hallId, Rating rating) {
showInsert(show);
}

public Show generateShow(Long hallId) {
var request = validShowRegisterRequest(hallId, randomEnum(Type.class).name(), randomEnum(Rating.class).name());
var show = Show.create(hallId, ShowCreateCommand.from(request));
return showInsert(show);
}

private Show showInsert(Show show) {
entityManager.persist(show);
return show;
Expand Down
Loading