zioinfo-mail/workspace/guardia-itsm/test_c2_change.py
DESKTOP-TKLFCPR\ython cfe2901a55 refactor(structure): consolidate all projects under workspace/
- itsm/    -> workspace/guardia-itsm/
- manager/ -> workspace/guardia-manager/
- app/     -> workspace/guardia-messenger/
- manual/  -> workspace/guardia-docs/

workspace/zioinfo-web/ unchanged.
git mv preserves full commit history.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 23:50:56 +09:00

188 lines
7.0 KiB
Python

"""C-2 변경 관리 CAB 테스트"""
import sys, ast, os
os.environ.setdefault("GUARDIA_SECRET_KEY", "test-c2-secret-key-32bytes-padded!")
os.environ.setdefault("DATABASE_URL", "sqlite+aiosqlite:///./test_c2.db")
ok = True
print("=== 1. 구문 검사 ===")
files = ["routers/change.py", "models.py", "main.py"]
for f in files:
try:
with open(f, encoding="utf-8") as fh:
src = fh.read()
ast.parse(src)
print(f" OK {f}")
except SyntaxError as e:
print(f" ERR {f}: {e}")
ok = False
print("\n=== 2. models.py CAB 모델 확인 ===")
with open("models.py", encoding="utf-8") as f:
models_src = f.read()
model_checks = [
("class RFChange(Base):", "RFChange ORM 클래스 (RFC)"),
("class CABVote(Base):", "CABVote ORM 클래스"),
("class FreezeWindow(Base):", "FreezeWindow ORM 클래스"),
("class RFCStatus(str, Enum):", "RFCStatus Enum"),
("class ChangeType(str, Enum):", "ChangeType Enum"),
("class ChangeRisk(str, Enum):", "ChangeRisk Enum"),
("class CABVoteResult(str, Enum):", "CABVoteResult Enum"),
("RFChangeOut", "RFChangeOut Pydantic"),
("RFChangeCreate", "RFChangeCreate Pydantic"),
("CABVoteCreate", "CABVoteCreate Pydantic"),
("FreezeWindowOut", "FreezeWindowOut Pydantic"),
("FreezeWindowCreate", "FreezeWindowCreate Pydantic"),
("tb_rfc", "tb_rfc 테이블명"),
("tb_cab_vote", "tb_cab_vote 테이블명"),
("tb_freeze_window", "tb_freeze_window 테이블명"),
("rollback_plan", "롤백 계획 컬럼"),
("freeze_exempt", "동결 기간 예외 컬럼"),
("ci_ids_json", "영향받는 CI 목록 컬럼"),
("EMERGENCY", "긴급 변경 타입"),
("APPROVE", "CAB 승인 투표"),
]
for sym, desc in model_checks:
status = "OK" if sym in models_src else "ERR"
if status == "ERR":
ok = False
print(f" {status} {desc}")
print("\n=== 3. routers/change.py 엔드포인트 확인 ===")
with open("routers/change.py", encoding="utf-8") as f:
router_src = f.read()
endpoint_checks = [
('@router.post("/rfc"', "POST /api/change/rfc"),
('@router.get("/rfc"', "GET /api/change/rfc"),
('@router.get("/rfc/{rfc_id}"', "GET /api/change/rfc/{id}"),
('@router.patch("/rfc/{rfc_id}"', "PATCH RFC"),
('@router.post("/rfc/{rfc_id}/submit"', "제출 (DRAFT→SUBMITTED)"),
('@router.post("/rfc/{rfc_id}/vote"', "CAB 투표"),
('@router.post("/rfc/{rfc_id}/decide"', "최종 결정"),
('@router.post("/rfc/{rfc_id}/schedule"', "일정 확정"),
('@router.post("/rfc/{rfc_id}/start"', "변경 시작"),
('@router.post("/rfc/{rfc_id}/complete"', "변경 완료"),
('@router.post("/rfc/{rfc_id}/fail"', "변경 실패"),
('@router.get("/rfc/{rfc_id}/votes"', "CAB 투표 현황"),
('@router.post("/freeze"', "동결 기간 등록"),
('@router.get("/freeze"', "동결 기간 목록"),
('@router.delete("/freeze/{freeze_id}"', "동결 기간 삭제"),
('@router.get("/freeze/check"', "동결 기간 확인"),
('@router.get("/calendar"', "변경 일정 캘린더"),
('@router.get("/stats"', "변경 통계"),
("_next_rfc_id", "RFC ID 생성 함수"),
("_check_freeze", "동결 기간 충돌 검사"),
("_count_votes", "투표 집계 함수"),
]
for sym, desc in endpoint_checks:
status = "OK" if sym in router_src else "ERR"
if status == "ERR":
ok = False
print(f" {status} {desc}")
print("\n=== 4. 상태 전환 흐름 검증 ===")
# RFC 상태 흐름: DRAFT→SUBMITTED→IN_REVIEW→(APPROVED|REJECTED)→SCHEDULED→IN_PROGRESS→(COMPLETED|FAILED)
state_flow = {
"DRAFT": "초안",
"SUBMITTED": "CAB 검토 제출",
"IN_REVIEW": "검토 중",
"APPROVED": "승인",
"REJECTED": "거부",
"SCHEDULED": "일정 확정",
"IN_PROGRESS": "진행 중",
"COMPLETED": "완료",
"FAILED": "실패",
"WITHDRAWN": "철회",
}
try:
import importlib.util
spec = importlib.util.spec_from_file_location("models_mod", "models.py")
models_mod = importlib.util.module_from_spec(spec)
spec.loader.exec_module(models_mod)
rfc_statuses = set(e.value for e in models_mod.RFCStatus)
for st, desc in state_flow.items():
status = "OK" if st in rfc_statuses else "ERR"
if status == "ERR":
ok = False
print(f" {status} RFCStatus.{st}: {desc}")
except Exception as e:
print(f" ERR Enum 로드 오류: {type(e).__name__}: {e}")
ok = False
print("\n=== 5. ChangeType / Risk / Vote Enum 검증 ===")
try:
change_types = set(e.value for e in models_mod.ChangeType)
for t in ["STANDARD", "NORMAL", "EMERGENCY", "MAJOR"]:
status = "OK" if t in change_types else "ERR"
if status == "ERR":
ok = False
print(f" {status} ChangeType.{t}")
risk_levels = set(e.value for e in models_mod.ChangeRisk)
for r in ["LOW", "MEDIUM", "HIGH", "CRITICAL"]:
status = "OK" if r in risk_levels else "ERR"
if status == "ERR":
ok = False
print(f" {status} ChangeRisk.{r}")
vote_results = set(e.value for e in models_mod.CABVoteResult)
for v in ["APPROVE", "REJECT", "ABSTAIN", "DEFER"]:
status = "OK" if v in vote_results else "ERR"
if status == "ERR":
ok = False
print(f" {status} CABVoteResult.{v}")
except Exception as e:
print(f" ERR {type(e).__name__}: {e}")
ok = False
print("\n=== 6. 변경 관리 비즈니스 규칙 검증 ===")
# 비즈니스 규칙이 코드에 구현되어 있는지 확인
rules = [
("rollback_plan", "롤백 계획 필수 체크 (submit 시)"),
("change_plan", "변경 계획 필수 체크 (submit 시)"),
("freeze_exempt", "동결 기간 예외 처리"),
("_check_freeze", "동결 기간 충돌 검사 (schedule 시)"),
("is_final", "최종 결정권자 투표"),
("approval_rate", "승인율 계산"),
("success_rate", "변경 성공률 계산"),
("UserRole.ADMIN", "ADMIN 권한 검사 (decide)"),
("UserRole.PM", "PM 권한 검사 (decide)"),
]
for sym, desc in rules:
status = "OK" if sym in router_src else "ERR"
if status == "ERR":
ok = False
print(f" {status} {desc}")
print("\n=== 7. RFC ID 형식 검증 ===")
from datetime import datetime
today = datetime.utcnow().strftime("%Y%m%d")
rfc_example = f"RFC-{today}-0001"
# RFC-YYYYMMDD-NNNN: 4+8+1+4 = 17자
try:
assert rfc_example.startswith("RFC-"), "RFC ID 형식 오류"
assert len(rfc_example) == 17, f"RFC ID 길이 오류: {len(rfc_example)}"
print(f" OK RFC ID 형식: {rfc_example} ({len(rfc_example)}자)")
except AssertionError as e:
print(f" ERR {e}")
ok = False
print("\n=== 8. main.py 등록 확인 ===")
for sym, desc in [("change", "change 임포트"), ("change.router", "change 라우터 등록")]:
status = "OK" if sym in open("main.py", encoding="utf-8").read() else "ERR"
if status == "ERR":
ok = False
print(f" {status} {desc}")
print("\n=== C-2 변경 관리 CAB 테스트 완료 ===")
if ok:
print("모든 검사 통과")
else:
sys.exit(1)