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:
@@ -110,7 +110,7 @@ pfhub service-token issue permanent --user alice --no-expiry
|
||||
pfhub service-token revoke nightly --force
|
||||
pfhub portfolio list --user <username>
|
||||
pfhub portfolio status 005930:tv --recent 10
|
||||
pfhub portfolio create 005930:tv --symbol 005930 --cash-balance 10000000 --force
|
||||
pfhub portfolio create 005930:tv --symbol 005930 --user <username> --cash-balance 10000000 --force
|
||||
pfhub portfolio buy 005930:tv --quantity 10 --price 71000 --fee 5000 --force
|
||||
pfhub portfolio sell 005930:tv --quantity 5 --price 72000 --tax 3000 --force
|
||||
pfhub portfolio invest 005930:tv --type deposit --amount 50000 --force
|
||||
|
||||
@@ -119,6 +119,7 @@ def portfolio_status(
|
||||
def create_portfolio(
|
||||
ctx: typer.Context,
|
||||
portfolio_id: str = typer.Argument(..., help="Portfolio identifier."),
|
||||
user: str | None = typer.Option(None, "--user", help="Owner username (required)."),
|
||||
name: str | None = typer.Option(None, help="Asset display name."),
|
||||
symbol: str = typer.Option(..., help="Trading symbol."),
|
||||
broker: str = typer.Option("kis", help="Broker code."),
|
||||
@@ -131,10 +132,16 @@ def create_portfolio(
|
||||
strategy_params: str = typer.Option("{}", help="Strategy params as JSON object."),
|
||||
force: bool = typer.Option(False, "--force", help="Skip confirmation prompt."),
|
||||
) -> None:
|
||||
if user is None:
|
||||
ctx.obj.console.print("Error: --user 옵션은 필수입니다.")
|
||||
raise typer.Exit(code=1)
|
||||
_confirm(force, f"Create portfolio {portfolio_id} for symbol {symbol}?")
|
||||
detail = _with_service(
|
||||
ctx,
|
||||
lambda service, app_context: service.create_portfolio(
|
||||
|
||||
def _callback(service, repository, app_context):
|
||||
owner = repository.get_user_by_username(user)
|
||||
if owner is None:
|
||||
raise ValueError(f"사용자를 찾을 수 없습니다: {user}")
|
||||
return service.create_portfolio(
|
||||
CreatePortfolioRequest(
|
||||
portfolio_id=portfolio_id,
|
||||
name=name,
|
||||
@@ -147,9 +154,11 @@ def create_portfolio(
|
||||
profit_buffer=profit_buffer,
|
||||
strategy_name=strategy_name,
|
||||
strategy_params=_parse_strategy_params(strategy_params),
|
||||
)
|
||||
),
|
||||
owner_user_id=owner.id,
|
||||
)
|
||||
|
||||
detail = _with_service_and_repo(ctx, _callback)
|
||||
render_output(ctx.obj.console, ctx.obj.output_format, detail)
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -11,3 +11,4 @@
|
||||
| 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-004 | batch-lite | release-candidate | REQ-014 | minor-change | [bat-004_20260502_portfolio-create-user-required](./bat-004_20260502_portfolio-create-user-required/) |
|
||||
@@ -0,0 +1,78 @@
|
||||
# REQ-014: CLI `portfolio create --user` 필수화 및 소유자 설정
|
||||
|
||||
- Status: Implemented
|
||||
- Type: Interface
|
||||
- Priority: Medium
|
||||
- Owner: repository-maintainers
|
||||
|
||||
## Intent
|
||||
- CLI `pfhub portfolio create`는 현재 `--user` 옵션이 없어 `owner_user_id="system"` 고정값으로 포트폴리오가 생성된다.
|
||||
- 생성 시 `--user <username>` 을 필수로 받아 실제 소유자의 `owner_user_id`(UUID)로 포트폴리오가 생성되도록 강제한다.
|
||||
- REQ-013(`portfolio list --user` 필수화)과 대칭을 이루는 변경으로, 소유자 기반 포트폴리오 관리 정책을 CLI 전반에 일관되게 적용한다.
|
||||
|
||||
## Requirement
|
||||
- CLI `pfhub portfolio create` 명령은 `--user <username>` 옵션을 필수로 받아야 한다.
|
||||
- `--user` 없이 실행하면 오류 메시지를 출력하고 exit code 1로 종료해야 한다.
|
||||
- `--user`로 전달된 username을 `repository.get_user_by_username(username)`으로 조회하여 `owner_user_id`(UUID)를 얻고, 이를 `PortfolioService.create_portfolio(owner_user_id=...)` 호출에 전달해야 한다.
|
||||
- 존재하지 않는 username이 전달되면 기존 CLI 오류 출력 형식과 일관된 오류 메시지를 출력하고 비정상 종료해야 한다.
|
||||
|
||||
## Acceptance Criteria
|
||||
1. Given `--user` 옵션 없이 `pfhub portfolio create <id> --symbol X --force`를 실행할 때, When 명령이 실행되면, Then 오류 메시지가 출력되고 exit code 1로 종료된다.
|
||||
2. Given 유효한 username `alice`로 `pfhub portfolio create <id> --symbol X --user alice --force`를 실행할 때, When 명령이 실행되면, Then 생성된 포트폴리오의 `owner_user_id`가 alice의 UUID로 설정된다.
|
||||
3. Given 존재하지 않는 username으로 실행할 때, When 명령이 실행되면, Then "사용자를 찾을 수 없습니다" 형태의 오류 메시지가 출력되고 exit code 1로 종료된다.
|
||||
4. Given in-memory 모드에서 `--user alice`로 실행할 때, When 명령이 실행되면, Then DB 모드와 동일하게 alice의 UUID가 `owner_user_id`로 설정된다.
|
||||
5. Given 오류 발생 시, When 오류 메시지가 출력될 때, Then 메시지 형식은 기존 CLI 오류 출력 형식(Rich Console)과 일관성을 유지한다.
|
||||
|
||||
## Impacted Area
|
||||
- Docs:
|
||||
- `README.md` — `pfhub portfolio create` 예제 라인에 `--user <username>` 옵션 추가
|
||||
- Modules:
|
||||
- `app/cli/commands/portfolio.py` — `create_portfolio` 명령 시그니처에 `--user` 필수 옵션 추가, `_with_service_and_repo` 콜백 사용, `owner_user_id` 전달
|
||||
- Tests:
|
||||
- `tests/test_cli.py` — 기존 `test_cli_create_buy_and_delete_flow` 수정 + AC 1~4 커버 케이스 추가
|
||||
|
||||
## Notes
|
||||
- `PortfolioService.create_portfolio` 시그니처(`owner_user_id: str = "system"`)는 변경하지 않는다. CLI 호출부에서만 실제 UUID를 전달한다.
|
||||
- REQ-013에서 도입한 `_with_service_and_repo` 헬퍼를 그대로 사용한다. 추가 헬퍼 불필요.
|
||||
- minor-change fast path 적용: Discovery 생략, Approved 직행. 범위는 CLI 옵션 1개 추가이며 기존 서비스/repository 인터페이스 변경 없음.
|
||||
- Source Discovery: 없음 (minor-change fast path)
|
||||
|
||||
## Change Log
|
||||
|
||||
### CHG-20260502-01
|
||||
- Date: 2026-05-02
|
||||
- Author: repository-maintainers
|
||||
|
||||
#### Change Summary
|
||||
- REQ-014 최초 생성 (Approved 직행). minor-change fast path 적용. REQ-012, REQ-013 선례 동일 경로.
|
||||
|
||||
#### Intent
|
||||
- `pfhub portfolio create`의 소유자 설정 누락 문제를 CLI 레벨에서 해결.
|
||||
|
||||
### CHG-20260502-02
|
||||
- Date: 2026-05-02
|
||||
- Author: repository-maintainers
|
||||
|
||||
#### Change Summary
|
||||
- BAT-004 verification(44 passed) 기반으로 Approved → Implemented 전환.
|
||||
|
||||
#### Intent
|
||||
- 구현 및 검증 증거 확보 완료 후 상태 동기화.
|
||||
|
||||
#### Acceptance Criteria Delta
|
||||
- Added: AC 1~5 신규 작성
|
||||
|
||||
#### Impacted Area Delta
|
||||
- Docs: README.md create 예제 갱신
|
||||
- Modules: app/cli/commands/portfolio.py create_portfolio 수정
|
||||
- Tests: tests/test_cli.py 케이스 추가/수정
|
||||
|
||||
#### Delivery Trace Delta
|
||||
- Batches: none (batch 미생성)
|
||||
- Releases: none
|
||||
|
||||
#### Revalidation Impact
|
||||
- Reverification Needed: no
|
||||
- Existing Implementation Invalidated: no
|
||||
- Status Recommendation: keep-current
|
||||
- Follow-up Action: draft-batch 진행
|
||||
+4
-7
@@ -74,7 +74,7 @@
|
||||
|
||||
## Next Requirement ID
|
||||
|
||||
- Current: `REQ-014`
|
||||
- Current: `REQ-015`
|
||||
- Rule: `Requirement Register`에 있는 가장 큰 REQ 번호보다 정확히 1 커야 한다.
|
||||
|
||||
## Requirement Register
|
||||
@@ -94,15 +94,12 @@
|
||||
| REQ-011 | trex-basic 계약 반영을 위한 baseline 문서 갱신 | Documentation | Approved | Medium | repository-maintainers | [REQ-011](./Documentation/req-011_baseline-docs-update-for-trex-basic-contract.md) |
|
||||
| REQ-012 | service-token rotate CLI 명령 구현 | Interface | Approved | Medium | repository-maintainers | [REQ-012](./Interface/req-012_service-token-rotate-cli.md) |
|
||||
| REQ-013 | CLI `portfolio list --user` 필수화 및 소유자 기반 조회 | Interface | Implemented | Medium | repository-maintainers | [REQ-013](./Interface/req-013_portfolio-list-user-required.md) |
|
||||
| REQ-014 | CLI `portfolio create --user` 필수화 및 소유자 설정 | Interface | Implemented | Medium | repository-maintainers | [REQ-014](./Interface/req-014_portfolio-create-user-required.md) |
|
||||
|
||||
## Recent Change Log Summary
|
||||
|
||||
- 2026-05-02: REQ-014 Implemented 전환 — BAT-004 verification evidence 확인 (44 passed)
|
||||
- 2026-05-02: REQ-014 Approved 직행 (minor-change) — CLI `portfolio create --user` 필수화 및 소유자 설정
|
||||
- 2026-05-02: REQ-013 Implemented 전환 — BAT-003 verification evidence 확인 (41 passed)
|
||||
- 2026-05-02: REQ-013 Approved 전환 — CLI `portfolio list --user` 필수화 및 소유자 기반 조회 (source Discovery: dcy-003)
|
||||
- 2026-05-02: REQ-013 Proposed 생성 — CLI `portfolio list --user` 필수화 및 소유자 기반 조회 (source Discovery: dcy-003)
|
||||
- 2026-05-02: REQ-012 Approved 직행 (minor-change) — service-token rotate CLI 명령 구현
|
||||
- 2026-05-02: REQ-011 Approved 전환 — trex-basic 계약 반영을 위한 baseline 문서 갱신 (source Discovery: dcy-002)
|
||||
- 2026-05-02: REQ-010 Approved 전환 — credential_payload Fernet 암호화 및 startup validation (source Discovery: dcy-002)
|
||||
- 2026-05-02: REQ-009 Approved 전환 — v1 API DB 스키마 migration 추가 (source Discovery: dcy-002)
|
||||
- 2026-05-02: REQ-008 Approved 전환 — POST /v1/assets/{asset_id}/ledger/trades 구현 (source Discovery: dcy-002)
|
||||
- 2026-05-02: REQ-007 Approved 전환 — GET /v1/user_info 엔드포인트 구현 (source Discovery: dcy-002)
|
||||
@@ -485,6 +485,7 @@ def test_cli_status_supports_json_output() -> None:
|
||||
|
||||
def test_cli_create_buy_and_delete_flow() -> None:
|
||||
repo = FakeRepo()
|
||||
user = repo.create_user("alice", permission="read")
|
||||
runner = build_runner(repo)
|
||||
|
||||
create_result = runner.invoke(
|
||||
@@ -495,6 +496,8 @@ def test_cli_create_buy_and_delete_flow() -> None:
|
||||
"005930:tv",
|
||||
"--symbol",
|
||||
"005930",
|
||||
"--user",
|
||||
"alice",
|
||||
"--cash-balance",
|
||||
"1000000",
|
||||
"--strategy-params",
|
||||
@@ -518,6 +521,7 @@ def test_cli_create_buy_and_delete_flow() -> None:
|
||||
],
|
||||
)
|
||||
assert repo.entries[0].action == "BUY"
|
||||
assert repo._portfolio.owner_user_id == user.id
|
||||
delete_result = runner.invoke(runner.app, ["portfolio", "delete", "005930:tv", "--keep-ledger", "--force"])
|
||||
|
||||
assert create_result.exit_code == 0
|
||||
@@ -528,6 +532,7 @@ def test_cli_create_buy_and_delete_flow() -> None:
|
||||
|
||||
def test_cli_write_command_prompts_before_create() -> None:
|
||||
repo = FakeRepo()
|
||||
repo.create_user("alice", permission="read")
|
||||
runner = build_runner(repo)
|
||||
|
||||
result = runner.invoke(
|
||||
@@ -538,6 +543,8 @@ def test_cli_write_command_prompts_before_create() -> None:
|
||||
"005930:tv",
|
||||
"--symbol",
|
||||
"005930",
|
||||
"--user",
|
||||
"alice",
|
||||
"--cash-balance",
|
||||
"1000000",
|
||||
],
|
||||
@@ -550,6 +557,7 @@ def test_cli_write_command_prompts_before_create() -> None:
|
||||
|
||||
def test_cli_write_command_can_abort() -> None:
|
||||
repo = FakeRepo()
|
||||
repo.create_user("alice", permission="read")
|
||||
runner = build_runner(repo)
|
||||
|
||||
result = runner.invoke(
|
||||
@@ -560,6 +568,8 @@ def test_cli_write_command_can_abort() -> None:
|
||||
"005930:tv",
|
||||
"--symbol",
|
||||
"005930",
|
||||
"--user",
|
||||
"alice",
|
||||
"--cash-balance",
|
||||
"1000000",
|
||||
],
|
||||
@@ -570,6 +580,47 @@ def test_cli_write_command_can_abort() -> None:
|
||||
assert repo._portfolio is None
|
||||
|
||||
|
||||
def test_cli_portfolio_create_without_user_exits_error() -> None:
|
||||
repo = FakeRepo()
|
||||
runner = build_runner(repo)
|
||||
|
||||
result = runner.invoke(
|
||||
runner.app,
|
||||
["portfolio", "create", "005930:tv", "--symbol", "005930", "--force"],
|
||||
)
|
||||
|
||||
assert result.exit_code == 1
|
||||
assert "--user" in result.stdout
|
||||
|
||||
|
||||
def test_cli_portfolio_create_with_valid_user_sets_owner() -> None:
|
||||
repo = FakeRepo()
|
||||
user = repo.create_user("alice", permission="read")
|
||||
runner = build_runner(repo)
|
||||
|
||||
result = runner.invoke(
|
||||
runner.app,
|
||||
["portfolio", "create", "005930:tv", "--symbol", "005930", "--user", "alice", "--force"],
|
||||
)
|
||||
|
||||
assert result.exit_code == 0
|
||||
assert repo._portfolio is not None
|
||||
assert repo._portfolio.owner_user_id == user.id
|
||||
|
||||
|
||||
def test_cli_portfolio_create_with_unknown_user_exits_error() -> None:
|
||||
repo = FakeRepo()
|
||||
runner = build_runner(repo)
|
||||
|
||||
result = runner.invoke(
|
||||
runner.app,
|
||||
["portfolio", "create", "005930:tv", "--symbol", "005930", "--user", "nobody", "--force"],
|
||||
)
|
||||
|
||||
assert result.exit_code == 1
|
||||
assert "nobody" in result.stdout
|
||||
|
||||
|
||||
def test_cli_health_check_reports_repository() -> None:
|
||||
repo = FakeRepo(_portfolio())
|
||||
runner = build_runner(repo)
|
||||
|
||||
Reference in New Issue
Block a user