Research-Stack/scripts/qc-flag/test_lean_qc_flagger.py
devin-ai-integration[bot] 6db78dccf3 test(coverage): add 138 unit tests for least-covered modules (#78)
Four test files covering the modules with zero or near-zero test coverage:

- scripts/qc-flag/test_lean_qc_flagger.py (55 tests)
  Covers all pure helpers, code-line detection, QCIssue/FileResult classes,
  and all 5 check functions (structural, naming, Q16, proof quality, deps).

- 4-Infrastructure/shim/test_braid_diat_codec.py (22 tests)
  Covers Q0_2 encode/decode, ChiralityDIAT roundtrips and verify_b,
  MountainPacked dict↔bytes, BraidResidualPacked bracket↔bytes.

- 5-Applications/tools-scripts/llm/test_emitter_utils.py (33 tests)
  Covers sha256_bytes, canonical_json_bytes, repo_relative, safe_slug,
  timestamps, context bundles, message building, extract_answer,
  auth guard, and verify_receipt schema rules.

- 4-Infrastructure/shim/test_validate_rrc_predictions.py (28 tests)
  Covers require_path, parse_equation, build_proof_metrics,
  build_receipt with all domain mappings, and MATH_* constant sets.

Build: all 138 tests pass (pytest), py_compile clean

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Allaun Silverfox <bigdataiscoming+9i37y6j2@protonmail.com>
2026-06-14 19:31:16 -05:00

411 lines
16 KiB
Python

#!/usr/bin/env python3
"""Unit tests for lean_qc_flagger.py — QC inspection helpers."""
import importlib.util
import json
import os
import sys
import tempfile
import unittest
from pathlib import Path
MODULE_PATH = Path(__file__).with_name("lean_qc_flagger.py")
SPEC = importlib.util.spec_from_file_location("lean_qc_flagger", MODULE_PATH)
assert SPEC is not None and SPEC.loader is not None
flagger = importlib.util.module_from_spec(SPEC)
sys.modules[SPEC.name] = flagger
SPEC.loader.exec_module(flagger)
# ---------------------------------------------------------------------------
# Helper utilities
# ---------------------------------------------------------------------------
class TestGetLine(unittest.TestCase):
def test_first_line(self):
self.assertEqual(flagger._get_line("abc\ndef\n", 0), 1)
def test_second_line(self):
self.assertEqual(flagger._get_line("abc\ndef\n", 4), 2)
def test_end_of_content(self):
self.assertEqual(flagger._get_line("abc\ndef\nghi", 10), 3)
class TestDefNames(unittest.TestCase):
def test_extracts_defs(self):
src = "def foo := 1\ndef bar := 2"
self.assertEqual(flagger._def_names(src), {"foo", "bar"})
def test_no_defs(self):
self.assertEqual(flagger._def_names("theorem t : True := trivial"), set())
class TestTheoremNames(unittest.TestCase):
def test_extracts_theorems(self):
src = "theorem thmA : True := trivial\ntheorem thmB : Nat = Nat := rfl"
self.assertEqual(flagger._theorem_names(src), {"thmA", "thmB"})
class TestPrivateDefNames(unittest.TestCase):
def test_extracts_private(self):
src = "private def helper := 42\ndef public := 1"
self.assertEqual(flagger._private_def_names(src), {"helper"})
class TestGetImportsOpens(unittest.TestCase):
def test_get_imports(self):
src = "import Mathlib.Data.Nat\nimport Semantics.RRC\ndef foo := 1"
self.assertEqual(flagger._get_imports(src), ["Mathlib.Data.Nat", "Semantics.RRC"])
def test_get_opens(self):
src = "open Nat\nopen List\ntheorem t := by decide"
self.assertEqual(flagger._get_opens(src), ["Nat", "List"])
class TestNormalizePathSep(unittest.TestCase):
def test_backslash(self):
self.assertEqual(flagger._normalize_path_sep("a\\b\\c"), "a/b/c")
def test_forward_slash_unchanged(self):
self.assertEqual(flagger._normalize_path_sep("a/b/c"), "a/b/c")
# ---------------------------------------------------------------------------
# Code-line detection (block comment filtering)
# ---------------------------------------------------------------------------
class TestGetCodeLines(unittest.TestCase):
def test_plain_code(self):
src = "def foo := 1\ntheorem t := by decide"
result = flagger._get_code_lines(src)
self.assertTrue(all(v is True for v in result))
def test_line_comments(self):
src = "-- comment\ndef foo := 1"
result = flagger._get_code_lines(src)
self.assertFalse(result[0])
self.assertTrue(result[1])
def test_block_comment(self):
src = "/- block\n comment -/\ndef foo := 1"
result = flagger._get_code_lines(src)
self.assertFalse(result[0])
self.assertFalse(result[1])
self.assertTrue(result[2])
def test_empty_lines(self):
src = "\n\ndef foo := 1"
result = flagger._get_code_lines(src)
self.assertFalse(result[0])
self.assertFalse(result[1])
self.assertTrue(result[2])
def test_single_line_block_comment(self):
src = "/- inline -/\ndef foo := 1"
result = flagger._get_code_lines(src)
self.assertFalse(result[0])
self.assertTrue(result[1])
class TestPosIsCode(unittest.TestCase):
def test_code_position(self):
src = "def foo := 1"
self.assertTrue(flagger._pos_is_code(src, 0))
def test_comment_position(self):
src = "-- comment\ndef foo := 1"
self.assertFalse(flagger._pos_is_code(src, 3))
class TestIsDataDef(unittest.TestCase):
def test_struct_def(self):
src = "def myConfig : Config := {\n field1 := 1\n}"
self.assertTrue(flagger._is_data_def(src, "myConfig"))
def test_numeric_def(self):
src = "def maxSize : Nat := 42"
self.assertTrue(flagger._is_data_def(src, "maxSize"))
def test_function_def(self):
src = "def addOne (n : Nat) : Nat := n + 1"
self.assertFalse(flagger._is_data_def(src, "addOne"))
# ---------------------------------------------------------------------------
# QCIssue / FileResult
# ---------------------------------------------------------------------------
class TestQCIssue(unittest.TestCase):
def test_to_dict(self):
issue = flagger.QCIssue("structural_health", "test msg", 10, "ERROR")
d = issue.to_dict()
self.assertEqual(d["check"], "structural_health")
self.assertEqual(d["message"], "test msg")
self.assertEqual(d["line"], 10)
self.assertEqual(d["severity"], "ERROR")
class TestFileResult(unittest.TestCase):
def test_empty_result_passes(self):
r = flagger.FileResult("test.lean")
self.assertTrue(r.passed)
self.assertEqual(r.issues, [])
def test_warning_keeps_pass(self):
r = flagger.FileResult("test.lean")
r.add_issue(flagger.QCIssue("check", "msg", 1, "WARNING"))
self.assertTrue(r.passed)
def test_error_fails(self):
r = flagger.FileResult("test.lean")
r.add_issue(flagger.QCIssue("check", "msg", 1, "ERROR"))
self.assertFalse(r.passed)
def test_to_dict_shape(self):
r = flagger.FileResult("Test.lean")
r.structural = {"theorems": 2}
r.add_issue(flagger.QCIssue("check", "msg", 5, "WARNING"))
d = r.to_dict()
self.assertEqual(d["path"], "Test.lean")
self.assertTrue(d["passed"])
self.assertEqual(d["issue_count"], 1)
self.assertEqual(d["structural"]["theorems"], 2)
# ---------------------------------------------------------------------------
# Check functions
# ---------------------------------------------------------------------------
class TestCheckStructuralHealth(unittest.TestCase):
def test_counts_theorems_and_defs(self):
src = "theorem t1 : True := trivial\ndef f := 1\ndef g := 2"
r = flagger.FileResult("Test.lean")
flagger.check_structural_health(src, r)
self.assertEqual(r.structural["theorems"], 1)
self.assertEqual(r.structural["defs"], 2)
def test_sorry_flagged_as_error(self):
src = "theorem t : True := by sorry"
r = flagger.FileResult("Test.lean")
flagger.check_structural_health(src, r)
self.assertFalse(r.passed)
self.assertTrue(any(i.check == "structural_health" and "sorry" in i.message for i in r.issues))
def test_sorry_in_comment_not_flagged(self):
src = "-- sorry is in comment\ntheorem t : True := trivial"
r = flagger.FileResult("Test.lean")
flagger.check_structural_health(src, r)
self.assertEqual(r.structural["sorries"], 0)
def test_empty_theorem_detected(self):
src = "theorem t : True := by\ntheorem t2 : False := by decide"
r = flagger.FileResult("Test.lean")
flagger.check_structural_health(src, r)
self.assertEqual(r.structural["empty_theorems"], 1)
def test_eval_and_eval_bang_counted(self):
src = "#eval 1 + 1\n#eval! IO.println"
r = flagger.FileResult("Test.lean")
flagger.check_structural_health(src, r)
self.assertEqual(r.structural["eval"], 1)
self.assertEqual(r.structural["eval_bang"], 1)
def test_native_decide_counted(self):
src = "theorem t := by native_decide"
r = flagger.FileResult("Test.lean")
flagger.check_structural_health(src, r)
self.assertEqual(r.structural["native_decide"], 1)
class TestCheckNamingConventions(unittest.TestCase):
def test_pascal_case_file_passes(self):
src = "def foo := 1"
r = flagger.FileResult("MyModule.lean")
flagger.check_naming_conventions(src, r, "MyModule.lean")
naming_issues = [i for i in r.issues if "File name" in i.message]
self.assertEqual(naming_issues, [])
def test_snake_case_file_flagged(self):
src = "def foo := 1"
r = flagger.FileResult("my_module.lean")
flagger.check_naming_conventions(src, r, "my_module.lean")
self.assertTrue(any("snake_case" in i.message for i in r.issues))
def test_non_pascal_type_flagged(self):
src = "inductive myType where | a | b"
r = flagger.FileResult("Test.lean")
flagger.check_naming_conventions(src, r, "Test.lean")
self.assertTrue(any("myType" in i.message and "PascalCase" in i.message for i in r.issues))
def test_non_camel_def_flagged(self):
src = "def MyFunc := 1"
r = flagger.FileResult("Test.lean")
flagger.check_naming_conventions(src, r, "Test.lean")
self.assertTrue(any("MyFunc" in i.message for i in r.issues))
def test_banned_prefix_flagged(self):
src = "def getItems := []"
r = flagger.FileResult("Test.lean")
flagger.check_naming_conventions(src, r, "Test.lean")
self.assertTrue(any("Banned prefix" in i.message for i in r.issues))
def test_banned_suffix_flagged(self):
src = "def helper_v2 := 1"
r = flagger.FileResult("Test.lean")
flagger.check_naming_conventions(src, r, "Test.lean")
self.assertTrue(any("Banned suffix" in i.message for i in r.issues))
class TestCheckQ16Compliance(unittest.TestCase):
def test_float_in_code_flagged(self):
src = "def x : Float := 1.0"
r = flagger.FileResult("Test.lean")
flagger.check_q16_compliance(src, r)
self.assertTrue(any("Float" in i.message for i in r.issues))
def test_float_in_comment_not_flagged(self):
src = "-- Float is fine in comments\ndef x : Nat := 1"
r = flagger.FileResult("Test.lean")
flagger.check_q16_compliance(src, r)
q16_issues = [i for i in r.issues if i.check == "q16_compliance"]
self.assertEqual(q16_issues, [])
class TestCheckProofQuality(unittest.TestCase):
def test_def_without_companion_flagged(self):
src = "def orphanFunc (n : Nat) : Nat := n + 1"
r = flagger.FileResult("Test.lean")
flagger.check_proof_quality(src, r)
self.assertTrue(any("orphanFunc" in i.message and "companion" in i.message for i in r.issues))
def test_def_with_companion_theorem_ok(self):
src = "def addOne (n : Nat) : Nat := n + 1\ntheorem addOneSpec : addOne 0 = 1 := rfl"
r = flagger.FileResult("Test.lean")
flagger.check_proof_quality(src, r)
orphan = [i for i in r.issues if "addOne" in i.message and "companion" in i.message]
self.assertEqual(orphan, [])
def test_private_def_not_flagged(self):
src = "private def helper := 42"
r = flagger.FileResult("Test.lean")
flagger.check_proof_quality(src, r)
orphan = [i for i in r.issues if "helper" in i.message and "companion" in i.message]
self.assertEqual(orphan, [])
def test_data_def_not_flagged(self):
src = "def maxSize : Nat := 42"
r = flagger.FileResult("Test.lean")
flagger.check_proof_quality(src, r)
orphan = [i for i in r.issues if "maxSize" in i.message and "companion" in i.message]
self.assertEqual(orphan, [])
class TestCheckDependencyAnalysis(unittest.TestCase):
def test_unused_import_flagged(self):
src = "import Semantics.Unused\ndef foo := 1"
r = flagger.FileResult("Test.lean")
flagger.check_dependency_analysis(src, r, "Test.lean")
self.assertTrue(any("Unused import" in i.message for i in r.issues))
def test_used_import_not_flagged(self):
src = "import Semantics.RRC\ndef foo := RRC.classify x"
r = flagger.FileResult("Test.lean")
flagger.check_dependency_analysis(src, r, "Test.lean")
unused = [i for i in r.issues if "Unused import" in i.message]
self.assertEqual(unused, [])
def test_opened_import_not_flagged(self):
src = "import Semantics.Types\nopen Types\ndef foo := mkValue 1"
r = flagger.FileResult("Test.lean")
flagger.check_dependency_analysis(src, r, "Test.lean")
unused = [i for i in r.issues if "Unused import" in i.message]
self.assertEqual(unused, [])
# ---------------------------------------------------------------------------
# scan_file integration
# ---------------------------------------------------------------------------
class TestScanFile(unittest.TestCase):
def test_scan_clean_file(self):
with tempfile.NamedTemporaryFile(suffix=".lean", mode="w", delete=False) as f:
f.write("def addOne (n : Nat) : Nat := n + 1\n#eval addOne 0\n")
f.flush()
try:
result = flagger.scan_file(f.name)
self.assertTrue(result.passed)
finally:
os.unlink(f.name)
def test_scan_file_with_sorry(self):
with tempfile.NamedTemporaryFile(suffix=".lean", mode="w", delete=False) as f:
f.write("theorem bogus : True := by sorry\n")
f.flush()
try:
result = flagger.scan_file(f.name)
self.assertFalse(result.passed)
finally:
os.unlink(f.name)
def test_scan_nonexistent_file(self):
result = flagger.scan_file("/tmp/nonexistent_file_12345.lean")
self.assertFalse(result.passed)
# ---------------------------------------------------------------------------
# find_lean_files / gather_imports
# ---------------------------------------------------------------------------
class TestFindLeanFiles(unittest.TestCase):
def test_single_file(self):
with tempfile.NamedTemporaryFile(suffix=".lean", delete=False) as f:
try:
files = flagger.find_lean_files(f.name)
self.assertEqual(len(files), 1)
finally:
os.unlink(f.name)
def test_directory(self):
with tempfile.TemporaryDirectory() as td:
(Path(td) / "A.lean").write_text("def a := 1")
(Path(td) / "B.lean").write_text("def b := 2")
(Path(td) / "not_lean.py").write_text("pass")
files = flagger.find_lean_files(td)
self.assertEqual(len(files), 2)
def test_nonexistent_path(self):
files = flagger.find_lean_files("/tmp/nonexistent_dir_12345")
self.assertEqual(files, [])
class TestGatherImports(unittest.TestCase):
def test_gathers_imports_from_files(self):
with tempfile.TemporaryDirectory() as td:
a = Path(td) / "A.lean"
a.write_text("import Semantics.RRC\ndef foo := 1")
b = Path(td) / "B.lean"
b.write_text("import Semantics.Types\nimport Semantics.AVM\ndef bar := 2")
imports = flagger.gather_imports([str(a), str(b)])
self.assertEqual(imports[str(a)], ["Semantics.RRC"])
self.assertEqual(imports[str(b)], ["Semantics.Types", "Semantics.AVM"])
# ---------------------------------------------------------------------------
# Markdown report generation
# ---------------------------------------------------------------------------
class TestGenerateMarkdownReport(unittest.TestCase):
def test_report_structure(self):
r = flagger.FileResult("Test.lean")
r.structural = {"theorems": 1, "defs": 0}
r.add_issue(flagger.QCIssue("check", "test issue", 5, "WARNING"))
md = flagger.generate_markdown_report([r], "/path")
self.assertIn("QC Flag Report", md)
self.assertIn("Test.lean", md)
self.assertIn("PASS", md)
self.assertIn("test issue", md)
if __name__ == "__main__":
unittest.main()