Files
document-haness/scripts/tests/test_pipeline_run.py
T

149 lines
6.2 KiB
Python

"""런 원장 검사기. 절차를 안 지킨 원장을 실제로 잡는지 본다.
이 검사기의 값은 「통과시키는 것」이 아니라 「안 지킨 것을 잡는 것」이라, 시험도 전부
위반을 넣어 걸리는지 보는 모양이다.
"""
import json
import os
import subprocess
import sys
import tempfile
import unittest
ROOT = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
SCRIPT = os.path.join(ROOT, "scripts", "verify-pipeline-run.py")
LEDGER = os.path.join(ROOT, "runs", "keycloak", "2026-09-07-2215", "run.json")
def run(path, *args):
p = subprocess.run([sys.executable, SCRIPT, path, *args],
capture_output=True, text=True, cwd=ROOT)
return p.returncode, p.stdout + p.stderr
@unittest.skipUnless(os.path.exists(LEDGER), "실증 런 원장이 없다")
class LedgerRules(unittest.TestCase):
"""실제로 돈 원장을 밑감으로 삼아 한 군데씩 어긴다."""
def setUp(self):
self.base = json.load(open(LEDGER, encoding="utf-8"))
self.dir = tempfile.TemporaryDirectory()
self.addCleanup(self.dir.cleanup)
def write(self, mutate):
run_ = json.loads(json.dumps(self.base))
mutate({s["id"]: s for s in run_["stages"]}, run_)
path = os.path.join(self.dir.name, "run.json")
json.dump(run_, open(path, "w", encoding="utf-8"), ensure_ascii=False)
return path
def test_실증한_원장은_통과한다(self):
code, out = run(LEDGER)
self.assertEqual(code, 0, out)
self.assertIn("PASS", out)
def test_영수증이_그_스킬의_문장이_아니면_잡는다(self):
path = self.write(lambda st, _: st["S5"].update(
skillEcho="이 문장은 그 스킬 어디에도 없다 정말로 없다 한 글자도"))
code, out = run(path)
self.assertEqual(code, 1)
self.assertIn("스킬 영수증이 그 스킬의 문장이 아니다", out)
def test_영수증이_비면_잡는다(self):
path = self.write(lambda st, _: st["S6"].update(skillEcho=""))
code, out = run(path)
self.assertEqual(code, 1)
self.assertIn("스킬 영수증이 없다", out)
def test_관문이_실패하면_잡는다(self):
def m(st, _):
st["S3"]["gates"][0]["exit"] = 1
path = self.write(m)
code, out = run(path)
self.assertEqual(code, 1)
self.assertIn("관문이 통과하지 못했다", out)
def test_관문이_빠지면_잡는다(self):
path = self.write(lambda st, _: st["S6"].update(gates=[]))
code, out = run(path)
self.assertEqual(code, 1)
self.assertIn("관문이 빠졌다", out)
def test_건너뛸_수_없는_단계를_건너뛰면_잡는다(self):
path = self.write(lambda st, _: st["S3"].update(
status="SKIPPED", skipReason="그냥"))
code, out = run(path)
self.assertEqual(code, 1)
self.assertIn("건너뛸 수 없는 단계를 건너뛰었다", out)
def test_건너뛴_사유가_없으면_잡는다(self):
path = self.write(lambda st, _: st["S4"].update(skipReason=""))
code, out = run(path)
self.assertEqual(code, 1)
self.assertIn("건너뛴 사유가 없다", out)
def test_없는_산출물을_적으면_잡는다(self):
path = self.write(lambda st, _: st["S1"].update(
outputs=["docs/keycloak/없는파일.md"]))
code, out = run(path)
self.assertEqual(code, 1)
self.assertIn("적어 낸 산출물이 디스크에 없다", out)
def test_다른_스킬을_쓰면_잡는다(self):
path = self.write(lambda st, _: st["S7"].update(skill="technical-visualizer"))
code, out = run(path)
self.assertEqual(code, 1)
self.assertIn("단계가 다른 스킬을 썼다", out)
def test_끝나지_않은_단계를_잡는다(self):
path = self.write(lambda st, _: st["S5"].update(status="RUNNING"))
code, out = run(path)
self.assertEqual(code, 1)
self.assertIn("끝나지 않은 단계가 있다", out)
def test_단계가_통째로_빠지면_잡는다(self):
def m(_, run_):
run_["stages"] = [s for s in run_["stages"] if s["id"] != "S6"]
path = self.write(m)
code, out = run(path)
self.assertEqual(code, 1)
self.assertIn("단계가 원장에 없다", out)
def test_곁증명의_영수증도_대조한다(self):
"""건너뛴 단계라도 곁증명을 냈으면 같은 잣대로 본다."""
proof = os.path.join(ROOT, "runs/keycloak/2026-09-07-2215/stage/S4/stage-report.json")
if not os.path.exists(proof):
self.skipTest("곁증명이 없다")
data = json.load(open(proof, encoding="utf-8"))
data["skillEcho"] = "그 스킬에 없는 문장을 곁증명에 적었다 정말로 없다"
bad = os.path.join(self.dir.name, "stage-report.json")
json.dump(data, open(bad, "w", encoding="utf-8"), ensure_ascii=False)
rel = os.path.relpath(bad, ROOT)
path = self.write(lambda st, _: st["S4"].update(outputs=[rel]))
code, out = run(path)
self.assertEqual(code, 1)
self.assertIn("곁증명의 스킬 영수증이 그 스킬의 문장이 아니다", out)
class Init(unittest.TestCase):
def test_틀에서_런을_연다(self):
with tempfile.TemporaryDirectory() as d:
path = os.path.join(d, "run.json")
p = subprocess.run(
[sys.executable, SCRIPT, "--init", path,
"--project", "keycloak", "--record", "docs/keycloak/final/document.md"],
capture_output=True, text=True, cwd=ROOT)
self.assertEqual(p.returncode, 0, p.stdout + p.stderr)
run_ = json.load(open(path, encoding="utf-8"))
self.assertEqual([s["id"] for s in run_["stages"]],
["S1", "S2", "S3", "S4", "S5", "S6", "S7"])
self.assertEqual(run_["project"], "keycloak")
# 갓 연 원장은 아직 아무 단계도 안 끝났으므로 통과하면 안 된다
code, out = run(path)
self.assertEqual(code, 1)
self.assertIn("끝나지 않은 단계가 있다", out)
if __name__ == "__main__":
unittest.main()