110 lines
4.3 KiB
Python
110 lines
4.3 KiB
Python
from __future__ import annotations
|
|
|
|
from pathlib import Path
|
|
import sys
|
|
import tempfile
|
|
import unittest
|
|
|
|
|
|
RUNTIME = Path(__file__).resolve().parents[1] / "runtime"
|
|
sys.path.insert(0, str(RUNTIME))
|
|
import quality_gate # noqa: E402
|
|
|
|
|
|
class QualityGateTest(unittest.TestCase):
|
|
def test_read_only_pass_and_transport_token_failure(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
path = root / "notes/example.md"
|
|
path.parent.mkdir(parents=True)
|
|
path.write_text("# 예시\n", encoding="utf-8")
|
|
before = path.read_bytes()
|
|
result = quality_gate.run(
|
|
root,
|
|
[path],
|
|
include_graph=False,
|
|
require_moc_convergence=False,
|
|
)
|
|
self.assertEqual(result["status"], "PASS", result)
|
|
self.assertEqual(path.read_bytes(), before)
|
|
self.assertEqual(result["checked_paths"], result["touched_paths"])
|
|
self.assertIn("source-hygiene", {item["name"] for item in result["checks"]})
|
|
|
|
path.write_text("# 예시\n</content>\n", encoding="utf-8")
|
|
failed = quality_gate.run(
|
|
root,
|
|
[path],
|
|
include_graph=False,
|
|
require_moc_convergence=False,
|
|
)
|
|
self.assertEqual(failed["status"], "FAIL")
|
|
self.assertIn("SOURCE_HYGIENE_VIOLATION", {item["code"] for item in failed["findings"]})
|
|
|
|
def test_unresolved_placeholder_and_duplicate_section_id_fail(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
path = root / "notes/example.md"
|
|
path.parent.mkdir(parents=True)
|
|
path.write_text(
|
|
"# 예시\n{{missing}}\n<!-- section-id: duplicate -->\n<!-- section-id: duplicate -->\n",
|
|
encoding="utf-8",
|
|
)
|
|
result = quality_gate.run(
|
|
root,
|
|
[path],
|
|
include_graph=False,
|
|
require_moc_convergence=False,
|
|
)
|
|
self.assertEqual(
|
|
{item["code"] for item in result["findings"]},
|
|
{"UNRESOLVED_PLACEHOLDER", "DUPLICATE_SECTION_ID"},
|
|
)
|
|
|
|
def test_public_extensions_are_fail_closed_and_reported(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
path = root / "notes/example.md"
|
|
path.parent.mkdir(parents=True)
|
|
path.write_text("# 예시\n", encoding="utf-8")
|
|
extension = quality_gate.QualityExtension(
|
|
"typed-contract",
|
|
lambda _root: {
|
|
"schema_version": "typed-contract-check-result/v1",
|
|
"status": "FAIL",
|
|
"findings": [{"code": "STALE_REVISION", "path": "notes/example.md"}],
|
|
},
|
|
)
|
|
|
|
result = quality_gate.run(
|
|
root,
|
|
[path],
|
|
include_graph=False,
|
|
require_moc_convergence=False,
|
|
extensions=[extension],
|
|
)
|
|
|
|
self.assertEqual(result["status"], "FAIL")
|
|
self.assertIn("STALE_REVISION", {item["code"] for item in result["findings"]})
|
|
typed = next(item for item in result["checks"] if item["name"] == "typed-contract")
|
|
self.assertEqual(typed["schema_version"], "typed-contract-check-result/v1")
|
|
self.assertEqual(typed["status"], "FAIL")
|
|
|
|
def test_required_extension_cannot_silently_skip(self) -> None:
|
|
with tempfile.TemporaryDirectory() as directory:
|
|
root = Path(directory)
|
|
path = root / "notes/example.md"
|
|
path.parent.mkdir(parents=True)
|
|
path.write_text("# 예시\n", encoding="utf-8")
|
|
result = quality_gate.run(
|
|
root,
|
|
[path],
|
|
include_graph=False,
|
|
require_moc_convergence=False,
|
|
extensions=[quality_gate.QualityExtension("semantic-certificate", lambda _root: {"status": "SKIP", "findings": []})],
|
|
)
|
|
self.assertIn("REQUIRED_EXTENSION_SKIPPED", {item["code"] for item in result["findings"]})
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|