"""Regressions for omitted business commands and false completion claims.""" import json import contextlib import io import sys import tempfile import unittest from pathlib import Path from unittest.mock import patch sys.path.insert(0, str(Path(__file__).resolve().parent)) import business_acceptance as acceptance import run_business_acceptance as runner import generate_web_business_completion_audit as audit from tools.generate_web_workbench_inventory import python_gui_commands, source_gui_inventory class InventoryTests(unittest.TestCase): def test_module_root_registration_and_named_menu_lists(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory) module = root / "src/Mod/Assembly" module.mkdir(parents=True) (module / "CommandJoint.py").write_text('Gui.addCommand("Assembly_CreateJointRevolute", Command())\n') init = module / "InitGui.py" init.write_text('commands = ["Assembly_CreateAssembly"]\nextra = ["Assembly_SolveAssembly"]\nself.appendMenu("Assembly", commands + extra)\n') inventory = source_gui_inventory(root, module) self.assertEqual(set(inventory["commands"]), {"Assembly_CreateJointRevolute", "Assembly_CreateAssembly", "Assembly_SolveAssembly"}) def test_cyclic_alias_does_not_hang_or_invent_command(self): with tempfile.TemporaryDirectory() as directory: path = Path(directory) / "InitGui.py" path.write_text('first = second\nsecond = first\nself.appendToolbar("Label", first)\n') self.assertEqual(python_gui_commands(path), []) class AcceptanceTests(unittest.TestCase): def tearDown(self): acceptance.digest.cache_clear() acceptance.module_inputs.cache_clear() def test_empty_or_failed_assertions_cannot_complete_business(self): self.assertFalse(acceptance.successful_result({"status": "passed", "cases": []})) self.assertFalse(acceptance.successful_result({"status": "passed", "cases": [{"input": {}, "assertions": [{"name": "volume", "passed": False}]}]})) self.assertFalse(acceptance.successful_result({"status": "passed", "cases": [{"assertions": [{"name": "volume", "passed": True}]}]})) def test_runtime_issue_closure_requires_declared_success_and_unchanged_observation(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory) path = root / "docs/current/runtime-issue-ledger.json" path.parent.mkdir(parents=True) evidence = root / "evidence.json" evidence.write_text("verified results") issue = {"id": "runtime:one", "capabilityId": "TechDraw:create", "status": "fix_deployed_pending_use", "occurrences": 1, "args": {"length": 10}} result = {"capabilityId": issue["capabilityId"], "status": "passed", "cases": [{"input": issue["args"], "assertions": [{"name": "native length", "passed": True}]}]} report = {"executedAt": "2026-09-08T00:00:00Z", "results": [result]} before = {issue["id"]: runner.observation(issue)} passed = {issue["capabilityId"]} with patch.object(runner, "ROOT", root): path.write_text(json.dumps({"issues": [issue]})) self.assertEqual(runner.close_tested_issues(report, evidence, before, passed), 0) result["resolvedIssueIds"] = [issue["id"]] self.assertEqual(runner.close_tested_issues(report, evidence, before, set()), 0) newer = {**issue, "occurrences": 2, "lastSeenAt": "later"} path.write_text(json.dumps({"issues": [newer]})) self.assertEqual(runner.close_tested_issues(report, evidence, before, passed), 0) path.write_text(json.dumps({"issues": [issue]})) self.assertEqual(runner.close_tested_issues(report, evidence, before, passed), 1) closed = json.loads(path.read_text())["issues"][0] self.assertEqual(closed["status"], "resolved") self.assertEqual(closed["businessEvidenceSha256"], acceptance.digest(evidence)) self.assertEqual(closed["resolvedObservation"], before[issue["id"]]) def test_current_evidence_is_reused_but_changed_module_is_reopened(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory) source = root / "web/src/part-facade.ts" source.parent.mkdir(parents=True) source.write_text("version1") evidence = root / "evidence.json" result = {"capabilityId": "Part:part.create", "status": "passed", "cases": [{"input": {"length": 2}, "assertions": [{"name": "volume", "passed": True}]}]} evidence.write_text(json.dumps({"schema": "freecad-web-business-cases/v1", "results": [result]})) ledger = root / "ledger.json" row = {"id": result["capabilityId"], "module": "Part"} with patch.object(acceptance, "ROOT", root), patch.object(acceptance, "LEDGER", ledger): ledger.write_text(json.dumps({"records": {row["id"]: {"status": "passed", "fingerprint": acceptance.fingerprint(row), "evidencePath": "evidence.json", "evidenceSha256": acceptance.digest(evidence)}}})) self.assertEqual(acceptance.accepted_ids([row]), {row["id"]}) source.write_text("version2") acceptance.digest.cache_clear() acceptance.module_inputs.cache_clear() self.assertEqual(acceptance.accepted_ids([row]), set()) def test_runner_requires_successful_execution_and_preserves_prior_evidence(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory) (root / "docs/current").mkdir(parents=True) (root / "docs/business-acceptance").mkdir() script = root / "case.py" row = {"id": "Part:part.create", "module": "Part", "implementationStatus": "routed"} result = {"schema": "freecad-web-business-cases/v1", "results": [{"capabilityId": row["id"], "status": "passed", "cases": [{"input": {"length": 2}, "assertions": [{"name": "volume", "passed": True}]}]}]} script.write_text(f"print({json.dumps(result)!r})\n") (root / "docs/business-acceptance/Part.json").write_text(json.dumps({"command": ["python3", "case.py"]})) (root / "docs/current/module-batch-manifest.json").write_text(json.dumps({"batchId": "batch", "selected": {"module": "Part"}, "records": [[row["id"]]]})) ledger = root / "docs/current/business-acceptance.json" with patch.object(acceptance, "ROOT", root), patch.object(acceptance, "LEDGER", ledger), patch.object(runner, "ROOT", root), patch.object(runner, "LEDGER", ledger), patch.object(runner, "requirements", return_value=[row]), contextlib.redirect_stdout(io.StringIO()): self.assertEqual(runner.main(), 0) self.assertEqual(acceptance.accepted_ids([row]), {row["id"]}) previous = json.loads(ledger.read_text())["records"][row["id"]]["evidencePath"] script.write_text("raise SystemExit(1)\n") self.assertEqual(runner.main(), 1) self.assertTrue((root / previous).is_file()) self.assertEqual(json.loads(ledger.read_text())["records"][row["id"]]["status"], "failed") self.assertEqual(acceptance.accepted_ids([row]), set()) def test_unrouted_business_remains_in_completion_denominator(self): with tempfile.TemporaryDirectory() as directory: root = Path(directory) sources = {key: root / f"{key}.json" for key in audit.SOURCES} rows = [{"id": "Part:one", "module": "Part", "implementationStatus": "routed"}, {"id": "Part:two", "module": "Part", "implementationStatus": "implementation_required"}] sources["features"].write_text(json.dumps({"records": rows})) sources["receipts"].write_text(json.dumps({"records": {row["id"]: {"status": "implementation_ready"} for row in rows}})) sources["modules"].write_text(json.dumps({"modules": {str(i): {} for i in range(32)}})) with patch.object(audit, "ROOT", root), patch.object(audit, "SOURCES", sources), patch.object(audit, "OUTPUT", root / "out.json"), patch.object(audit, "MARKDOWN", root / "out.md"), patch.object(audit, "requirements", return_value=rows), patch.object(audit, "accepted_ids", return_value=set()), contextlib.redirect_stdout(io.StringIO()): self.assertEqual(audit.main(), 0) result = json.loads((root / "out.json").read_text()) self.assertEqual(result["implementationProgress"], {"completed": 1, "total": 2, "remaining": 1}) self.assertFalse(result["businessFunctionalComplete"]) if __name__ == "__main__": unittest.main()