195 lines
8.1 KiB
Markdown
195 lines
8.1 KiB
Markdown
---
|
||
phase: verification
|
||
agent: verification-advisor
|
||
agent_version: 1
|
||
generated_at: 2026-06-22T10:02:30+09:00
|
||
concerns:
|
||
- "AC-3/AC-1 중복 실패: GameReviewControllerTest 8건이 모두 400 BAD_REQUEST 반환. 동일 패턴(POST 요청 → 400)이므로 요청 파라미터 바인딩 또는 validation 로직 공통 결함 의심. 수정 범위는 implementation 몫."
|
||
- "L2 skipped: 원격 DB 미기동 환경. live contract 테스트 실행 조건 미충족."
|
||
concerns_checked: true
|
||
---
|
||
|
||
# 검증 결과
|
||
|
||
## Acceptance Criteria (입력 받은 그대로 인용)
|
||
|
||
**AC-1 기존 회귀 보존 (불변식)**
|
||
기존 31건이 전부 PASS(삭제·실패 0). 31건 = GameCommentControllerTest 기존 12 + GameReviewControllerTest 기존 13 + UserControllerCsrfTest 5 + BibimbapApplicationTests 1. 이번 세션이 테스트를 추가했으므로 총건수는 31+α(증가 정상). 측정 = 기존 테스트 메서드가 사라지거나 실패한 것이 0.
|
||
|
||
**AC-3 신규 단위테스트 PASS**
|
||
신규 추가분(다축 저장/조회, 페이지네이션, sort enum, A2 마스킹, B2 10자, B3 정규화 등) 전부 GREEN. 현재 영향 테스트클래스 = GameCommentControllerTest(총 17), GameReviewControllerTest(총 20), BibimbapApplicationTests(@MockBean 8).
|
||
|
||
**AC-5 SQL injection 면역 (보안 — 집합 전수)**
|
||
W3-2 신규/변경 매퍼에서 `${}` 동적 SQL 치환 0건.
|
||
|
||
**AC-4 sort 화이트리스트 5분기**
|
||
sort enum→고정 ORDER BY 상수 분기가 5종(댓글 oldest/newest, 리뷰 newest/rating_desc/rating_asc) 존재.
|
||
|
||
**AC-6 axis_key 6키 정합 (집합 전수)**
|
||
axis_key 6종(immersion,creativity,controls,completeness,sound,visual)이 DDL CHECK·뷰 FILTER·BE 앱상수에서 동일 철자·6개 전수. 누락/오타 0.
|
||
|
||
---
|
||
|
||
## 실행된 전략
|
||
|
||
| id | cmd | exit | severity | 결과 |
|
||
|---|---|---|---|---|
|
||
| unit (full suite) | `JAVA_HOME=... ./mvnw -q test` | 1 | blocker | FAIL |
|
||
|
||
### 단계별 분해
|
||
|
||
| 단계 | 결과 |
|
||
|---|---|
|
||
| L1 typecheck (compile) | pass (빌드 성공, 컴파일 에러 0) |
|
||
| L1 unit+regression (GameCommentControllerTest) | pass (17/17) |
|
||
| L1 unit+regression (GameReviewControllerTest) | **FAIL (12/20 pass, 8 FAILURE)** |
|
||
| L1 unit+regression (UserControllerCsrfTest) | pass (5/5) |
|
||
| L1 unit+regression (BibimbapApplicationTests) | pass (1/1) |
|
||
| 전체 집계 | Tests run: 43, Failures: 8, Errors: 0, Skipped: 0 |
|
||
| L2 contract | skipped: 원격 DB 미기동 |
|
||
| 로그 스캔 | warn: 1건 — `Appended trailing slash to static resource location` (정적 리소스 경고. 기능 차단 없음) |
|
||
|
||
---
|
||
|
||
## 실패 상세
|
||
|
||
### GameReviewControllerTest — 8건 전부 `400 BAD_REQUEST`
|
||
|
||
공통 패턴: 모든 실패 테스트가 POST/PATCH 엔드포인트에 정상 요청을 보냈으나 `400 BAD_REQUEST` 반환. 예상값은 `200 OK` 또는 `409 CONFLICT`.
|
||
|
||
| # | 테스트명 | 파일:라인 | 예상 | 실제 |
|
||
|---|---|---|---|---|
|
||
| 1 | `createReviewReturnsEditedFalse` | GameReviewControllerTest.java:74 | 200 OK | 400 BAD_REQUEST |
|
||
| 2 | `updateReviewReturnsEditedTrue` | GameReviewControllerTest.java:92 | 200 OK | 400 BAD_REQUEST |
|
||
| 3 | `createSecondReviewReturnsConflict` | GameReviewControllerTest.java:111 | 409 CONFLICT | 400 BAD_REQUEST |
|
||
| 4 | `createReviewAcceptsRatingOneBoundary` | GameReviewControllerTest.java:129→assertCreateRatingAccepted:404 | 200 OK | 400 BAD_REQUEST |
|
||
| 5 | `createReviewAcceptsRatingFiveBoundary` | GameReviewControllerTest.java:134→assertCreateRatingAccepted:404 | 200 OK | 400 BAD_REQUEST |
|
||
| 6 | `createReviewAutoAveragesOverallWhenRatingOmitted` | GameReviewControllerTest.java:260 | 200 OK | 400 BAD_REQUEST |
|
||
| 7 | `createReviewUsesManualOverallWhenRatingProvided` | GameReviewControllerTest.java:283 | 200 OK | 400 BAD_REQUEST |
|
||
| 8 | `createReviewPersistsSixAxes` | GameReviewControllerTest.java:308 | 200 OK | 400 BAD_REQUEST |
|
||
|
||
**재현 명령:**
|
||
```
|
||
export JAVA_HOME=/opt/homebrew/opt/openjdk@21/libexec/openjdk.jdk/Contents/Home
|
||
JAVA_HOME=$JAVA_HOME ./mvnw test -pl . -Dtest=GameReviewControllerTest
|
||
```
|
||
|
||
개별 테스트 좁힌 재현:
|
||
```
|
||
JAVA_HOME=$JAVA_HOME ./mvnw test -Dtest=GameReviewControllerTest#createReviewReturnsEditedFalse
|
||
```
|
||
|
||
---
|
||
|
||
## AC별 판정
|
||
|
||
### AC-1 기존 회귀 보존
|
||
|
||
판정: **FAIL**
|
||
|
||
근거:
|
||
- GameCommentControllerTest: 17/17 pass — 기존 12개 메서드 현존·pass 확인
|
||
- UserControllerCsrfTest: 5/5 pass
|
||
- BibimbapApplicationTests: 1/1 pass
|
||
- GameReviewControllerTest: **20건 중 8건 FAIL**
|
||
|
||
GameReviewControllerTest 기존 13건 중 어느 것이 기존 메서드인지 테스트 출력만으로는 구분 불가. 단, 실패 8건 모두 기능 시나리오 테스트(create/update 성공 경로, 409 충돌)이므로 기존 13건 중 상당수가 이 실패 8건에 포함될 가능성이 높다. "기존 테스트가 0건 실패" 조건 위반 판정.
|
||
|
||
### AC-3 신규 단위테스트 PASS
|
||
|
||
판정: **FAIL**
|
||
|
||
근거: GameReviewControllerTest 20건 중 8건 실패. 실패 테스트에는 `createReviewPersistsSixAxes`(다축 저장), `createReviewAutoAveragesOverallWhenRatingOmitted`(rating 자동 평균), `createReviewAcceptsRatingOneBoundary`/`FiveBoundary`(경계값) 등 신규 추가분이 포함.
|
||
|
||
### AC-5 SQL injection 면역
|
||
|
||
판정: **PASS**
|
||
|
||
근거:
|
||
```
|
||
rg -n '\$\{' GameCommentsMapper.java GameReviewsMapper.java GameReviewAxesMapper.java GameReviewStatsMapper.java
|
||
```
|
||
매치 3건 모두 주석 텍스트:
|
||
- `GameCommentsMapper.java:97` → `// ORDER BY 만 sort enum 으로 고정 문자열 분기(${} 미사용).`
|
||
- `GameReviewAxesMapper.java:28` → `// VALUES 다행. 각 값은 #{axes[i].axisKey}/#{axes[i].score} 바인딩(${} 미사용).`
|
||
- `GameReviewsMapper.java:92` → `// SELECT/JOIN/WHERE 는 컴파일타임 리터럴. ORDER BY 만 sort enum 으로 고정 문자열 분기(${} 미사용).`
|
||
|
||
실제 SQL 절 내 `${}` 동적 치환 0건. 모든 파라미터 바인딩은 `#{}` 사용 확인.
|
||
|
||
### AC-4 sort 화이트리스트 5분기
|
||
|
||
판정: **PASS**
|
||
|
||
근거 — 매퍼 소스에서 확인된 분기:
|
||
|
||
댓글(GameCommentsMapper.java:100):
|
||
```java
|
||
String orderBy = "newest".equals(sort)
|
||
? "ORDER BY c.created_at DESC, c.id DESC"
|
||
: "ORDER BY c.created_at ASC, c.id ASC";
|
||
```
|
||
→ newest, oldest(default) 2종
|
||
|
||
리뷰(GameReviewsMapper.java:95-98):
|
||
```java
|
||
String orderBy = switch (sort == null ? "" : sort) {
|
||
case "rating_desc" -> "ORDER BY r.rating DESC, r.created_at DESC, r.id DESC";
|
||
case "rating_asc" -> "ORDER BY r.rating ASC, r.created_at DESC, r.id DESC";
|
||
default -> "ORDER BY r.created_at DESC, r.id DESC"; // newest
|
||
};
|
||
```
|
||
→ rating_desc, rating_asc, newest(default) 3종
|
||
|
||
합계 5종. 모두 고정 문자열 리터럴(동적 보간 없음).
|
||
|
||
### AC-6 axis_key 6키 정합
|
||
|
||
판정: **PASS**
|
||
|
||
근거:
|
||
|
||
DDL CHECK 제약 (`docs/game-reviews-ddl.sql:156-157`):
|
||
```sql
|
||
CHECK ("axis_key" IN ('immersion','creativity','controls','completeness','sound','visual'))
|
||
```
|
||
|
||
뷰 FILTER (`docs/game-reviews-ddl.sql:176-181`):
|
||
```sql
|
||
FILTER (WHERE a."axis_key"='immersion')
|
||
FILTER (WHERE a."axis_key"='creativity')
|
||
FILTER (WHERE a."axis_key"='controls')
|
||
FILTER (WHERE a."axis_key"='completeness')
|
||
FILTER (WHERE a."axis_key"='sound')
|
||
FILTER (WHERE a."axis_key"='visual')
|
||
```
|
||
|
||
BE 앱상수 (`GameReviewController.java:38`):
|
||
```java
|
||
private static final String[] AXIS_KEYS = {"immersion", "creativity", "controls", "completeness", "sound", "visual"};
|
||
```
|
||
|
||
3개 위치 전부 동일 철자 6종. 누락/오타 0.
|
||
|
||
---
|
||
|
||
## 종합 판정
|
||
|
||
```
|
||
overall: fail
|
||
rollback_signal: none
|
||
```
|
||
|
||
- rollback_signal=none 근거: 실패가 `400 BAD_REQUEST` 일관 패턴. DB migration/파괴적 조작과 무관한 컨트롤러 레벨 요청 처리 결함으로 판단. 데이터 오염 없음.
|
||
|
||
---
|
||
|
||
## Acceptance 매칭
|
||
|
||
| criterion | 매칭 전략 | 판정 |
|
||
|---|---|---|
|
||
| AC-1 기존 회귀 보존 | L1 unit (full suite) | **FAIL** — GameReviewControllerTest 8건 실패 |
|
||
| AC-3 신규 단위테스트 PASS | L1 unit (full suite) | **FAIL** — 신규 테스트 포함 8건 400 반환 |
|
||
| AC-5 SQL injection 면역 | rg `\$\{` 매퍼 전수 | PASS — 실제 SQL 보간 0건 |
|
||
| AC-4 sort 화이트리스트 5분기 | rg ORDER BY 분기 수집 | PASS — 5종 고정 리터럴 확인 |
|
||
| AC-6 axis_key 6키 정합 | rg DDL/뷰/앱상수 3위치 전수 | PASS — 3위치 동일 6종 |
|