# BAT-015 Implementation - Status: completed - Batch: BAT-015 ## Plan Summary - 변경 순서: REQ-034 → REQ-035 → REQ-033 (planning 권장 순서대로 진행) - 각 REQ 구현 후 부분 회귀 (pytest 전체 + 해당 영역 smoke) - 최종 batch 통합 회귀 + 로컬 smoke ## Changed Files - REQ-034: - `app/schemas/auth.py` — `LoginRequest.model_config = ConfigDict(extra="forbid")` 추가 - `tests/test_auth.py` — `test_login_rejects_no_expiry_extra_field`, `test_login_rejects_arbitrary_extra_field` 신규 - REQ-035: - `app/api/health.py` — `HealthResponse.db_enabled` 필드 추가 + `healthcheck()` 핸들러가 `settings.db_enabled` 노출 - `tests/test_app.py` — `test_healthcheck`에 db_enabled 확인 추가, `test_health_exposes_db_enabled_true`, `test_health_exposes_db_enabled_false` 신규 - `docs/runtime-flows.md` — health endpoint 섹션(line 97)에 `db_enabled` 의미 명문화 노트 추가 - `docs/interface-contract.md` — `/health` payload schema에 `db_enabled` 필드 추가 + 의미 노트 - REQ-033: - `migrations/versions/20260601_0024_ledger_trades_portfolio_id_rename.py` — 신규 alembic revision (ALTER COLUMN RENAME with has_table guard) - `app/domain/models.py` — `LedgerTrade.asset_id` → `portfolio_id` - `app/repositories/portfolio_repository.py` — `insert_ledger_trade` interface 시그니처 갱신 - `app/repositories/postgres.py` — `insert_ledger_trade` INSERT/SELECT/RETURNING SQL 컬럼명 + `_row_to_ledger_trade` 매핑 - `app/schemas/v1.py` — `LedgerTradeResponse.asset_id` → `portfolio_id` - `app/api/v1_portfolios.py` — ledger handler에서 `insert_ledger_trade(portfolio_id=...)` + 응답 매핑 - `tests/test_v1_portfolios_ledger.py` — `LedgerTrade(portfolio_id=...)` + 응답 `body["portfolio_id"]` 검증 - `tests/test_v1.py` — `_make_ledger_trade` + `body["portfolio_id"]` 검증 - `tests/test_daemon_assignment.py` — mock `trade.portfolio_id` 갱신 - `docs/data-model.md` — LedgerTrade entity, Key Fields, Uniqueness Constraints, State Rules의 `asset_id` 표기 모두 `portfolio_id`로 갱신 + revision 20260601_0024 명시 - `docs/interface-contract.md` — `POST /v1/portfolios/{id}/ledger/trades` 응답 schema에 `portfolio_id` 명시 + REQ-033 cross-reference ## Execution Log 1. REQ-034: `LoginRequest`에 `extra="forbid"` 적용 → `tests/test_auth.py` 16 passed 2. REQ-035: `HealthResponse` 확장 + 핸들러 → `tests/test_app.py` 9 passed → baseline 갱신 3. REQ-033: alembic revision 20260601_0024 작성 → `alembic upgrade head` 적용(local DB에 `ledger_trades` 테이블 부재로 `has_table` guard에 의해 안전 skip) → repository/도메인/스키마/라우트 정합 → `tests/test_v1_portfolios_ledger.py` 10 passed 4. 통합 회귀 1차: 5 failed 발견 (test_v1.py + test_daemon_assignment.py 잔존 `asset_id` mock) → 갱신 → 통합 회귀 2차: **372 passed, 0 failed** 5. 로컬 smoke: - GET /health → HTTP 200 + `db_enabled: true` (REQ-035 AC-1) - POST /auth/login + `no_expiry: true` → HTTP 422 + `extra_forbidden` (REQ-034 AC-2) - codebase grep `ledger_trades.*asset_id` → 0건 (REQ-033 AC-3) - `docs/runtime-flows.md` baseline 노트 grep → 매치 (REQ-035 AC-4) - `docs/interface-contract.md` /health payload grep → 매치 (REQ-035 AC-5) ## Validation - pytest 전체: **372 passed, 0 failed** (BAT-015 base 368 → +4: test_auth 2건 + test_app 2건 신규) - 로컬 smoke 5건 모두 통과 - AC 매핑은 verification.md에서 정리 - 실 DB AC (REQ-033 AC-1 `\d ledger_trades`, AC-4 staging dry-run lock 측정): 본 워크스페이스 `ledger_trades` 테이블 미생성 상태로 N/A. REL-002 staging 시점에 별도 수집. ## Remaining Risks - R-실 DB ledger_trades 테이블 미생성: REQ-033 마이그레이션은 `has_table` guard로 안전하지만, 본 워크스페이스에서 ledger_trades 적재 흐름이 활용되지 않는 상태. BAT-011 release-candidate verification은 mock 기반이라 이 gap이 release 단계에서 발견되지 않았음. **본 batch 범위 밖**, 별도 후속 항목으로 등록 후보(REL-002 또는 후속 Discovery). - (workspace_purpose 메모리: 본 워크스페이스 개발용 → prod 영향 검증 N/A)