feat(REQ-014): add portfolio-create-user-required requirement and implementation

- Add docs/srs/Interface/req-014_portfolio-create-user-required.md with acceptance criteria
- Create docs/batches/bat-004_20260502_portfolio-create-user-required/ delivery batch
- Enhance app/cli/commands/portfolio.py: validate user_id parameter before portfolio creation
- Add comprehensive CLI tests in tests/test_cli.py for portfolio create validation flows
- Update docs/batches/index.md and docs/srs/index.md with new requirement/batch references
- Update README.md documentation
This commit is contained in:
2026-05-02 13:45:46 +00:00
parent 2f686f79ce
commit a111fdd83f
10 changed files with 368 additions and 16 deletions
@@ -0,0 +1,59 @@
# BAT-004 Implementation
- Status: completed
- Batch: BAT-004
- Profile: batch-lite
## Plan Summary
- CLI `pfhub portfolio create`에 `--user <username>` 필수 옵션 추가
- BAT-003에서 도입한 `_with_service_and_repo` 헬퍼 재사용
- `repository.get_user_by_username(user)` 호출 → None이면 ValueError → exit code 1
- `service.create_portfolio(payload, owner_user_id=owner.id)` 실제 user UUID 전달
- README 예제 라인 갱신
- 기존 테스트 3개 수정 + 신규 3개 추가
## Changed Files
| File | Change |
|------|--------|
| `app/cli/commands/portfolio.py` | `create_portfolio` 함수에 `--user` 옵션 추가, `_with_service` → `_with_service_and_repo` 교체, username→UUID 조회 후 `owner_user_id` 전달 |
| `tests/test_cli.py` | `test_cli_create_buy_and_delete_flow`, `test_cli_write_command_prompts_before_create`, `test_cli_write_command_can_abort` 수정; AC 1~3 커버 신규 3개 케이스 추가 |
| `README.md` | `pfhub portfolio create` → `pfhub portfolio create ... --user <username>` 갱신 |
## Execution Log
### 구현 결정 사항
- `--user` 옵션: `typer.Option(None, "--user", ...)` + 수동 None 체크로 exit code 1 보장 (BAT-003과 동일 패턴)
- `_confirm` 호출 전에 None 체크를 배치하여 owner 미지정 시 확인 프롬프트 없이 즉시 종료
- callback 내 username → user_id 조회: `repository.get_user_by_username(user)` → None이면 `ValueError`
- `service.create_portfolio(payload, owner_user_id=owner.id)`: `portfolio_service.py` 시그니처 변경 불필요 (기존 키워드 인자 그대로 활용)
### 기존 테스트 수정
- `test_cli_create_buy_and_delete_flow`: alice user 생성 + `--user alice` 추가, `owner_user_id` 검증 BUY 전에 배치 (delete 후 `_portfolio`가 None이 되므로)
- `test_cli_write_command_prompts_before_create`: alice user 생성 + `--user alice` 추가
- `test_cli_write_command_can_abort`: alice user 생성 + `--user alice` 추가
## Validation
```
pytest tests/test_cli.py -k "test_cli_portfolio_create or test_cli_create or test_cli_write_command" -v
```
```
6 passed
```
```
pytest tests/test_cli.py -v
```
```
44 passed in 1.37s (전체 회귀, 기존 테스트 전원 통과)
```
## Remaining Risks
- 없음
@@ -0,0 +1,42 @@
# BAT-004 Index
- Status: release-candidate
- Batch: BAT-004
- Profile: batch-lite
- Created: 2026-05-02
## Included REQ
| REQ | 제목 | Type | Priority |
|-----|------|------|----------|
| REQ-014 | CLI `portfolio create --user` 필수화 및 소유자 설정 | Interface | Medium |
## Source Discovery
- minor-change fast path (Discovery 없음)
## Scope Summary
- CLI `pfhub portfolio create` 명령에 `--user <username>` 필수 옵션 추가
- `_with_service_and_repo` 헬퍼(REQ-013 도입) 재사용, `repository.get_user_by_username(user)` 호출
- `PortfolioService.create_portfolio(owner_user_id=owner.id, ...)` 실제 UUID 전달
- `--user` 미입력 시 exit code 1, 미존재 username 시 exit code 1
- README 예제 갱신
- DB 스키마 변경 없음, HTTP API 변경 없음
## Impacted Area
- Docs:
- `README.md` — `pfhub portfolio create` 예제 라인 `--user <username>` 추가
- Modules:
- `app/cli/commands/portfolio.py` — `create_portfolio` 명령 수정
- Tests:
- `tests/test_cli.py` — 기존 `test_cli_create_buy_and_delete_flow` 수정 + AC 1~4 커버 케이스 추가
## Stage Documents
| Stage | File | Status |
|-------|------|--------|
| Planning | [planning.md](./planning.md) | confirmed |
| Implementation | [implementation.md](./implementation.md) | completed |
| Verification | [verification.md](./verification.md) | verified |
@@ -0,0 +1,50 @@
# BAT-004 Planning
- Status: confirmed
- Batch: BAT-004
- Profile: batch-lite
## Included REQ
- REQ-014: CLI `portfolio create --user` 필수화 및 소유자 설정
## Out of Scope
- HTTP API `POST /portfolios` owner 설정 변경
- `owner_user_id` 기본값(`"system"`) 서비스 레이어 변경
## 구현 순서
### 1단계: CLI 명령 수정
- `app/cli/commands/portfolio.py` — `create_portfolio` 명령 수정
- `user: str | None = typer.Option(None, "--user", help="Owner username (required).")` 추가
- `_with_service_and_repo` 콜백으로 교체
- callback 내부:
- `user is None` → `console.print(...)` + `raise typer.Exit(code=1)`
- `repository.get_user_by_username(user)` → `None`이면 `raise ValueError(...)`
- `service.create_portfolio(payload, owner_user_id=owner.id)` 호출
### 2단계: README 수정
- `README.md` — `pfhub portfolio create` 예제 라인에 `--user alice` 추가
### 3단계: 테스트
- `tests/test_cli.py`
- `test_cli_create_buy_and_delete_flow`: `--user alice` 추가, repo에 alice 생성
- `test_cli_write_command_prompts_before_create`: 동일 수정
- `test_cli_write_command_can_abort`: 동일 수정
- `test_cli_portfolio_create_without_user_exits_error` (신규)
- `test_cli_portfolio_create_with_valid_user_sets_owner` (신규)
- `test_cli_portfolio_create_with_unknown_user_exits_error` (신규)
## Dependencies
- Internal: `_with_service_and_repo` (BAT-003 도입, 이미 존재)
- External: 없음
## Design Gate
- Design Required Before Implementation: no
- Reason: 기존 `_with_service_and_repo` 패턴 재사용. 구조/스키마 변경 없음.
@@ -0,0 +1,65 @@
# BAT-004 Verification
- Status: verified
- Batch: BAT-004
- Profile: batch-lite
## Architecture Impact Check
- Design Gate (from planning.md): no — DB 스키마 변경 없음, HTTP API 변경 없음
- Actual Architecture Impact: none confirmed — `app/cli/commands/portfolio.py` 내 기존 `_with_service_and_repo` 헬퍼 재사용, Protocol stub/구현체 변경 없음
- Baseline Document Sync Required: no
## Acceptance Mapping
### AC 1: `--user` 없이 실행 → exit code 1 + 오류 메시지
| 항목 | 내용 |
|------|------|
| Evidence | `test_cli_portfolio_create_without_user_exits_error` |
| Verification | `runner.invoke(app, ["portfolio", "create", "005930:tv", "--symbol", "005930", "--force"])` → `exit_code == 1`, `"--user" in result.stdout` |
| Result | ✅ PASSED |
### AC 2: `--user alice` → 생성된 포트폴리오의 `owner_user_id`가 alice UUID로 설정됨
| 항목 | 내용 |
|------|------|
| Evidence | `test_cli_portfolio_create_with_valid_user_sets_owner` |
| Verification | alice user 생성 → `--user alice --force` 실행 → `repo._portfolio.owner_user_id == user.id` 확인 |
| Result | ✅ PASSED |
### AC 3: 존재하지 않는 username → 오류 메시지 + exit code 1
| 항목 | 내용 |
|------|------|
| Evidence | `test_cli_portfolio_create_with_unknown_user_exits_error` |
| Verification | `--user nobody` → `exit_code == 1`, `"nobody" in result.stdout` |
| Result | ✅ PASSED |
### AC 4: in-memory 모드에서도 동일 동작
| 항목 | 내용 |
|------|------|
| Evidence | `test_cli_portfolio_create_with_valid_user_sets_owner` (FakeRepo 사용) |
| Verification | FakeRepo(in-memory) 환경에서 alice UUID가 `owner_user_id`로 설정됨 확인 |
| Result | ✅ PASSED |
### AC 5: 오류 형식이 기존 CLI와 일관성 유지
| 항목 | 내용 |
|------|------|
| Evidence | 코드 검토 — BAT-003 `list_portfolios` 구현과 동일한 패턴 사용 |
| Verification | `--user` 미입력 → `ctx.obj.console.print(...)` + `raise typer.Exit(code=1)`, 미등록 user → `ValueError` → `execute_command` 핸들러 동일 경로 |
| Result | ✅ 충족 |
## Test Execution
```
pytest tests/test_cli.py -v
44 passed in 1.37s
```
## Regression
- BAT-003 추가 테스트(portfolio list 관련 4개) 전원 PASSED — 회귀 없음
- 기존 `test_cli_create_buy_and_delete_flow`, `test_cli_write_command_prompts_before_create`, `test_cli_write_command_can_abort` 수정 후 전원 PASSED
+2 -1
View File
@@ -10,4 +10,5 @@
| --- | --- | --- | --- | --- | --- |
| BAT-001 | standard | release-candidate | REQ-004, REQ-005, REQ-006, REQ-007, REQ-008, REQ-009, REQ-010 | dcy-002 | [bat-001_20260502_trex-basic-v1-api-contract](./bat-001_20260502_trex-basic-v1-api-contract/) |
| BAT-002 | batch-lite | release-candidate | REQ-012 | minor-change | [bat-002_20260502_service-token-rotate-cli](./bat-002_20260502_service-token-rotate-cli/) |
| BAT-003 | batch-lite | release-candidate | REQ-013 | dcy-003 | [bat-003_20260502_portfolio-list-user-required](./bat-003_20260502_portfolio-list-user-required/) |
| BAT-003 | batch-lite | release-candidate | REQ-013 | dcy-003 | [bat-003_20260502_portfolio-list-user-required](./bat-003_20260502_portfolio-list-user-required/) |
| BAT-004 | batch-lite | release-candidate | REQ-014 | minor-change | [bat-004_20260502_portfolio-create-user-required](./bat-004_20260502_portfolio-create-user-required/) |