# BAT-002 Verification - Status: verified - Batch: BAT-002 - Profile: batch-lite - Verified: 2026-05-02 ## Included REQ - REQ-012: service-token rotate CLI 명령 구현 ## Architecture Impact - Design Gate (from planning.md): Design Required Before Implementation — **no** - Reason: DB 스키마 변경 없음. 기존 패턴(issue/revoke) 동일하게 적용. 결정 사항 없음. - Confirmed: 구조/인터페이스/런타임 흐름 변경 없음 — **no architecture impact confirmed** - Baseline Docs Update Required: no ## Acceptance Mapping ### REQ-012 AC-1 > Given `expires_at IS NULL`인 유효한 service-token 이름, When `pfhub service-token rotate --force`를 실행, Then DB 행의 `token_hash`, `token_prefix`, `runtime_id`가 새 값으로 변경되고 `id`, `permission`, `expires_at`(NULL)은 기존과 동일하다. - Evidence: `test_cli_service_token_rotate_no_expiry_succeeds` - `rotated.token_prefix != "oldpref"` 검증 - `rotated.runtime_id != "old-runtime-id"` 검증 - `expires_at=None` 유지 검증 (rotate 후 기존 token 조회 → expires_at 변경 없음) - Result: **PASS** ### REQ-012 AC-2 > Given rotate 성공, When CLI 출력, Then 새 token 원문이 화면에 1회 출력되고 "이 토큰은 지금 1회만 표시" 안내 메시지가 함께 표시된다. - Evidence: `test_cli_service_token_rotate_no_expiry_succeeds` - `result.stdout`에 `"token:"` 포함 확인 - CLI 출력 패턴: `⚠ 갱신된 토큰 원문은 지금 1회만 표시됩니다.` + `token: ` - Result: **PASS** ### REQ-012 AC-3 > Given 존재하지 않는 token 이름, When `rotate`, Then 오류 메시지를 출력하고 종료 코드 1을 반환한다. - Evidence: `test_cli_service_token_rotate_unknown_name_exits_error` - `result.exit_code == 1` 검증 - `"nonexistent" in result.stdout` 검증 - Result: **PASS** ### REQ-012 AC-4 > Given revoked 상태의 token, When `rotate`, Then 오류 메시지를 출력하고 종료 코드 1을 반환한다. - Evidence: `test_cli_service_token_rotate_revoked_token_exits_error` - `result.exit_code == 1` 검증 - `"old-token" in result.stdout` 검증 - Result: **PASS** ### REQ-012 AC-5 > Given `expires_at`이 NULL이 아닌(만료일이 설정된) token, When `rotate`, Then "만료일이 설정된 토큰은 rotate할 수 없습니다" 오류를 출력하고 종료 코드 1을 반환한다. - Evidence: `test_cli_service_token_rotate_with_expiry_exits_error` - `result.exit_code == 1` 검증 - `"expiring" in result.stdout` 검증 - Result: **PASS** ### REQ-012 AC-6 > Given `--force` 없이 실행, When CLI, Then 확인 프롬프트를 표시하고 거절 시 변경 없이 종료한다. - Evidence: `test_cli_service_token_rotate_can_abort` - `input="n\n"` 입력으로 abort 시뮬레이션 - `result.exit_code == 0` 검증 - `token.token_prefix == "pref"` (변경 없음) 검증 - Result: **PASS** ## Test Summary ``` tests/test_cli.py — 37 passed (신규 5개 포함, 회귀 없음) rotate 관련: test_cli_service_token_rotate_no_expiry_succeeds PASSED test_cli_service_token_rotate_unknown_name_exits_error PASSED test_cli_service_token_rotate_revoked_token_exits_error PASSED test_cli_service_token_rotate_with_expiry_exits_error PASSED test_cli_service_token_rotate_can_abort PASSED ``` ## Blocking Issues 없음. ## Ready for Release - true