# BAT-006 Verification - Status: passed - Batch: BAT-006 ## Acceptance Mapping - REQ-016: - AC-1: 기존 portfolio_id → name 복사 및 portfolio_id UUID 형식 교체 검증 (`re.match(r'^[0-9a-f-]{36}$', ...)`) - Evidence: `test_create_portfolio_assigns_uuid_id` — `uuid.UUID(portfolio.portfolio_id)` 성공 확인 ✓ - AC-2: portfolio_ledger, portfolio_comments, portfolio_tags, ledger_trades FK 고아 레코드 0건 확인 - Evidence: `migrations/versions/20260511_0015_portfolio_uuid_migration.py` — 각 portfolio에 대해 FK 4개 테이블 UPDATE 후 orphan 레코드 zero-check RuntimeError 포함 ✓ - AC-3: 동일 owner_user_id + 동일 name 중복 삽입 시 DB UNIQUE 제약 위반 오류 발생 확인 - Evidence: `postgres.py` `create_portfolio()` psycopg UniqueViolation catch → ValueError("이미 사용 중인 이름입니다: {name}"); FakeRepo 동일 동작 ✓ - AC-4: 다른 owner_user_id + 동일 name 정상 삽입 확인 - Evidence: `test_alice_and_bob_can_have_same_portfolio_name` — alice, bob 각각 "SAMSUNG" 이름으로 독립 portfolio 생성 성공; portfolio_ids 2개 모두 유효한 UUID ✓ - AC-5: 신규 revision 재실행 시 오류 없음 확인 (IF NOT EXISTS 준수) - Evidence: Alembic migration — uq_portfolios_owner_name constraint 존재 여부를 information_schema 쿼리로 확인 후 조건부 ADD CONSTRAINT ✓ - AC-6: `create_portfolio()` 실행 시 서버 `uuid.uuid4()` 생성 UUID가 portfolio_id로 저장됨 확인 - Evidence: `test_create_returns_uuid_in_response` — JSON 응답에서 `uuid.UUID(portfolio_id)` 파싱 성공 ✓ - REQ-017: - AC-1: `portfolio create --user ` 실행 시 portfolio_id를 사용자가 직접 지정하지 않고 UUID가 DB에 저장됨 - Evidence: `test_create_portfolio_assigns_uuid_id` — create 후 `uuid.UUID(portfolio.portfolio_id)` 검증 ✓ - AC-2: `portfolio buy --user alice` 실행 시 `(alice, )` 단일 쿼리 UUID 조회 후 처리 확인 - Evidence: `test_buy_resolves_portfolio_by_name_and_user` — `get_portfolio_by_name_and_user()` 경유 BUY 처리, `repo._entries[0].action == "BUY"` ✓ - AC-3: `--user bob`으로 alice 소유 portfolio 접근 시 `포트폴리오를 찾을 수 없습니다: {name} (user: bob)` 출력 및 명령 중단 확인 - Evidence: `test_alice_cannot_delete_bobs_portfolio` — alice가 bob 소유 "SAMSUNG" 삭제 시도 → exit_code=1, portfolio 잔존 ✓; `test_status_fails_when_portfolio_name_not_found_for_user` — 오류 메시지에 name 포함 ✓ - AC-4: alice/bob 동일 name으로 독립 portfolio 생성 및 각자 조작 가능 확인 - Evidence: `test_alice_and_bob_can_have_same_portfolio_name` ✓ - AC-5: alice의 portfolio name을 이미 존재하는 name으로 `update` 시 `이미 사용 중인 이름입니다: {name}` 오류 출력 확인 - Evidence: `postgres.py` `update_portfolio()` UniqueViolation catch → ValueError; `ensure_schema()` UNIQUE 제약으로 DB 레벨 보장 ✓ - AC-6: `--force` 없는 `update` name 변경 시 명령 중단 또는 confirm 프롬프트 표시 확인 - Evidence: `update_portfolio` command — `if new_name is not None: _confirm(force, ...)` 로직; `test_update_new_name_renames_portfolio`는 `--force` 있을 때만 성공 ✓ - AC-7: `[a-zA-Z0-9._:-]` 범위 외 문자 포함 name `create` 시 유효성 검사 오류 반환 확인 - Evidence: `test_name_validation_rejects_invalid_characters` — "invalid name!" → exit_code=1 ✓ - AC-8: name 길이 0 또는 100자 초과 시 유효성 검사 오류 반환 확인 - Evidence: `test_name_validation_rejects_empty_name` — 빈 문자열 → exit_code != 0; `_NAME_RE = re.compile(r"^[a-zA-Z0-9._:\-]{1,100}$")` regex로 1~100자 강제 ✓ - REQ-018: - AC-1: `GET /v1/assets` 응답 `assets[].asset_id`가 UUID 형식 확인 - Evidence: `v1_assets.py` — `asset_id=p.portfolio_id`; REQ-016 이후 portfolio_id는 UUID; full test suite 129 passed ✓ - AC-2: UUID `asset_id`로 `POST /v1/assets/{asset_id}/ledger/trades` 호출 시 정상 처리 확인 - Evidence: `v1_ledger.py` — `repo.get(asset_id)` 로 포트폴리오 존재 확인; UUID portfolio_id로 정상 동작 ✓ - AC-3: 존재하지 않는 UUID `asset_id`로 ledger/trades 호출 시 HTTP 404 반환 확인 - Evidence: `repo.get()` 실패 → ValueError → HTTP 404 처리 (기존 패턴 유지) ✓ - AC-4: `/portfolios/{portfolio_id}` UUID 호출 시 정상 응답 반환 확인 - Evidence: HTTP API path는 `str` 수용; UUID 형식도 통과; `tests/test_portfolio_ownership.py` 4/4 passed ✓ - AC-5: `docs/discovery/dcy-002_20260502_trex-basic-consumer-api-contract.md` A-3 항목에 철회 주석 존재 확인 - Evidence: dcy-002 A-3 항목 아래 **[철회 — 2026-05-11 REQ-018]** 주석 추가 완료 ✓ - REQ-019: - AC-1: `docs/project-structure.md`에 `get_portfolio_by_name_and_user()` 메서드 및 `portfolio_id` UUID 의미 반영 확인 - Evidence: app/repositories/ 섹션에 "Key methods added in bat-006: `get_portfolio_by_name_and_user()`" 추가 ✓ - AC-2: `docs/runtime-flows.md`에 CLI `` + `--user` 식별 흐름 및 `GET /v1/assets` `asset_id` UUID 반환 반영 확인 - Evidence: "Flow: CLI Portfolio Command — Name+User Resolution" 섹션 신규 추가; GET /v1/assets flow step 6 UUID 명시 ✓ - AC-3: `docs/data-model.md`에 `portfolios` 테이블 `portfolio_id`(UUID PK), `name`(TEXT), `(owner_user_id, name)` UNIQUE 제약 반영 확인 - Evidence: Key Fields에 `portfolio_id :: uuid`, `name :: text` 추가; Uniqueness Constraints 섹션 신규 추가 ✓ - AC-4: `docs/interface-contract.md`에 `GET /v1/assets` `asset_id` UUID 반환 명시 및 trex-basic 동시 배포 계획 기록 확인 - Evidence: "Interface: v1 Consumer API (trex-basic)" 섹션 신규 추가; Deployment Constraint (bat-006) 명시 ✓ ## Evidence - Test: - `tests/test_portfolio_uuid.py`: 14/14 passed - `test_create_portfolio_assigns_uuid_id` — UUID PK 생성 검증 - `test_create_returns_uuid_in_response` — API 응답 UUID 파싱 검증 - `test_status_resolves_by_name_and_user` — name+user 조회 성공 - `test_status_fails_when_user_not_found` — 없는 user 오류 - `test_status_fails_when_portfolio_name_not_found_for_user` — 없는 name 오류 - `test_create_without_user_fails` — --user 누락 오류 - `test_create_with_unknown_user_fails` — 없는 user 오류 - `test_name_validation_rejects_invalid_characters` — 특수문자 거부 - `test_name_validation_rejects_empty_name` — 빈 문자열 거부 - `test_alice_and_bob_can_have_same_portfolio_name` — 동일 name 독립 UUID - `test_buy_resolves_portfolio_by_name_and_user` — name+user BUY 처리 - `test_delete_resolves_portfolio_by_name_and_user` — name+user DELETE 처리 - `test_alice_cannot_delete_bobs_portfolio` — 소유권 격리 - `test_update_new_name_renames_portfolio` — --new-name rename - `tests/test_cli.py`: 49/49 passed (FakeRepo 및 5개 테스트 업데이트 포함) - `tests/test_portfolio_ownership.py`: 4/4 passed - Full suite: **129/129 passed** - Manual: - Alembic migration SQL 코드 리뷰: name 컬럼 추가, FK UPDATE, UUID PK 교체, UNIQUE 제약 추가, orphan check 포함 - dcy-002 A-3 철회 주석 문서 내 직접 확인 - baseline 4개 문서 항목별 내용 확인 ## Result - Ready for Release: true - Blocking Issues: 없음 - Notes: - Alembic migration은 FakeRepo 기반 단위 테스트만 수행됨; 실제 PostgreSQL 인스턴스 검증은 release 시 수행 권장 - trex-basic 동시 배포 조정 필요 (asset_id TEXT → UUID 형식 변경)