import json import subprocess import unittest from pathlib import Path REPO = Path(__file__).resolve().parents[1] BUILD_SCRIPT = REPO / "scripts" / "build-win-helper.ps1" HELPER_EXE = REPO / "runs" / "win-helper" / "ISphereWinHelper.exe" def build_helper(): result = subprocess.run( [ "powershell", "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", str(BUILD_SCRIPT), ], cwd=REPO, text=True, encoding="utf-8", errors="replace", stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=60, ) if result.returncode != 0: raise AssertionError(result.stdout + result.stderr) def invoke_helper(payload): if isinstance(payload, str): stdin = payload else: stdin = json.dumps(payload, ensure_ascii=False) result = subprocess.run( [str(HELPER_EXE), "--json"], cwd=REPO, input=stdin, text=True, encoding="utf-8", errors="replace", stdout=subprocess.PIPE, stderr=subprocess.PIPE, timeout=30, ) try: decoded = json.loads(result.stdout) except json.JSONDecodeError as exc: raise AssertionError(f"stdout was not JSON: {result.stdout!r}; stderr={result.stderr!r}") from exc return result, decoded class WinHelperBuildTests(unittest.TestCase): def test_build_script_creates_helper_exe(self): if HELPER_EXE.exists(): HELPER_EXE.unlink() build_helper() self.assertTrue(HELPER_EXE.exists(), f"helper exe missing: {HELPER_EXE}") class WinHelperProtocolTests(unittest.TestCase): @classmethod def setUpClass(cls): build_helper() def request(self, op, args=None): result, decoded = invoke_helper( { "protocol": "isphere.helper.v1", "request_id": f"test-{op}", "op": op, "timeout_ms": 5000, "args": args or {}, } ) self.assertEqual(result.returncode, 0, result.stderr) return decoded def test_version_returns_contract_metadata(self): decoded = self.request("version") self.assertTrue(decoded["ok"]) self.assertEqual(decoded["protocol"], "isphere.helper.v1") self.assertEqual(decoded["request_id"], "test-version") self.assertEqual(decoded["op"], "version") self.assertEqual(decoded["data"]["helper_name"], "ISphereWinHelper") self.assertEqual(decoded["data"]["protocol"], "isphere.helper.v1") def test_self_check_reports_desktop_and_uia_status(self): decoded = self.request("self_check") self.assertTrue(decoded["ok"]) self.assertIn("desktop_access", decoded["data"]) self.assertIn("uia_available", decoded["data"]) def test_scan_windows_returns_json_window_list(self): decoded = self.request("scan_windows", {"include_all_visible": True}) self.assertTrue(decoded["ok"]) self.assertIsInstance(decoded["data"]["windows"], list) def test_unsupported_op_returns_structured_error(self): decoded = self.request("does_not_exist") self.assertFalse(decoded["ok"]) self.assertEqual(decoded["error"]["code"], "UNSUPPORTED_OP") def test_invalid_json_returns_structured_error(self): result, decoded = invoke_helper("{not json") self.assertEqual(result.returncode, 0, result.stderr) self.assertFalse(decoded["ok"]) self.assertEqual(decoded["protocol"], "isphere.helper.v1") self.assertEqual(decoded["error"]["code"], "INVALID_REQUEST") def test_dump_uia_with_missing_window_returns_structured_error(self): decoded = self.request("dump_uia", {"hwnd": "0x0"}) self.assertFalse(decoded["ok"]) self.assertIn(decoded["error"]["code"], {"WINDOW_NOT_FOUND", "UIA_DUMP_FAILED"}) if __name__ == "__main__": unittest.main()