Files
portfolio-hub/docs/batches/bat-006_20260511_portfolio-uuid-migration/verification.md
T
genichin 829629fa8f feat: implement portfolio UUID migration and v1 API finalization (BAT-006, BAT-001 re-verified)
Core Changes:
- Migrate portfolio_id from string to UUID in schema and repository layer
- Update portfolio CLI commands to use name+user combination for identification
- Update HTTP API to accept portfolio_id as UUID path parameter
- Implement portfolio ownership validation in repository queries

Requirements Completed (REQ-016 to REQ-019):
- REQ-016: portfolios table UUID schema migration with data migration script
- REQ-017: CLI portfolio commands with name+user identification
- REQ-018: HTTP API portfolio path parameters and v1 asset_id UUID returns
- REQ-019: Portfolio UUID baseline documentation updates

BAT-001 Re-verification:
- REQ-004~010 marked as Implemented after verification evidence validation
- All v1 API endpoints verified with comprehensive test coverage

Documentation Updates:
- Update data-model.md with portfolio UUID structure
- Update interface-contract.md with UUID path parameters
- Update runtime-flows.md with ownership-based resolution flows
- Update project-structure.md with database layer changes
- Add migration script for portfolio UUID transition

Testing:
- Add test_portfolio_uuid.py for migration validation
- Update test_cli.py and test_portfolio_ownership.py for ownership checks
2026-05-11 09:27:57 +00:00

7.6 KiB

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 <name> --user <username> 실행 시 portfolio_id를 사용자가 직접 지정하지 않고 UUID가 DB에 저장됨
      • Evidence: test_create_portfolio_assigns_uuid_id — create 후 uuid.UUID(portfolio.portfolio_id) 검증 ✓
    • AC-2: portfolio buy <name> --user alice 실행 시 (alice, <name>) 단일 쿼리 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 <name> + --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 형식 변경)