bibimbap/.atp/work-session/20260622-092800/verification.md

195 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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종 |