mirror of
https://github.com/kevinveenbirkenbach/computer-playbook.git
synced 2026-11-08 19:56:53 +00:00
registeredServices() derives each slug from its env key, so
AGENT_BROKER_SERVICE_ENABLED registers agent_broker, while
assertKnownService() compared the raw name. isServiceEnabled("agent-broker")
therefore threw "Unknown service" before the spec ran. The check now
normalises the name through envKey(), the same path the registry uses;
names made of [a-z0-9_] resolve exactly as before.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
215 lines
7.2 KiB
Python
215 lines
7.2 KiB
Python
"""Unit tests for roles/test-e2e-playwright/files/service-gating.js.
|
|
|
|
The helper is plain CommonJS. Its only external dependency is
|
|
`@playwright/test` for the `test.skip()` control flow. We fabricate a
|
|
tiny `node_modules/@playwright/test` stub in a temp dir so the helper
|
|
loads against a predictable fake, then call it from a short Node
|
|
script and assert the observable behaviour.
|
|
"""
|
|
|
|
import json
|
|
import os
|
|
import shutil
|
|
import subprocess
|
|
import tempfile
|
|
import textwrap
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
from . import PROJECT_ROOT
|
|
|
|
HELPER_PATH = str(
|
|
PROJECT_ROOT / "roles" / "test-e2e-playwright" / "files" / "service-gating.js"
|
|
)
|
|
|
|
STUB_PKG_JSON = json.dumps({"name": "@playwright/test", "main": "index.js"})
|
|
STUB_INDEX_JS = textwrap.dedent(
|
|
"""
|
|
const skipRecords = [];
|
|
module.exports = {
|
|
test: {
|
|
skip(cond, reason) {
|
|
if (cond) {
|
|
skipRecords.push(reason || "");
|
|
console.log("SKIP:" + (reason || ""));
|
|
const e = new Error("skipped");
|
|
e.__skipped = true;
|
|
throw e;
|
|
}
|
|
},
|
|
},
|
|
expect: () => ({ toBeVisible: () => {}, toBeTruthy: () => {} }),
|
|
__skipRecords: skipRecords,
|
|
};
|
|
"""
|
|
)
|
|
|
|
|
|
def _have_node():
|
|
return shutil.which("node") is not None
|
|
|
|
|
|
@unittest.skipUnless(_have_node(), "node is not available in PATH")
|
|
class TestServiceGatingHelper(unittest.TestCase):
|
|
def setUp(self):
|
|
self.tmpdir = tempfile.mkdtemp(prefix="sg-helper-")
|
|
stub_dir = str(Path(self.tmpdir) / "node_modules" / "@playwright" / "test")
|
|
Path(stub_dir).mkdir(parents=True, exist_ok=True)
|
|
with Path(str(Path(stub_dir) / "package.json")).open("w") as f:
|
|
f.write(STUB_PKG_JSON)
|
|
with Path(str(Path(stub_dir) / "index.js")).open("w") as f:
|
|
f.write(STUB_INDEX_JS)
|
|
shutil.copy(HELPER_PATH, str(Path(self.tmpdir) / "service-gating.js"))
|
|
|
|
def tearDown(self):
|
|
shutil.rmtree(self.tmpdir, ignore_errors=True)
|
|
|
|
def _run_in_node(self, snippet, env):
|
|
script = textwrap.dedent(
|
|
f"""
|
|
const helper = require("./service-gating");
|
|
(async () => {{
|
|
try {{
|
|
const result = await (async () => {{ {snippet} }})();
|
|
process.stdout.write("RESULT:" + JSON.stringify(result ?? null) + "\\n");
|
|
}} catch (err) {{
|
|
if (err && err.__skipped) {{
|
|
process.exit(0);
|
|
}}
|
|
process.stdout.write(
|
|
"ERROR:" + (err && err.message ? err.message : String(err)) + "\\n"
|
|
);
|
|
process.exit(1);
|
|
}}
|
|
}})();
|
|
"""
|
|
)
|
|
script_path = str(Path(self.tmpdir) / "run.js")
|
|
with Path(script_path).open("w") as f:
|
|
f.write(script)
|
|
return subprocess.run(
|
|
["node", "run.js"],
|
|
capture_output=True,
|
|
text=True,
|
|
env={**os.environ, **env},
|
|
cwd=self.tmpdir,
|
|
timeout=10,
|
|
check=False,
|
|
)
|
|
|
|
# isServiceEnabled -----------------------------------------------------
|
|
|
|
def test_enabled_when_flag_true(self):
|
|
proc = self._run_in_node(
|
|
"return helper.isServiceEnabled('sso');",
|
|
env={"SSO_SERVICE_ENABLED": "true"},
|
|
)
|
|
self.assertEqual(
|
|
proc.returncode, 0, msg=f"stderr={proc.stderr}\nstdout={proc.stdout}"
|
|
)
|
|
self.assertIn("RESULT:true", proc.stdout)
|
|
|
|
def test_disabled_when_flag_false(self):
|
|
proc = self._run_in_node(
|
|
"return helper.isServiceEnabled('sso');",
|
|
env={
|
|
"SSO_SERVICE_ENABLED": "false",
|
|
"EMAIL_SERVICE_ENABLED": "true",
|
|
},
|
|
)
|
|
self.assertEqual(
|
|
proc.returncode, 0, msg=f"stderr={proc.stderr}\nstdout={proc.stdout}"
|
|
)
|
|
self.assertIn("RESULT:false", proc.stdout)
|
|
|
|
def test_strict_value_rejected(self):
|
|
proc = self._run_in_node(
|
|
"return helper.isServiceEnabled('sso');",
|
|
env={"SSO_SERVICE_ENABLED": "yes"},
|
|
)
|
|
self.assertNotEqual(proc.returncode, 0)
|
|
self.assertIn("SSO_SERVICE_ENABLED", proc.stdout)
|
|
|
|
def test_a_hyphenated_service_resolves_to_its_underscored_flag(self):
|
|
proc = self._run_in_node(
|
|
"return helper.isServiceEnabled('agent-broker');",
|
|
env={"AGENT_BROKER_SERVICE_ENABLED": "false"},
|
|
)
|
|
self.assertEqual(
|
|
proc.returncode, 0, msg=f"stderr={proc.stderr}\nstdout={proc.stdout}"
|
|
)
|
|
self.assertIn("RESULT:false", proc.stdout)
|
|
|
|
def test_unknown_service_hard_fails(self):
|
|
proc = self._run_in_node(
|
|
"return helper.isServiceEnabled('oicd');",
|
|
env={"SSO_SERVICE_ENABLED": "true"},
|
|
)
|
|
self.assertNotEqual(proc.returncode, 0)
|
|
self.assertIn("Unknown service", proc.stdout)
|
|
|
|
def test_empty_registry_treats_everything_enabled(self):
|
|
proc = self._run_in_node(
|
|
"return helper.isServiceEnabled('sso');",
|
|
env={},
|
|
)
|
|
self.assertEqual(
|
|
proc.returncode, 0, msg=f"stderr={proc.stderr}\nstdout={proc.stdout}"
|
|
)
|
|
self.assertIn("RESULT:true", proc.stdout)
|
|
|
|
# isServiceDisabledReason ---------------------------------------------
|
|
|
|
def test_disabled_reason_names_the_flag(self):
|
|
proc = self._run_in_node(
|
|
"return helper.isServiceDisabledReason('email');",
|
|
env={
|
|
"EMAIL_SERVICE_ENABLED": "false",
|
|
"SSO_SERVICE_ENABLED": "true",
|
|
},
|
|
)
|
|
self.assertEqual(
|
|
proc.returncode, 0, msg=f"stderr={proc.stderr}\nstdout={proc.stdout}"
|
|
)
|
|
self.assertIn("EMAIL_SERVICE_ENABLED=false", proc.stdout)
|
|
|
|
def test_enabled_reason_is_null(self):
|
|
proc = self._run_in_node(
|
|
"return helper.isServiceDisabledReason('email');",
|
|
env={"EMAIL_SERVICE_ENABLED": "true"},
|
|
)
|
|
self.assertEqual(
|
|
proc.returncode, 0, msg=f"stderr={proc.stderr}\nstdout={proc.stdout}"
|
|
)
|
|
self.assertIn("RESULT:null", proc.stdout)
|
|
|
|
# requireService -------------------------------------------------------
|
|
|
|
def test_require_service_runs_testfn_when_enabled(self):
|
|
proc = self._run_in_node(
|
|
"const fn = helper.requireService('sso', async () => 'HIT');return fn({});",
|
|
env={"SSO_SERVICE_ENABLED": "true"},
|
|
)
|
|
self.assertEqual(
|
|
proc.returncode, 0, msg=f"stderr={proc.stderr}\nstdout={proc.stdout}"
|
|
)
|
|
self.assertIn('RESULT:"HIT"', proc.stdout)
|
|
|
|
def test_require_service_skips_when_disabled(self):
|
|
proc = self._run_in_node(
|
|
"const fn = helper.requireService('sso', async () => 'HIT');return fn({});",
|
|
env={
|
|
"SSO_SERVICE_ENABLED": "false",
|
|
"EMAIL_SERVICE_ENABLED": "true",
|
|
},
|
|
)
|
|
self.assertEqual(
|
|
proc.returncode, 0, msg=f"stderr={proc.stderr}\nstdout={proc.stdout}"
|
|
)
|
|
self.assertIn("SKIP:", proc.stdout)
|
|
self.assertIn("SSO_SERVICE_ENABLED=false", proc.stdout)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|