from __future__ import annotations import importlib.util import json import subprocess import sys import unittest from pathlib import Path from uuid import uuid4 from core.software_contracts import SoftwareContractError, get_contract ROOT = Path(__file__).resolve().parents[1] ADAPTER = ( ROOT / "windows-node" / "adapters" / "ansys.mechanical.static_structural@v1" ) WORKER_PATH = ADAPTER / "worker.py" def _load_worker(): spec = importlib.util.spec_from_file_location("zcbot_ansys_worker", WORKER_PATH) if spec is None or spec.loader is None: raise RuntimeError("could not load ANSYS worker") module = importlib.util.module_from_spec(spec) spec.loader.exec_module(module) return module def _request() -> dict: return { "schema_version": 1, "inputs": [{"key": "geometry", "artifact_id": str(uuid4())}], "operation": { "analysis": { "type": "static_structural", "title": "coupon static analysis", "unit_system": "mm_kg_s", "geometry": {"input": "geometry"}, "material": "structural_steel", "mesh": {"global_size": 2.0}, "boundary_conditions": [ {"type": "fixed_support", "named_selection": "fixed_face"} ], "loads": [ { "type": "force", "named_selection": "load_face", "components": [0, 0, -1000], } ], "results": ["total_deformation", "equivalent_stress"], } }, "outputs": [ {"key": "project", "format": "mechdat"}, {"key": "stress_image", "format": "png"}, ], } class AnsysContractTests(unittest.TestCase): def test_contract_is_not_enrolled_by_default_and_normalizes_static_job(self) -> None: contract = get_contract("ansys.mechanical.static_structural@v1") normalized, digest = contract.normalize_request(_request()) self.assertFalse(contract.default_enrollment) self.assertEqual(contract.feature(normalized), "static_structural") self.assertEqual(contract.required_adapter_version(normalized), "0.1.0") self.assertEqual(len(digest), 64) self.assertEqual( set(contract.expected_outputs(normalized)), { "project", "stress_image", "summary", "result_table", "analysis_spec", "provenance", }, ) def test_contract_rejects_code_paths_and_unsupported_analysis(self) -> None: contract = get_contract("ansys.mechanical.static_structural@v1") for key, value in ( ("script", "print('unsafe')"), ("apdl", "/SOLU"), ("path", r"C:\\private\\model.step"), ): request = _request() request[key] = value with self.assertRaises(SoftwareContractError): contract.normalize_request(request) request = _request() request["operation"]["analysis"]["type"] = "modal" with self.assertRaises(SoftwareContractError): contract.normalize_request(request) class AnsysWorkerTests(unittest.TestCase): @classmethod def setUpClass(cls) -> None: cls.worker = _load_worker() def test_worker_semantics_reject_zero_force_and_reused_target(self) -> None: request = _request() request["operation"]["analysis"]["loads"][0]["components"] = [0, 0, 0] with self.assertRaisesRegex(ValueError, "FORCE_VECTOR_MUST_BE_NONZERO"): self.worker._validate_semantics(request) request = _request() request["operation"]["analysis"]["loads"][0]["named_selection"] = "FIXED_FACE" with self.assertRaisesRegex(ValueError, "NAMED_SELECTION_TARGETS_MUST_BE_UNIQUE"): self.worker._validate_semantics(request) def test_probe_is_json_and_does_not_claim_ready_off_windows(self) -> None: completed = subprocess.run( [sys.executable, str(WORKER_PATH), "--probe"], check=True, capture_output=True, text=True, ) value = json.loads(completed.stdout) self.assertEqual(value["adapter_version"], "0.1.0") self.assertEqual(value["software"], "ANSYS Mechanical") if sys.platform != "win32": self.assertEqual(value["health"], "unavailable") def test_execution_is_guarded_until_real_license_acceptance(self) -> None: source = WORKER_PATH.read_text(encoding="utf-8") self.assertIn('EXECUTION_GATE = "ZCBOT_ANSYS_242_VALIDATED"', source) self.assertIn("ANSYS_242_REAL_LICENSE_ACCEPTANCE_REQUIRED", source) for forbidden in ("subprocess", "eval(", "exec(", "os.system"): self.assertNotIn(forbidden, source) if __name__ == "__main__": unittest.main()