"""C-3 Problem Management / C-4 용량 관리 / C-5 서비스 카탈로그 통합 테스트""" import sys, ast, os os.environ.setdefault("GUARDIA_SECRET_KEY", "test-c345-secret-key-32bytes-padded!") os.environ.setdefault("DATABASE_URL", "sqlite+aiosqlite:///./test_c345.db") ok = True print("=== 1. 구문 검사 ===") files = [ "routers/problem.py", "routers/capacity.py", "routers/catalog.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. C-3 Problem Management 확인 ===") with open("models.py", encoding="utf-8") as f: models_src = f.read() with open("routers/problem.py", encoding="utf-8") as f: prob_src = f.read() prob_checks = [ (models_src, "class ProblemRecord(Base):", "ProblemRecord ORM"), (models_src, "class ProblemNote(Base):", "ProblemNote ORM"), (models_src, "class ProblemStatus(str, Enum):", "ProblemStatus Enum"), (models_src, "INVESTIGATING", "INVESTIGATING 상태"), (models_src, "RCA_DONE", "RCA_DONE 상태"), (models_src, "WORKAROUND", "WORKAROUND 상태"), (models_src, "known_error", "known_error 컬럼"), (models_src, "root_cause", "root_cause 컬럼"), (models_src, "tb_problem", "tb_problem 테이블"), (prob_src, '@router.post("/"', "POST 문제 생성"), (prob_src, '@router.get("/known-errors"', "Known Error DB"), (prob_src, '@router.post("/{prb_id}/rca"', "RCA 기록"), (prob_src, '@router.post("/{prb_id}/workaround"', "임시 해결"), (prob_src, '@router.post("/{prb_id}/resolve"', "해결 처리"), (prob_src, '@router.post("/{prb_id}/close"', "종결 처리"), (prob_src, '@router.post("/{prb_id}/notes"', "활동 노트"), (prob_src, "PRB-", "Problem ID 형식 (PRB-)"), ] for src, sym, desc in prob_checks: status = "OK" if sym in src else "ERR" if status == "ERR": ok = False print(f" {status} {desc}") print("\n=== 3. C-4 용량 관리 확인 ===") with open("routers/capacity.py", encoding="utf-8") as f: cap_src = f.read() cap_checks = [ (models_src, "class CapacityPlan(Base):", "CapacityPlan ORM"), (models_src, "class CapacityStatus(str, Enum):", "CapacityStatus Enum"), (models_src, "forecast_3m", "3개월 예측 컬럼"), (models_src, "forecast_6m", "6개월 예측 컬럼"), (models_src, "forecast_12m", "12개월 예측 컬럼"), (models_src, "expansion_needed_at", "확장 필요 시점 컬럼"), (models_src, "growth_rate", "월 성장률 컬럼"), (models_src, "tb_capacity_plan", "tb_capacity_plan 테이블"), (cap_src, '@router.get("/dashboard"', "대시보드"), (cap_src, '@router.post("/plans"', "용량 계획 등록"), (cap_src, '@router.get("/plans"', "용량 계획 목록"), (cap_src, '@router.post("/plans/{plan_id}/recalculate"', "재계산"), (cap_src, '@router.get("/alerts"', "경보 목록"), (cap_src, '@router.get("/trends/{source}"', "트렌드"), (cap_src, "_calc_forecasts", "예측 계산 함수"), (cap_src, "_calc_status", "상태 계산 함수"), (cap_src, "OVERLOAD", "OVERLOAD 상태"), ] for src, sym, desc in cap_checks: status = "OK" if sym in src else "ERR" if status == "ERR": ok = False print(f" {status} {desc}") print("\n=== 4. C-5 서비스 카탈로그 확인 ===") with open("routers/catalog.py", encoding="utf-8") as f: cat_src = f.read() cat_checks = [ (models_src, "class ServiceItem(Base):", "ServiceItem ORM"), (models_src, "class ServiceStatus(str, Enum):", "ServiceStatus Enum"), (models_src, "sla_response_h", "응답 SLA 컬럼"), (models_src, "sla_resolve_h", "해결 SLA 컬럼"), (models_src, "sla_availability", "가용성 SLA 컬럼"), (models_src, "approval_required", "승인 필요 컬럼"), (models_src, "request_count", "요청 카운트 컬럼"), (models_src, "tb_service_catalog", "tb_service_catalog 테이블"), (cat_src, '@router.get("/"', "카탈로그 목록"), (cat_src, '@router.post("/"', "카탈로그 등록"), (cat_src, '@router.get("/{service_id}"', "서비스 상세"), (cat_src, '@router.post("/{service_id}/request"', "서비스 요청 (SR 생성)"), (cat_src, '@router.get("/categories"', "카테고리 목록"), (cat_src, '@router.get("/stats"', "통계"), (cat_src, "SVC-", "서비스 ID 형식 (SVC-)"), (cat_src, "PENDING_APPROVAL", "승인 필요 SR 상태"), ] for src, sym, desc in cat_checks: status = "OK" if sym in src else "ERR" if status == "ERR": ok = False print(f" {status} {desc}") print("\n=== 5. main.py 등록 확인 ===") with open("main.py", encoding="utf-8") as f: main_src = f.read() for sym, desc in [ ("problem", "C-3 problem 라우터"), ("problem.router", "problem 라우터 등록"), ("capacity", "C-4 capacity 라우터"), ("capacity.router", "capacity 라우터 등록"), ("catalog", "C-5 catalog 라우터"), ("catalog.router", "catalog 라우터 등록"), ]: status = "OK" if sym in main_src else "ERR" if status == "ERR": ok = False print(f" {status} {desc}") print("\n=== 6. _calc_forecasts 수학 검증 ===") try: import importlib.util, math spec = importlib.util.spec_from_file_location("cap_mod", "routers/capacity.py") cap_mod = importlib.util.module_from_spec(spec) spec.loader.exec_module(cap_mod) # 월 10% 성장률로 현재값 100 f3, f6, f12, _ = cap_mod._calc_forecasts(100.0, 10.0, 200.0) expected_f3 = round(100.0 * 1.1**3, 2) # 133.1 expected_f12 = round(100.0 * 1.1**12, 2) # 313.84 assert abs(f3 - expected_f3) < 0.1, f"3개월 예측 오류: {f3} != {expected_f3}" assert abs(f12 - expected_f12) < 0.5, f"12개월 예측 오류: {f12} != {expected_f12}" print(f" OK forecast(100, 10%, 3M) = {f3} (기대: {expected_f3})") print(f" OK forecast(100, 10%, 12M) = {f12} (기대: {expected_f12})") # 성장률 0이면 None 반환 f3_0, _, _, _ = cap_mod._calc_forecasts(100.0, 0.0, 200.0) assert f3_0 is None, f"성장률 0에서 None 반환해야 함: {f3_0}" print(f" OK 성장률 0 → None 반환") except AssertionError as e: print(f" ERR {e}") ok = False except Exception as e: print(f" ERR _calc_forecasts 오류: {type(e).__name__}: {e}") ok = False print("\n=== 7. _calc_status 임계값 검증 ===") try: # OVERLOAD: current >= crit * 1.1 status = cap_mod._calc_status(100.0, 75.0, 90.0) assert status == "OVERLOAD", f"OVERLOAD 판정 실패: {status}" print(f" OK 100% (crit=90%) → {status}") status = cap_mod._calc_status(92.0, 75.0, 90.0) assert status == "CRITICAL", f"CRITICAL 판정 실패: {status}" print(f" OK 92% (crit=90%) → {status}") status = cap_mod._calc_status(80.0, 75.0, 90.0) assert status == "WARNING", f"WARNING 판정 실패: {status}" print(f" OK 80% (warn=75%) → {status}") status = cap_mod._calc_status(50.0, 75.0, 90.0) assert status == "NORMAL", f"NORMAL 판정 실패: {status}" print(f" OK 50% → {status}") except AssertionError as e: print(f" ERR {e}") ok = False except Exception as e: print(f" ERR _calc_status 오류: {type(e).__name__}: {e}") print("\n=== 8. Problem / Capacity / Service ID 형식 검증 ===") from datetime import datetime today = datetime.utcnow().strftime("%Y%m%d") ids = { f"PRB-{today}-0001": 17, # PRB-YYYYMMDD-NNNN = 4+8+1+4 = 17 f"SVC-0001": 8, # SVC-NNNN = 4+4 = 8 } for id_val, expected_len in ids.items(): status = "OK" if len(id_val) == expected_len else "WARN" print(f" {status} {id_val} ({len(id_val)}자, 기대:{expected_len}자)") print("\n=== C-3/C-4/C-5 통합 테스트 완료 ===") if ok: print("모든 검사 통과") else: sys.exit(1)