- 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>
123 lines
4.7 KiB
Python
123 lines
4.7 KiB
Python
"""E-2/E-3 Analytics API 테스트"""
|
|
import sys, ast, os
|
|
|
|
os.environ.setdefault("GUARDIA_SECRET_KEY", "test-analytics-secret-32bytes!!")
|
|
os.environ.setdefault("DATABASE_URL", "sqlite+aiosqlite:///./test_analytics.db")
|
|
|
|
# ── 1. 구문 검사 ─────────────────────────────────────────────────────────────
|
|
print("=== 1. 구문 검사 ===")
|
|
files = ["routers/analytics.py", "main.py"]
|
|
ok = True
|
|
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
|
|
if not ok:
|
|
sys.exit(1)
|
|
|
|
# ── 2. 라우터 임포트 ─────────────────────────────────────────────────────────
|
|
print("\n=== 2. analytics 라우터 임포트 ===")
|
|
import importlib.util
|
|
spec = importlib.util.spec_from_file_location("analytics_mod", "routers/analytics.py")
|
|
analytics_mod = importlib.util.module_from_spec(spec)
|
|
try:
|
|
spec.loader.exec_module(analytics_mod)
|
|
router = analytics_mod.router
|
|
routes = {}
|
|
for r in router.routes:
|
|
if hasattr(r, "methods"):
|
|
routes[r.path] = list(r.methods)
|
|
|
|
expected = [
|
|
"/deploy/trend",
|
|
"/deploy/summary",
|
|
"/deploy/by-project",
|
|
"/engineer/workload",
|
|
"/engineer/overview",
|
|
"/sr/trend",
|
|
"/sr/resolution-time",
|
|
]
|
|
for path in expected:
|
|
found = any(path in r for r in routes.keys())
|
|
status = "OK" if found else "ERR"
|
|
if status == "ERR":
|
|
ok = False
|
|
print(f" {status} /api/analytics{path}")
|
|
print(f" INFO 전체 라우트: {list(routes.keys())}")
|
|
except Exception as e:
|
|
print(f" INFO 외부 의존성 에러 (정상): {type(e).__name__}: {str(e)[:80]}")
|
|
|
|
# ── 3. 집계 로직 단위 테스트 ─────────────────────────────────────────────────
|
|
print("\n=== 3. 집계 로직 단위 테스트 ===")
|
|
from datetime import date, datetime, timedelta
|
|
|
|
def date_range(days, offset=0):
|
|
end = date.today() - timedelta(days=offset)
|
|
start = end - timedelta(days=days - 1)
|
|
return start, end
|
|
|
|
start, end = date_range(30)
|
|
assert start < end, "date range invalid"
|
|
assert (end - start).days == 29, f"Expected 29 days gap, got {(end-start).days}"
|
|
print(f" OK date_range(30): {start} to {end}")
|
|
|
|
# 버킷 키 계산
|
|
from datetime import timezone
|
|
sample_dt = datetime(2026, 5, 15, 14, 30)
|
|
day_key = sample_dt.strftime("%Y-%m-%d")
|
|
week_key = (sample_dt - timedelta(days=sample_dt.weekday())).strftime("%Y-%m-%d")
|
|
month_key = sample_dt.strftime("%Y-%m")
|
|
assert day_key == "2026-05-15"
|
|
assert week_key == "2026-05-11" # 2026-05-15 is Friday, Monday is 2026-05-11
|
|
assert month_key == "2026-05"
|
|
print(f" OK bucket keys: day={day_key}, week={week_key}, month={month_key}")
|
|
|
|
# 성공률 계산
|
|
total, success = 12, 9
|
|
success_rate = round(success / total * 100, 1)
|
|
assert success_rate == 75.0, f"Expected 75.0, got {success_rate}"
|
|
print(f" OK success_rate: {success}/{total} = {success_rate}%")
|
|
|
|
# 해결 시간 통계
|
|
durations = [1.5, 2.0, 3.5, 4.0, 8.0, 10.0, 24.0, 72.0, 100.0, 5.0]
|
|
durations.sort()
|
|
n = len(durations)
|
|
avg = round(sum(durations) / n, 2)
|
|
p50 = round(durations[n // 2], 2)
|
|
p90 = round(durations[int(n * 0.9)], 2)
|
|
assert avg == 23.0, f"Expected 23.0, got {avg}"
|
|
print(f" OK resolution time stats: avg={avg}h, p50={p50}h, p90={p90}h")
|
|
|
|
# 분포 버킷
|
|
buckets_dist = {"0-4h": 0, "4-8h": 0, "8-24h": 0, "24-72h": 0, "72h+": 0}
|
|
for d in durations:
|
|
if d < 4: buckets_dist["0-4h"] += 1
|
|
elif d < 8: buckets_dist["4-8h"] += 1
|
|
elif d < 24: buckets_dist["8-24h"] += 1
|
|
elif d < 72: buckets_dist["24-72h"] += 1
|
|
else: buckets_dist["72h+"] += 1
|
|
assert buckets_dist["0-4h"] == 3, f"Expected 3, got {buckets_dist['0-4h']}"
|
|
assert buckets_dist["72h+"] == 2, f"Expected 2, got {buckets_dist['72h+']}"
|
|
print(f" OK distribution buckets: {buckets_dist}")
|
|
|
|
# ── 4. main.py에서 analytics 라우터 등록 확인 ───────────────────────────────
|
|
print("\n=== 4. main.py analytics 라우터 등록 확인 ===")
|
|
with open("main.py", encoding="utf-8") as f:
|
|
main_src = f.read()
|
|
if "analytics" in main_src and "analytics.router" in main_src:
|
|
print(" OK analytics 라우터 main.py에 등록됨")
|
|
else:
|
|
print(" ERR analytics 라우터 미등록")
|
|
ok = False
|
|
|
|
print("\n=== 테스트 완료: E-2/E-3 Analytics ===")
|
|
if ok:
|
|
print("모든 검사 통과")
|
|
else:
|
|
sys.exit(1)
|