Merge pull request #35 from fourblocks2020/feat/dcy-036-kis-credential-inject
feat(dcy-036/bat-054): KISBroker 양의 경로 — 데몬-스코프 KIS 인증정보 실거래 주입
This commit is contained in:
@@ -0,0 +1,108 @@
|
||||
# BAT-054: Design
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-054
|
||||
|
||||
## Architecture Summary
|
||||
|
||||
그날-시작 체크포인트에서 fetch된 `BrokerCredentialPayload`를 실제 `KISBroker` 인스턴스에 주입하는 경로를 완성한다.
|
||||
|
||||
설계 핵심 두 가지:
|
||||
1. `KISBroker.reinitialize(credential)` 메서드: `app_key/app_secret/account_ref`를 원자적으로 교체, `base_url/timeout/allow_live/mock`은 유지. L1 token 캐시 무효화 포함.
|
||||
2. `SnapshotScheduler`에 `kis_reinit_callback: Optional[Callable[[BrokerCredentialPayload], None]] = None` 추가. 그날-시작 성공 시 callback 호출 → KISBroker.reinitialize 트리거.
|
||||
|
||||
## Changed Areas
|
||||
|
||||
- Docs: docs/batches/bat-054_.../ (이번 batch 문서)
|
||||
- Modules:
|
||||
- `trex_core/adapters/broker/kis.py` — `reinitialize()` 메서드 추가
|
||||
- `trex_core/application/snapshot_scheduler.py` — `kis_reinit_callback` 인자 추가 + 성공 시 호출
|
||||
- `trex_core/runtime/daemon.py` — `_build_kis_reinit_callback()` + SnapshotScheduler 조립 수정
|
||||
- Interfaces: 없음 (AMQP wire 계약 변경 없음)
|
||||
|
||||
## Key Decisions
|
||||
|
||||
### D1: KISBroker.reinitialize() — 인스턴스 변수 직접 교체
|
||||
|
||||
**결정**: `KISBroker`에 `reinitialize(credential: BrokerCredentialPayload) -> None` 메서드를 추가한다.
|
||||
메서드 내에서:
|
||||
- `self._app_key`, `self._app_secret`, `self._account_ref`를 새 값으로 교체
|
||||
- L1 token 캐시(`self._token = None`, `self._token_exp = datetime.min`) 무효화
|
||||
- L2 token cache(Redis, `token_cache.invalidate`가 있으면 호출; 없으면 skip — fallback graceful)
|
||||
- `base_url/timeout/mock/allow_live`는 **불변** (env에서 온 인프라 설정)
|
||||
|
||||
**근거**:
|
||||
- `KISBroker`는 이미 실행 중인 여러 스레드(execution loop, safety-sell)가 공유하는 인스턴스다. 새 객체로 교체하면 기존 참조가 stale해진다.
|
||||
- 인스턴스 변수 교체는 CPython GIL 하에서 atomic하며, 진행 중인 HTTP 요청이 완료된 후 다음 _ensure_token() 호출부터 새 자격증명을 사용한다. 그날-시작(07:30 KST)은 장 시작 전이므로 경쟁 조건 위험이 낮다.
|
||||
- threading.Lock은 추가하지 않는다(reinitialize가 그날-시작 1회만 호출되고 장 시작 전이라 안전). 만약 향후 장중 재초기화가 필요해지면 그때 lock을 추가한다(REQ-115 설계: 그날 고정 캐시로 장중 재fetch 없음).
|
||||
|
||||
### D2: SnapshotScheduler.kis_reinit_callback — Optional callable 주입
|
||||
|
||||
**결정**: `SnapshotScheduler.__init__`에 `kis_reinit_callback: Optional[Callable[[BrokerCredentialPayload], None]] = None` 추가.
|
||||
`_maybe_fetch_credential()` 성공 후, 캐시 저장 + INFO 로그 이후에 callback을 호출한다.
|
||||
|
||||
```python
|
||||
# 성공 경로
|
||||
self._cached_credential = ctx.broker
|
||||
self._credential_date = today
|
||||
self._entry_allowed = True
|
||||
_log.info("DAY_START credential loaded: account_ref=%s ...", ...)
|
||||
if self._kis_reinit_callback is not None:
|
||||
try:
|
||||
self._kis_reinit_callback(ctx.broker)
|
||||
_log.info("DAY_START KIS broker reinitialized with fetched credential (instance=%s)", ...)
|
||||
except Exception as exc:
|
||||
_log.error("DAY_START KIS broker reinitialize failed: %s (instance=%s) — entry remains allowed", ...)
|
||||
# 재초기화 실패는 entry_allowed를 False로 전환하지 않는다(기존 env 인증정보로 계속 운행)
|
||||
```
|
||||
|
||||
**근거**:
|
||||
- callback 실패가 entry_allowed를 뒤집지 않는다: 재초기화 실패 시 기존 env 인증정보로 운행 계속 (fail-safe: 이미 기동 중인 브로커가 있으므로).
|
||||
- 새 인증정보 주입에 성공하면 INFO 로그로 가시화.
|
||||
- `credential_fetch_enabled=False`(기본)일 때 callback이 None이므로 코드 경로 완전 미진입 → 기존 동작 byte-for-byte 보존.
|
||||
|
||||
### D3: daemon.py — callback 조립
|
||||
|
||||
**결정**: `daemon.py`에 private helper `_build_kis_reinit_callback(config, broker)` 추가:
|
||||
```python
|
||||
def _build_kis_reinit_callback(config, broker):
|
||||
if config.broker != "kis" or not config.credential_fetch_enabled:
|
||||
return None
|
||||
if not hasattr(broker, "reinitialize"):
|
||||
return None
|
||||
return broker.reinitialize
|
||||
```
|
||||
이를 `SnapshotScheduler` 생성 시 `kis_reinit_callback=_build_kis_reinit_callback(config, self.broker)` 로 전달.
|
||||
|
||||
**근거**:
|
||||
- `broker == 'paper'`이면 callback=None → 기존 경로 보존.
|
||||
- `credential_fetch_enabled=False`이면 callback=None.
|
||||
- `KISBroker`가 아닌 브로커가 `reinitialize`를 가지지 않을 경우 graceful skip.
|
||||
|
||||
### D4: access token cache (Redis, REQ-016) 무효화
|
||||
|
||||
**결정**: `reinitialize()` 내에서:
|
||||
1. L1 in-memory token 무효화: `self._token = None`, `self._token_exp = datetime.min(UTC)`.
|
||||
2. L2 Redis: `token_cache`에 `invalidate(account_ref, app_key)` 메서드가 있으면 호출, 없으면 skip. `RedisTokenCache`는 기존 key scheme이 `(account_ref, app_key)` 기반이므로 기존 key가 자연스럽게 miss로 처리된다(새 account_ref/app_key로 조회 시 miss → 새 토큰 발급). 명시적 invalidate API는 추가하지 않는다.
|
||||
|
||||
**근거**: 새 `account_ref/app_key`로 L2 조회 시 miss → 새 토큰 발급. 구 key는 TTL로 자연 만료. 인증정보 변경이 1일 1회이므로 수동 무효화 복잡성 불필요.
|
||||
|
||||
### D5: REQ-113 AC-4 보강 — break-glass 로그 assert
|
||||
|
||||
**결정**: `tests/application/test_snapshot_scheduler.py`에 break-glass 활성화 시 로그에 "break-glass" 키워드가 포함됨을 `caplog`로 검증하는 케이스 추가.
|
||||
|
||||
## Edge Cases
|
||||
|
||||
- `credential_fetch_enabled=True`이고 `broker='paper'`인 경우: callback=None이므로 재초기화 없음. entry_allowed는 fetch 결과에 따라 설정됨(논리적으로 paper broker에 fetch가 의미 없으나 테스트 가능).
|
||||
- `reinitialize()` 호출 중 in-flight HTTP 요청: CPython GIL 하에서 변수 할당은 atomic, 다음 `_ensure_token()` 호출부터 새 값 사용. 경쟁 조건 없음(그날-시작 = 장 전).
|
||||
- `credential_fetch_enabled=True`이고 break-glass: callback 호출 없음(재초기화 없이 env 인증정보 사용) — D2 기술.
|
||||
|
||||
## Architecture Impact
|
||||
|
||||
- runtime-flows: KISBroker 재초기화 경로 추가 (경미한 갱신)
|
||||
- project-structure: 변경 없음
|
||||
|
||||
## Reference Doc Update Plan
|
||||
|
||||
- project-structure.md: none (구조 변경 없음)
|
||||
- runtime-flows.md: DAY_START 흐름에 KISBroker reinitialize 스텝 추가 설명이 유용하나 이번 batch 필수 아님 — verification에서 판단
|
||||
@@ -0,0 +1,79 @@
|
||||
# BAT-054: Implementation
|
||||
|
||||
- Status: completed
|
||||
- Batch: BAT-054
|
||||
|
||||
## Plan Summary
|
||||
|
||||
BAT-053에서 스캐폴드로 처리된 양의 경로를 완성:
|
||||
1. `KISBroker.reinitialize(credential)` 메서드 추가 — app_key/app_secret/account_ref 교체 + L1 token 무효화
|
||||
2. `SnapshotScheduler`에 `kis_reinit_callback` 주입 지점 추가 — 그날-시작 성공 시 callback 호출
|
||||
3. `daemon.py`에 `_build_kis_reinit_callback()` 헬퍼 + SnapshotScheduler 조립 수정
|
||||
4. 신규 AC 테스트 12개 추가 (REQ-113 AC-2/AC-4 보강, REQ-115 AC-1)
|
||||
|
||||
## Changed Files
|
||||
|
||||
| 파일 | 변경 유형 | 관련 REQ |
|
||||
| --- | --- | --- |
|
||||
| `trex_core/adapters/broker/kis.py` | 확장 (reinitialize 메서드 추가) | REQ-113 AC-2, REQ-115 AC-1 |
|
||||
| `trex_core/application/snapshot_scheduler.py` | 확장 (kis_reinit_callback 인자 + 성공 경로 callback 호출) | REQ-113 AC-2, REQ-115 AC-1 |
|
||||
| `trex_core/runtime/daemon.py` | 확장 (_build_kis_reinit_callback 헬퍼 + SnapshotScheduler 조립) | REQ-113 AC-2, REQ-115 AC-1 |
|
||||
| `tests/application/test_snapshot_scheduler.py` | 확장 (BAT-054 테스트 12개 추가) | REQ-113 AC-2/AC-4, REQ-115 AC-1 |
|
||||
|
||||
## Execution Log
|
||||
|
||||
1. `KISBroker.reinitialize()` 구현:
|
||||
- `self._account_ref/app_key/app_secret` 교체
|
||||
- `self._token = None`, `self._token_exp = datetime.min(UTC)` L1 무효화
|
||||
- `base_url/timeout/mock/allow_live` 불변 (env-sourced 인프라 설정)
|
||||
- docstring에 L2 Redis token cache 처리 방침(새 key miss → 자연 재발급) 기술
|
||||
|
||||
2. `SnapshotScheduler.__init__` 확장:
|
||||
- `kis_reinit_callback: Optional[Callable[[BrokerCredentialPayload], None]] = None` 추가
|
||||
- `self._kis_reinit_callback` 저장
|
||||
|
||||
3. `SnapshotScheduler._maybe_fetch_credential()` 성공 경로 확장:
|
||||
- 기존 INFO 로그 뒤에 callback 호출 블록 추가
|
||||
- callback 실패 시 ERROR 로그 + entry_allowed 유지(fail-safe)
|
||||
- break-glass 경로: callback 호출 없음 (env 인증정보 사용)
|
||||
|
||||
4. `daemon.py` 확장:
|
||||
- `_build_kis_reinit_callback(config, broker)` 헬퍼 추가
|
||||
- `config.broker != 'kis'` → None
|
||||
- `not config.credential_fetch_enabled` → None
|
||||
- `broker.reinitialize` 없음 → None
|
||||
- 조건 충족 시 `broker.reinitialize` callable 반환
|
||||
- `SnapshotScheduler` 생성 시 `kis_reinit_callback=_build_kis_reinit_callback(config, self.broker)` 전달
|
||||
|
||||
5. 테스트 추가 (12개):
|
||||
- `test_kis_reinit_callback_called_on_success` — AC-2: fetch 성공 시 callback 호출
|
||||
- `test_kis_reinit_callback_not_called_when_fail_closed` — AC-2: fail-closed 시 callback 미호출
|
||||
- `test_kis_reinit_callback_not_called_when_disabled` — AC-1 회귀: opt-in off 시 callback 미호출
|
||||
- `test_kis_reinit_callback_failure_does_not_flip_entry_allowed` — 설계 안전망: reinit 실패 시 entry_allowed 유지
|
||||
- `test_kis_reinit_callback_cached_for_day_not_called_twice` — REQ-115 AC-3: 그날 두 번째 run_once에서 재호출 없음
|
||||
- `test_kis_reinit_callback_none_when_break_glass` — AC-3+AC-2: break-glass 시 callback 없음
|
||||
- `test_break_glass_log_contains_break_glass_keyword` — REQ-113 AC-4 보강: break-glass 로그 content
|
||||
- `test_kis_broker_reinitialize_updates_credentials` — REQ-115 AC-1: 인증정보 교체 + L1 무효화
|
||||
- `test_kis_broker_reinitialize_preserves_base_url_and_flags` — REQ-115 AC-1: 인프라 설정 불변
|
||||
- `test_build_kis_reinit_callback_returns_none_for_paper` — AC-2: paper broker → None
|
||||
- `test_build_kis_reinit_callback_returns_none_when_disabled` — AC-1: fetch disabled → None
|
||||
- `test_build_kis_reinit_callback_returns_callable_for_kis` — AC-2/AC-1: kis+enabled → callable
|
||||
|
||||
## Validation
|
||||
|
||||
```
|
||||
cd /home/genichin/workspace/trex-lead/repos/trex-core/.claude/worktrees/agent-a0d6ff73d43f2e96b
|
||||
python3 -m pytest tests/application/test_snapshot_scheduler.py -v
|
||||
```
|
||||
결과: **29 passed** (BAT-053 기존 17 + BAT-054 신규 12)
|
||||
|
||||
```
|
||||
python3 -m pytest tests/ -q
|
||||
```
|
||||
결과: **757 passed, 47 skipped** (기존 베이스라인 745 대비 +12, 회귀 0)
|
||||
|
||||
## Remaining Risks
|
||||
|
||||
- R1 (Low): 실 portfolio-hub 없이 stub 기반으로만 검증. 실 `/v1/user_info` 응답 스키마 호환은 portfolio-hub dcy-010 배포 후 통합 테스트에서 확인 필요.
|
||||
- R2 (Low): KISBroker.reinitialize()는 그날-시작(장 전) 1회 호출 설계이므로 in-flight HTTP 경쟁 조건 위험이 낮으나, 장중 reinitialize가 필요한 시나리오가 생기면 threading.Lock 추가 검토 필요.
|
||||
- R3 (Low): L2 Redis token cache(REQ-016) 명시적 무효화 없음 — 새 (account_ref, app_key) key miss로 자연 재발급 처리. 동일 account_ref/app_key인데 app_secret만 바뀐 경우 구 토큰이 TTL 만료 전까지 유효할 수 있음. 운영상 계좌 인증정보 전체가 함께 교체되는 구조라 실용적으로 문제 없음.
|
||||
@@ -0,0 +1,35 @@
|
||||
# BAT-054: 그날-시작 KIS 인증정보 — 양의 경로 주입 (KISBroker 재조립)
|
||||
|
||||
- Status: in-progress
|
||||
- Profile: standard
|
||||
- Discovery: dcy-036
|
||||
- Created: 2026-06-28
|
||||
|
||||
## Included REQ
|
||||
|
||||
- REQ-113: KISConfig 소스 우선순위 — portfolio-hub fetch 1차 + env break-glass + opt-in (AC-2, AC-4, AC-9 미구현 부분)
|
||||
- REQ-115: 그날-시작 장중 무중단 보장 — 그날 고정 인증정보 캐시 + portfolio-hub 장애 비중단 (AC-1 미구현 부분)
|
||||
|
||||
## Excluded REQ
|
||||
|
||||
- REQ-112: Implemented (BAT-053)
|
||||
- REQ-114: Implemented (BAT-053)
|
||||
- REQ-113 AC-1/3/5/6/7: Implemented (BAT-053)
|
||||
- REQ-115 AC-2/3/4/5/6: Implemented (BAT-053)
|
||||
|
||||
## Documents
|
||||
|
||||
- [planning.md](./planning.md)
|
||||
- [design.md](./design.md)
|
||||
- [implementation.md](./implementation.md)
|
||||
- [verification.md](./verification.md)
|
||||
|
||||
## Notes
|
||||
|
||||
- BAT-053 (PR #34 머지, commit 01655ab)에서 스텁/스캐폴드로 처리된 **양의 경로 미구현 부분**을 이번 batch에서 완성한다.
|
||||
- 핵심 잔여 작업:
|
||||
- REQ-113 AC-2: fetch 성공 시 `BrokerCredentialPayload`로 `KISConfig` 재조립 → `KISBroker` 재초기화
|
||||
- REQ-115 AC-1: 장중 portfolio-hub 장애 후에도 캐시된 인증정보로 KIS 주문 처리 지속
|
||||
- REQ-113 AC-4 보강: break-glass 로그 content assert (BAT-053 Notes: "AC-4 break-glass 로그 내용 assert 보강 후속")
|
||||
- AMQP wire 계약 변경 없음 — trex-orb-v1/trex-tsl-v1 파급 없음.
|
||||
- 실거래(real money) 주입 경로 — blast radius 크다. 팀장 검수 + QC + 사장님 머지 필요.
|
||||
@@ -0,0 +1,57 @@
|
||||
# BAT-054: Planning
|
||||
|
||||
- Status: confirmed
|
||||
- Batch: BAT-054
|
||||
- Profile: standard
|
||||
|
||||
## Included REQ
|
||||
|
||||
- REQ-113 (Approved, 미충족 AC: AC-2 fetch→KISConfig 재조립, AC-4 break-glass 로그 assert)
|
||||
- REQ-115 (Approved, 미충족 AC: AC-1 캐시 인증정보로 KIS 주문 지속)
|
||||
|
||||
## Scope Summary
|
||||
|
||||
BAT-053에서 스캐폴드로 처리된 "양의 경로(positive path)"를 완성한다.
|
||||
- 그날-시작 체크포인트에서 fetch 성공 시 `BrokerCredentialPayload`로 `KISConfig`의 `app_key/app_secret/account_ref`를 재조립하고 이를 실제 `KISBroker` 인스턴스에 주입한다.
|
||||
- 재조립 후 `KISBroker`는 그날 캐시된 인증정보로 장중 KIS API 호출(주문·체결 조회·토큰 갱신)을 처리한다.
|
||||
- portfolio-hub 장중 장애는 이미 재조립된 KISBroker 운행에 영향을 주지 않는다.
|
||||
- break-glass 로그 내용 assert 테스트를 추가한다(AC-4 보강).
|
||||
|
||||
## Task Breakdown
|
||||
|
||||
### T1: REQ-113 AC-2 — KISBroker 재조립 callback 설계 + 구현
|
||||
|
||||
- `SnapshotScheduler`에 `kis_reinit_callback: Optional[Callable[[BrokerCredentialPayload], None]] = None` 추가
|
||||
- 그날-시작 성공 시 `_maybe_fetch_credential()`에서 callback 호출 → KISBroker 재초기화
|
||||
- `daemon.py`의 `_build_broker()`에서 KIS 브로커면 callback을 조립하여 주입
|
||||
|
||||
### T2: REQ-115 AC-1 — KISBroker 런타임 교체 메서드
|
||||
|
||||
- `KISBroker`(또는 daemon.py)에 `reinitialize(credential: BrokerCredentialPayload)` 같은 메서드 제공
|
||||
- 재초기화는 `app_key/app_secret/account_ref`만 교체; `base_url/timeout/allow_live`는 env 원본 유지
|
||||
- KIS access token 캐시(Redis, REQ-016)는 `app_key` 변경 시 무효화 필요
|
||||
|
||||
### T3: REQ-113 AC-4 보강 — break-glass 로그 assert 테스트
|
||||
|
||||
- `tests/runtime/test_config.py` 또는 `tests/application/test_snapshot_scheduler.py`에 AC-4 로그 content 검증 케이스 추가
|
||||
|
||||
## Design Gate
|
||||
|
||||
- Design Required Before Implementation: yes
|
||||
- Reason: KISBroker 재초기화 방법(메서드 추가 vs callback 교체 vs wrapper) + 재초기화 시 access token 캐시 무효화 정책이 구현 전에 명시되어야 한다. 기존 `KISBroker` 코드를 확인 후 결정.
|
||||
|
||||
## Dependencies
|
||||
|
||||
- Internal:
|
||||
- BAT-053 완료(REQ-112 `BrokerCredentialPayload`/`TradingContext` 구현, REQ-114 fail-closed 스캐폴드)
|
||||
- `adapters/broker/kis.py` (KISBroker) — reinitialize 진입점
|
||||
- `runtime/daemon.py` (`_build_broker`, SnapshotScheduler 조립)
|
||||
- External:
|
||||
- 없음 (portfolio-hub 실 연동은 스텁 기반 테스트로 커버)
|
||||
|
||||
## Risks
|
||||
|
||||
- R1 (High): KISBroker 재초기화 중 진행 중인 주문 경쟁 조건 — broker 레벨 lock 또는 재초기화 안전 구간 확인 필요
|
||||
- R2 (Medium): access token 캐시(Redis, REQ-016) 무효화 누락 → 구 account_ref 토큰이 신규 인증정보와 충돌
|
||||
- R3 (Medium): `daemon.py`에서 callback 조립 시 `DaemonConfig.broker == 'kis'` 여부 확인 — paper broker면 callback 불필요
|
||||
- R4 (Low): 테스트에서 실제 KIS API를 호출하지 않도록 mock/stub 격리 유지 — KISBroker.reinitialize()는 stub BrokerPort로 테스트
|
||||
@@ -0,0 +1,101 @@
|
||||
# BAT-054: Verification
|
||||
|
||||
- Status: draft
|
||||
- Batch: BAT-054
|
||||
|
||||
## Acceptance Mapping
|
||||
|
||||
### REQ-113 — KISConfig 소스 우선순위 (미충족 AC)
|
||||
|
||||
**AC-2**: Given TOML에 `credential_fetch_enabled = true`이고 portfolio-hub fetch가 유효한 `BrokerCredentialPayload`를 반환, When 데몬 기동(그날-시작), Then `KISConfig.app_key`, `KISConfig.app_secret`, `KISConfig.account_ref`가 fetch 결과로 구성된다.
|
||||
|
||||
- Evidence: `test_kis_reinit_callback_called_on_success` — fetch 성공 시 callback이 `BrokerCredentialPayload(account_ref="stub-account", app_key="stub-app-key", app_secret="stub-app-secret")`으로 호출됨 확인. PASSED.
|
||||
- Evidence: `test_kis_broker_reinitialize_updates_credentials` — `KISBroker.reinitialize(cred)` 호출 후 `broker._account_ref/app_key/app_secret`이 cred 값으로 교체됨 + L1 token 무효화 확인. PASSED.
|
||||
- Evidence: `test_build_kis_reinit_callback_returns_callable_for_kis` — broker=kis + fetch enabled 시 callback callable 반환 + 실제 broker.reinitialize 동작 확인. PASSED.
|
||||
- Note: 실 KISBroker를 KIS API에 연결한 통합 테스트는 portfolio-hub dcy-010 배포 후 가능. 현재 stub 기반으로 핵심 경로 검증.
|
||||
|
||||
**AC-4**: Given break-glass 활성화 상태, When 데몬 기동 로그 확인, Then break-glass 활성화 사실이 INFO로 기록된다.
|
||||
|
||||
- Evidence (보강): `test_break_glass_log_contains_break_glass_keyword` — `TREX_KIS_BROKER_BREAK_GLASS=true` 시 로그에 "break-glass" 키워드 포함 확인. PASSED.
|
||||
- Note: BAT-053에서 break-glass 경로는 구현되었으나 로그 content assert가 없었음. 이번 batch에서 추가.
|
||||
|
||||
**REQ-113 AC-1, AC-3, AC-5, AC-6, AC-7**: BAT-053 Implemented. 회귀 없음 (전체 스위트 757 passed, 회귀 0).
|
||||
|
||||
### REQ-115 — 그날-시작 장중 무중단 (미충족 AC)
|
||||
|
||||
**AC-1**: Given 그날-시작 로드 성공(`credential_fetch_enabled = true`) 후 portfolio-hub를 내려(장중 장애 주입), When BuyIntent 또는 SellIntent를 처리, Then KIS 브로커 어댑터가 캐시된 인증정보로 주문을 처리하고 `PortfolioHubUnavailableError`로 인해 주문이 차단되지 않는다.
|
||||
|
||||
- Evidence: `test_kis_reinit_callback_called_on_success` — 그날-시작에 callback(=broker.reinitialize)이 호출됨으로써 KISBroker가 fetched 인증정보로 업데이트됨. PASSED.
|
||||
- Evidence: `test_kis_broker_reinitialize_updates_credentials` — reinitialize 후 broker가 새 인증정보 보유, 이후 KIS API 호출은 broker 자체 인증정보로 수행 (portfolio-hub 불필요). PASSED.
|
||||
- Evidence: `test_kis_reinit_callback_failure_does_not_flip_entry_allowed` — reinit callback 실패 시에도 entry_allowed 유지 (기존 env 인증정보로 계속 운행). PASSED.
|
||||
- Note: 장중 portfolio-hub 다운 상태에서 실 KISBroker 주문 처리를 끝까지 검증하려면 통합 테스트 환경이 필요. 현재 stub 기반으로 핵심 속성(reinitialize 완료 후 broker가 portfolio-hub와 분리됨) 검증.
|
||||
|
||||
**AC-3**: Given 그날 로드 성공 후 portfolio-hub가 잠시 다운되었다가 복구, When 그날 중 재fetch 트리거 없음, Then 그날 인증정보는 재load 없이 최초 fetch 값 그대로 유지된다.
|
||||
|
||||
- Evidence: `test_kis_reinit_callback_cached_for_day_not_called_twice` — 동일 날 두 번째 run_once에서 callback 재호출 없음(캐시 hit). PASSED.
|
||||
- Evidence: `test_successful_credential_cached_for_day` (BAT-053) — fetch count = 1 유지. PASSED.
|
||||
|
||||
**REQ-115 AC-2, AC-4, AC-5, AC-6**: BAT-053 Implemented. 회귀 없음.
|
||||
|
||||
## Evidence
|
||||
|
||||
### Test Results
|
||||
|
||||
```
|
||||
pytest tests/ -q
|
||||
757 passed, 47 skipped in 1.59s
|
||||
```
|
||||
|
||||
기존 베이스라인: 745 passed, 47 skipped (BAT-053 완료 시점)
|
||||
이번 batch 신규 테스트: +12 (29 total in test_snapshot_scheduler.py)
|
||||
회귀: 0
|
||||
|
||||
### 신규 테스트 목록 (12개)
|
||||
|
||||
| 테스트명 | 검증 AC | 결과 |
|
||||
| --- | --- | --- |
|
||||
| `test_kis_reinit_callback_called_on_success` | REQ-113 AC-2, REQ-115 AC-1 | PASS |
|
||||
| `test_kis_reinit_callback_not_called_when_fail_closed` | REQ-113 AC-2 | PASS |
|
||||
| `test_kis_reinit_callback_not_called_when_disabled` | REQ-113 AC-1 회귀 | PASS |
|
||||
| `test_kis_reinit_callback_failure_does_not_flip_entry_allowed` | 설계 안전망 | PASS |
|
||||
| `test_kis_reinit_callback_cached_for_day_not_called_twice` | REQ-115 AC-3 | PASS |
|
||||
| `test_kis_reinit_callback_none_when_break_glass` | REQ-113 AC-3 + AC-2 | PASS |
|
||||
| `test_break_glass_log_contains_break_glass_keyword` | REQ-113 AC-4 보강 | PASS |
|
||||
| `test_kis_broker_reinitialize_updates_credentials` | REQ-115 AC-1, REQ-113 AC-2 | PASS |
|
||||
| `test_kis_broker_reinitialize_preserves_base_url_and_flags` | REQ-115 AC-1 | PASS |
|
||||
| `test_build_kis_reinit_callback_returns_none_for_paper` | REQ-113 AC-2 | PASS |
|
||||
| `test_build_kis_reinit_callback_returns_none_when_disabled` | REQ-113 AC-1 | PASS |
|
||||
| `test_build_kis_reinit_callback_returns_callable_for_kis` | REQ-113 AC-2, REQ-115 AC-1 | PASS |
|
||||
|
||||
### fail-closed 불변식 회귀 확인
|
||||
|
||||
BAT-053 구현 테스트 17개 모두 PASS:
|
||||
- `test_run_once_no_credential_fetch_when_disabled` — opt-in off 회귀
|
||||
- `test_run_once_portfolio_hub_unavailable_sets_fail_closed` — AC-1
|
||||
- `test_run_once_broker_null_sets_fail_closed` — AC-2
|
||||
- `test_run_once_trading_disabled_sets_fail_closed` — AC-3
|
||||
- `test_intent_service_buy_rejected_when_entry_not_allowed` — AC-4
|
||||
- `test_intent_service_sell_passes_when_entry_not_allowed` — AC-5
|
||||
- 기타 기존 12개 PASS
|
||||
|
||||
## Result
|
||||
|
||||
- Ready for Release: false
|
||||
- Blocking Issues:
|
||||
- 없음 (코드/테스트 레벨 구현 및 검증 완료)
|
||||
- Missing evidence (비차단):
|
||||
- REQ-115 AC-1 통합 검증: 실 portfolio-hub + 실 KIS mock(openapivts) 환경에서 그날-시작 fetch → KISBroker reinitialize → 장중 주문 처리까지의 end-to-end 검증은 portfolio-hub dcy-010 배포 후 가능. 현재 stub 기반 단위 검증으로 충분하다고 판단하나, 팀장 확인 필요.
|
||||
- REQ-113 AC-9 (break-glass 전용 경보): BAT-053 Notes에 기재된 "AC-9 전용 테스트 보강"은 REQ-113에 AC-9 항목이 없음을 확인 (AC는 1~7까지). BAT-053 Notes의 "AC-9"는 REQ-114 AC-9(그날 고정 상태)를 지칭하며, 이는 `test_entry_allowed_fixed_for_day_no_refetch`(PASS)로 커버됨. 별도 break-glass 경보 요구사항 없음 확인.
|
||||
|
||||
### 팀장 검수 요청 사항
|
||||
|
||||
1. REQ-115 AC-1 stub 기반 검증으로 `confirm-batch-verification` 진행 가능한지 판단 (portfolio-hub dcy-010 배포 전 단계에서의 기준)
|
||||
2. REQ-113 Status 갱신 권한: 이번 batch 완료로 AC-2/AC-4 충족 → Status `Approved` → `Implemented` 갱신 여부
|
||||
|
||||
### QC 체크리스트 (qc-reviewer용)
|
||||
|
||||
- [ ] BAT-054 신규 테스트 12개 독립 실행 PASS 확인
|
||||
- [ ] fail-closed 불변식 미파괴 확인 (opt-in=False 경로에서 gateway 비활성, `entry_allowed_fn=None`)
|
||||
- [ ] app_secret 평문 로그 미노출 확인 (기존 AC-7 테스트 PASS, reinitialize 로그 없음)
|
||||
- [ ] domain 순수성 불변 확인 (`trex_core/domain/` 변경 없음)
|
||||
- [ ] AMQP wire 계약 변경 없음 확인 (trex-orb-v1/trex-tsl-v1 영향 없음)
|
||||
@@ -60,4 +60,5 @@
|
||||
| BAT-050 | standard | release-candidate | REQ-102, REQ-103, REQ-104, REQ-107, REQ-108 | dcy-034 (core-A) | [bat-050](./bat-050_20260626_reconcile-warning-blocking-gate-core-a/) |
|
||||
| BAT-051 | standard | release-candidate | REQ-105, REQ-106 | dcy-034 (core-B) | [bat-051](./bat-051_20260626_sync-verify-cli-cold-start-core-b/) |
|
||||
| BAT-052 | standard | release-candidate | REQ-109, REQ-110, REQ-111 | dcy-035 | [bat-052](./bat-052_20260626_wac-realized-pnl-report/) |
|
||||
| BAT-053 | standard | in-progress | REQ-112, REQ-113, REQ-114, REQ-115 | dcy-036 | [bat-053](./bat-053_20260628_daystart-credential-fetch-failsafe/) |
|
||||
| BAT-053 | standard | release-candidate | REQ-112, REQ-113, REQ-114, REQ-115 | dcy-036 | [bat-053](./bat-053_20260628_daystart-credential-fetch-failsafe/) |
|
||||
| BAT-054 | standard | in-progress | REQ-113 (AC-2/AC-4 보강), REQ-115 (AC-1) | dcy-036 | [bat-054](./bat-054_20260628_daystart-credential-kis-inject/) |
|
||||
|
||||
Reference in New Issue
Block a user