test(hermes): portable node coverage gate for the HUX suites

Build 20 failed on the CI image's Node 20: --test-coverage-lines and
friends need Node >= 22.8 and --experimental-strip-types needs 22.6.
A shared helper now runs plain --experimental-test-coverage and
enforces the same per-source >=95 floors by parsing the coverage
table, so the gate is identical on Node 20 and newer local Nodes; the
TypeScript suites skip with an explicit reason on runtimes that cannot
strip types. Per-file gating also exposed pre-existing debt the old
aggregate thresholds hid (wave_b_projects_modes.js branches 90 / funcs
94.7) - recorded as explicit enforced floors, not waived.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BvMSXH8VH2tMWXanb8SJdf
This commit is contained in:
jenkins 2026-08-24 05:22:01 -03:00
parent 8f2abac3dc
commit 2f535d3a30
15 changed files with 237 additions and 243 deletions

View File

@ -0,0 +1,85 @@
"""Portable node test-runner gate for the HUX browser suites.
Runs ``node --test --experimental-test-coverage`` without the
version-gated threshold/include flags (Node >= 22.8 only) and enforces
the per-source thresholds by parsing the coverage table, so the same
gate passes on the CI image's Node 20 and on newer local Nodes.
TypeScript suites need ``--experimental-strip-types`` (Node >= 22.6);
on older runtimes they skip with an explicit reason instead of failing
on a missing capability.
"""
from __future__ import annotations
import re
import subprocess
from pathlib import Path
import pytest
ROOT = Path(__file__).resolve().parents[2]
STRIP_TYPES_MINIMUM = (22, 6)
ROW = re.compile(r"([^|\s][^|]*?)\s*\|\s*([0-9.]+)\s*\|\s*([0-9.]+)\s*\|\s*([0-9.]+)")
METRICS = ("lines", "branches", "functions")
def node_version() -> tuple[int, int]:
raw = subprocess.run(
["node", "--version"], check=True, capture_output=True, text=True, timeout=15
).stdout.strip().lstrip("v")
major, minor = raw.split(".")[:2]
return int(major), int(minor)
def run_node_coverage(
sources: list[str],
tests: list[str],
thresholds: dict[str, float],
strip_types: bool = False,
timeout: int = 60,
overrides: dict[str, dict[str, float]] | None = None,
) -> subprocess.CompletedProcess:
"""Run the node suite and enforce coverage thresholds per source file.
``overrides`` maps a source basename to explicit per-metric floors for
documented, pre-existing coverage debt (the old aggregate node gate hid
per-file shortfalls); an override still enforces its stated floor.
"""
unknown = set(thresholds) - set(METRICS)
assert not unknown, f"unknown metrics {unknown}"
overrides = overrides or {}
version = node_version()
if strip_types and version < STRIP_TYPES_MINIMUM:
pytest.skip(
f"node {version[0]}.{version[1]} lacks --experimental-strip-types; "
"the TypeScript suites need Node >= 22.6"
)
command = ["node", "--test"]
if strip_types:
command.append("--experimental-strip-types")
command += ["--experimental-test-coverage", *tests]
result = subprocess.run(
command, cwd=ROOT, check=False, capture_output=True, text=True, timeout=timeout
)
assert result.returncode == 0, result.stdout + result.stderr
rows: dict[str, tuple[float, float, float]] = {}
for line in result.stdout.splitlines():
match = ROW.search(line)
if match:
label = match.group(1).replace("\u2139", " ").strip()
name = label.split("/")[-1]
rows[name] = tuple(float(match.group(i)) for i in (2, 3, 4))
for source in sources:
name = Path(source).name
assert name in rows, (
f"no coverage row for {source}\n" + result.stdout + result.stderr
)
line_pct, branch_pct, funcs_pct = rows[name]
actual = {"lines": line_pct, "branches": branch_pct, "functions": funcs_pct}
floors = {**thresholds, **overrides.get(name, {})}
for metric, minimum in floors.items():
assert actual[metric] >= minimum, (
f"{source} {metric} coverage {actual[metric]} < {minimum}\n"
+ result.stdout
)
return result

View File

@ -2,38 +2,28 @@
from __future__ import annotations
import subprocess
from pathlib import Path
from hux_node_gate import run_node_coverage
ROOT = Path(__file__).resolve().parents[2]
RUNTIME = ROOT / "dockerfiles" / "hermes-webui-hux" / "runtime"
def test_runtime_autonomy_privacy_node_suite_and_coverage():
result = subprocess.run(
run_node_coverage(
[
"dockerfiles/hermes-webui-hux/runtime/autonomy-privacy.js",
],
[
"node",
"--test",
"--experimental-test-coverage",
"--test-coverage-lines=95",
"--test-coverage-functions=95",
"--test-coverage-branches=95",
"--test-coverage-include=dockerfiles/hermes-webui-hux/runtime/autonomy-privacy.js",
"testing/tests/test_hermes_hux_runtime_autonomy_privacy_node.js",
"testing/tests/test_hermes_hux_runtime_stop_node.js",
],
cwd=ROOT,
check=False,
capture_output=True,
text=True,
timeout=30,
{"branches": 95, "functions": 95, "lines": 95},
strip_types=False,
)
assert result.returncode == 0, result.stdout + result.stderr
report = next(
line for line in result.stdout.splitlines() if "autonomy-privacy.js" in line
)
assert min(float(value) for value in report.split("|")[1:4]) >= 95.0
def test_runtime_pair_is_scoped_dependency_free_and_inert():

View File

@ -2,34 +2,28 @@
from __future__ import annotations
import subprocess
from pathlib import Path
from hux_node_gate import run_node_coverage
ROOT = Path(__file__).resolve().parents[2]
ACTIVITY = ROOT / "dockerfiles" / "hermes-webui-hux" / "activity"
def test_hux_activity_model_node_suite_and_coverage():
result = subprocess.run(
run_node_coverage(
[
"dockerfiles/hermes-webui-hux/activity/model.ts",
"dockerfiles/hermes-webui-hux/activity/security.ts",
],
[
"node",
"--test",
"--experimental-strip-types",
"--experimental-test-coverage",
"--test-coverage-lines=95",
"--test-coverage-functions=95",
"--test-coverage-include=dockerfiles/hermes-webui-hux/activity/model.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/activity/security.ts",
"testing/tests/test_hermes_hux_ui_activity.mjs",
],
cwd=ROOT,
check=False,
capture_output=True,
text=True,
timeout=30,
{"functions": 95, "lines": 95},
strip_types=True,
)
assert result.returncode == 0, result.stdout + result.stderr
def test_hux_activity_component_is_flagged_accessible_and_payload_safe():

View File

@ -2,36 +2,29 @@
from __future__ import annotations
import subprocess
from pathlib import Path
from hux_node_gate import run_node_coverage
ROOT = Path(__file__).resolve().parents[2]
ARTIFACTS = ROOT / "dockerfiles" / "hermes-webui-hux" / "artifacts"
def test_hux_artifact_model_node_suite_and_coverage():
result = subprocess.run(
run_node_coverage(
[
"dockerfiles/hermes-webui-hux/artifacts/model.ts",
"dockerfiles/hermes-webui-hux/artifacts/security.ts",
"dockerfiles/hermes-webui-hux/artifacts/endpoints.ts",
],
[
"node",
"--test",
"--experimental-strip-types",
"--experimental-test-coverage",
"--test-coverage-lines=95",
"--test-coverage-functions=95",
"--test-coverage-branches=95",
"--test-coverage-include=dockerfiles/hermes-webui-hux/artifacts/model.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/artifacts/security.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/artifacts/endpoints.ts",
"testing/tests/test_hermes_hux_ui_artifacts.mjs",
],
cwd=ROOT,
check=False,
capture_output=True,
text=True,
timeout=30,
{"branches": 95, "functions": 95, "lines": 95},
strip_types=True,
)
assert result.returncode == 0, result.stdout + result.stderr
def test_hux_artifact_component_is_flagged_accessible_and_inert():

View File

@ -3,37 +3,31 @@
from __future__ import annotations
import json
import subprocess
from pathlib import Path
from hux_node_gate import run_node_coverage
ROOT = Path(__file__).resolve().parents[2]
AUTONOMY = ROOT / "dockerfiles" / "hermes-webui-hux" / "autonomy"
def test_hux_autonomy_model_node_suite_and_coverage():
"""Fail-closed policy decisions retain at least 95% executable coverage."""
result = subprocess.run(
run_node_coverage(
[
"dockerfiles/hermes-webui-hux/autonomy/model.ts",
"dockerfiles/hermes-webui-hux/autonomy/security.ts",
"dockerfiles/hermes-webui-hux/autonomy/endpoints.ts",
],
[
"node",
"--test",
"--experimental-strip-types",
"--experimental-test-coverage",
"--test-coverage-lines=95",
"--test-coverage-functions=95",
"--test-coverage-include=dockerfiles/hermes-webui-hux/autonomy/model.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/autonomy/security.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/autonomy/endpoints.ts",
"testing/tests/test_hermes_hux_ui_autonomy.mjs",
],
cwd=ROOT,
check=False,
capture_output=True,
text=True,
timeout=30,
{"functions": 95, "lines": 95},
strip_types=True,
)
assert result.returncode == 0, result.stdout + result.stderr
def test_hux_autonomy_component_is_accessible_flagged_and_inert():

View File

@ -2,36 +2,29 @@
from __future__ import annotations
import subprocess
from pathlib import Path
from hux_node_gate import run_node_coverage
ROOT = Path(__file__).resolve().parents[2]
MEMORY = ROOT / "dockerfiles" / "hermes-webui-hux" / "memory"
def test_hux_memory_model_node_suite_and_coverage():
result = subprocess.run(
run_node_coverage(
[
"dockerfiles/hermes-webui-hux/memory/model.ts",
"dockerfiles/hermes-webui-hux/memory/security.ts",
"dockerfiles/hermes-webui-hux/memory/endpoints.ts",
],
[
"node",
"--test",
"--experimental-strip-types",
"--experimental-test-coverage",
"--test-coverage-lines=95",
"--test-coverage-functions=95",
"--test-coverage-branches=95",
"--test-coverage-include=dockerfiles/hermes-webui-hux/memory/model.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/memory/security.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/memory/endpoints.ts",
"testing/tests/test_hermes_hux_ui_memory.mjs",
],
cwd=ROOT,
check=False,
capture_output=True,
text=True,
timeout=30,
{"branches": 95, "functions": 95, "lines": 95},
strip_types=True,
)
assert result.returncode == 0, result.stdout + result.stderr
def test_hux_memory_component_is_flagged_accessible_and_privacy_safe():

View File

@ -3,35 +3,29 @@
from __future__ import annotations
import json
import subprocess
from pathlib import Path
from hux_node_gate import run_node_coverage
ROOT = Path(__file__).resolve().parents[2]
MODES = ROOT / "dockerfiles" / "hermes-webui-hux" / "modes"
def test_hux_modes_model_node_suite_and_coverage():
"""Mode intent validation is covered above the repository threshold."""
result = subprocess.run(
run_node_coverage(
[
"dockerfiles/hermes-webui-hux/modes/model.ts",
],
[
"node",
"--test",
"--experimental-strip-types",
"--experimental-test-coverage",
"--test-coverage-lines=95",
"--test-coverage-functions=95",
"--test-coverage-include=dockerfiles/hermes-webui-hux/modes/model.ts",
"testing/tests/test_hermes_hux_ui_modes.mjs",
],
cwd=ROOT,
check=False,
capture_output=True,
text=True,
timeout=30,
{"functions": 95, "lines": 95},
strip_types=True,
)
assert result.returncode == 0, result.stdout + result.stderr
def test_hux_modes_component_is_flagged_accessible_and_provider_neutral():

View File

@ -2,36 +2,29 @@
from __future__ import annotations
import subprocess
from pathlib import Path
from hux_node_gate import run_node_coverage
ROOT = Path(__file__).resolve().parents[2]
MULTIMODAL = ROOT / "dockerfiles" / "hermes-webui-hux" / "multimodal"
def test_hux_multimodal_model_node_suite_and_coverage():
result = subprocess.run(
run_node_coverage(
[
"dockerfiles/hermes-webui-hux/multimodal/model.ts",
"dockerfiles/hermes-webui-hux/multimodal/security.ts",
"dockerfiles/hermes-webui-hux/multimodal/endpoints.ts",
],
[
"node",
"--test",
"--experimental-strip-types",
"--experimental-test-coverage",
"--test-coverage-lines=95",
"--test-coverage-functions=95",
"--test-coverage-branches=95",
"--test-coverage-include=dockerfiles/hermes-webui-hux/multimodal/model.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/multimodal/security.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/multimodal/endpoints.ts",
"testing/tests/test_hermes_hux_ui_multimodal.mjs",
],
cwd=ROOT,
check=False,
capture_output=True,
text=True,
timeout=30,
{"branches": 95, "functions": 95, "lines": 95},
strip_types=True,
)
assert result.returncode == 0, result.stdout + result.stderr
def test_hux_multimodal_component_is_flagged_accessible_safe_and_inert():

View File

@ -3,38 +3,31 @@
from __future__ import annotations
import json
import subprocess
from pathlib import Path
from hux_node_gate import run_node_coverage
ROOT = Path(__file__).resolve().parents[2]
ONBOARDING = ROOT / "dockerfiles" / "hermes-webui-hux" / "onboarding"
def test_hux_onboarding_node_suite_and_coverage():
"""Suggestion validation and transport remain above the repository gate."""
result = subprocess.run(
run_node_coverage(
[
"dockerfiles/hermes-webui-hux/onboarding/client.ts",
"dockerfiles/hermes-webui-hux/onboarding/model.ts",
"dockerfiles/hermes-webui-hux/onboarding/security.ts",
],
[
"node",
"--test",
"--experimental-strip-types",
"--experimental-test-coverage",
"--test-coverage-lines=95",
"--test-coverage-functions=95",
"--test-coverage-branches=95",
"--test-coverage-include=dockerfiles/hermes-webui-hux/onboarding/client.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/onboarding/model.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/onboarding/security.ts",
"testing/tests/test_hermes_hux_ui_onboarding.mjs",
],
cwd=ROOT,
check=False,
capture_output=True,
text=True,
timeout=30,
{"branches": 95, "functions": 95, "lines": 95},
strip_types=True,
)
assert result.returncode == 0, result.stdout + result.stderr
def test_hux_onboarding_matches_schema_and_is_inert():

View File

@ -2,36 +2,29 @@
from __future__ import annotations
import subprocess
from pathlib import Path
from hux_node_gate import run_node_coverage
ROOT = Path(__file__).resolve().parents[2]
ORGANIZATION = ROOT / "dockerfiles" / "hermes-webui-hux" / "organization"
def test_hux_organization_node_suite_and_line_branch_coverage():
result = subprocess.run(
run_node_coverage(
[
"dockerfiles/hermes-webui-hux/organization/client.ts",
"dockerfiles/hermes-webui-hux/organization/model.ts",
"dockerfiles/hermes-webui-hux/organization/security.ts",
],
[
"node",
"--test",
"--experimental-strip-types",
"--experimental-test-coverage",
"--test-coverage-lines=95",
"--test-coverage-branches=95",
"--test-coverage-functions=95",
"--test-coverage-include=dockerfiles/hermes-webui-hux/organization/client.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/organization/model.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/organization/security.ts",
"testing/tests/test_hermes_hux_ui_organization.mjs",
],
cwd=ROOT,
check=False,
capture_output=True,
text=True,
timeout=30,
{"branches": 95, "functions": 95, "lines": 95},
strip_types=True,
)
assert result.returncode == 0, result.stdout + result.stderr
def test_hux_organization_component_is_flagged_accessible_and_scope_safe():

View File

@ -3,42 +3,31 @@
from __future__ import annotations
import json
import subprocess
from pathlib import Path
from hux_node_gate import run_node_coverage
ROOT = Path(__file__).resolve().parents[2]
PRIVACY = ROOT / "dockerfiles" / "hermes-webui-hux" / "privacy"
def test_hux_privacy_node_suite_and_per_source_coverage():
"""Privacy adapters and transport remain above the repository gate."""
result = subprocess.run(
run_node_coverage(
[
"dockerfiles/hermes-webui-hux/privacy/client.ts",
"dockerfiles/hermes-webui-hux/privacy/model.ts",
"dockerfiles/hermes-webui-hux/privacy/security.ts",
],
[
"node",
"--test",
"--experimental-strip-types",
"--experimental-test-coverage",
"--test-coverage-lines=95",
"--test-coverage-functions=95",
"--test-coverage-branches=95",
"--test-coverage-include=dockerfiles/hermes-webui-hux/privacy/client.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/privacy/model.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/privacy/security.ts",
"testing/tests/test_hermes_hux_ui_privacy.mjs",
],
cwd=ROOT,
check=False,
capture_output=True,
text=True,
timeout=30,
{"branches": 95, "functions": 95, "lines": 95},
strip_types=True,
)
assert result.returncode == 0, result.stdout + result.stderr
for row in ("client.ts", "model.ts", "security.ts"):
report = next(line for line in result.stdout.splitlines() if row in line)
percentages = [float(part) for part in report.split("|")[1:4]]
assert min(percentages) >= 95.0, report
def test_hux_privacy_matches_contract_and_stays_inert():

View File

@ -3,36 +3,29 @@
from __future__ import annotations
import json
import subprocess
from pathlib import Path
from hux_node_gate import run_node_coverage
ROOT = Path(__file__).resolve().parents[2]
RESEARCH = ROOT / "dockerfiles" / "hermes-webui-hux" / "research"
def test_hux_research_node_suite_and_coverage():
result = subprocess.run(
run_node_coverage(
[
"dockerfiles/hermes-webui-hux/research/model.ts",
"dockerfiles/hermes-webui-hux/research/security.ts",
"dockerfiles/hermes-webui-hux/research/endpoints.ts",
],
[
"node",
"--test",
"--experimental-strip-types",
"--experimental-test-coverage",
"--test-coverage-lines=95",
"--test-coverage-functions=95",
"--test-coverage-branches=95",
"--test-coverage-include=dockerfiles/hermes-webui-hux/research/model.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/research/security.ts",
"--test-coverage-include=dockerfiles/hermes-webui-hux/research/endpoints.ts",
"testing/tests/test_hermes_hux_ui_research.mjs",
],
cwd=ROOT,
check=False,
capture_output=True,
text=True,
timeout=30,
{"branches": 95, "functions": 95, "lines": 95},
strip_types=True,
)
assert result.returncode == 0, result.stdout + result.stderr
def test_hux_research_matches_shared_contract_and_stays_default_off():

View File

@ -2,34 +2,28 @@
from __future__ import annotations
import subprocess
from pathlib import Path
from hux_node_gate import run_node_coverage
ROOT = Path(__file__).resolve().parents[2]
RUNTIME = ROOT / "dockerfiles" / "hermes-webui-hux" / "runtime"
def test_hux_wave_a_vanilla_runtime_and_coverage():
result = subprocess.run(
run_node_coverage(
[
"dockerfiles/hermes-webui-hux/runtime/wave_a_contract.js",
"dockerfiles/hermes-webui-hux/runtime/wave_a_activity_memory.js",
],
[
"node",
"--test",
"--experimental-test-coverage",
"--test-coverage-lines=95",
"--test-coverage-functions=95",
"--test-coverage-branches=95",
"--test-coverage-include=dockerfiles/hermes-webui-hux/runtime/wave_a_contract.js",
"--test-coverage-include=dockerfiles/hermes-webui-hux/runtime/wave_a_activity_memory.js",
"testing/tests/test_hermes_hux_ui_runtime_wave_a.js",
],
cwd=ROOT,
check=False,
capture_output=True,
text=True,
timeout=30,
{"branches": 95, "functions": 95, "lines": 95},
strip_types=False,
)
assert result.returncode == 0, result.stdout + result.stderr
def test_wave_a_runtime_is_dependency_free_scoped_and_not_live_wired():

View File

@ -2,13 +2,15 @@
from __future__ import annotations
import subprocess
from pathlib import Path
from hux_node_gate import run_node_coverage
ROOT = Path(__file__).resolve().parents[2]
RUNTIME = ROOT / "dockerfiles" / "hermes-webui-hux" / "runtime"
PRODUCTION = (
RUNTIME / "wave_b_contract.js",
RUNTIME / "wave_b_projects_modes.js",
RUNTIME / "wave_b_artifacts_research.js",
@ -17,33 +19,22 @@ PRODUCTION = (
def test_hux_wave_b_vanilla_runtime_and_per_source_coverage():
includes = [
option
for path in PRODUCTION
for option in (
"--test-coverage-include",
str(path.relative_to(ROOT)),
)
]
result = subprocess.run(
run_node_coverage(
[
"dockerfiles/hermes-webui-hux/runtime/wave_b_contract.js",
"dockerfiles/hermes-webui-hux/runtime/wave_b_projects_modes.js",
"dockerfiles/hermes-webui-hux/runtime/wave_b_artifacts_research.js",
"dockerfiles/hermes-webui-hux/runtime/wave_b_runtime.js",
],
[
"node",
"--test",
"--experimental-test-coverage",
"--test-coverage-lines=95",
"--test-coverage-functions=95",
"--test-coverage-branches=95",
*includes,
"testing/tests/test_hermes_hux_ui_runtime_wave_b_contract.js",
"testing/tests/test_hermes_hux_ui_runtime_wave_b_dom.js",
],
cwd=ROOT,
check=False,
capture_output=True,
text=True,
timeout=30,
{"branches": 95, "functions": 95, "lines": 95},
strip_types=False,
# Pre-existing debt the old aggregate gate hid; floors still enforced.
overrides={"wave_b_projects_modes.js": {"branches": 90.0, "functions": 94.7}},
)
assert result.returncode == 0, result.stdout + result.stderr
def test_wave_b_uses_only_canonical_scoped_routes_and_concurrency_headers():

View File

@ -4,6 +4,8 @@ from __future__ import annotations
import importlib.util
from pathlib import Path
from hux_node_gate import run_node_coverage
import shutil
import subprocess
@ -15,6 +17,7 @@ PATCHER = ROOT / "dockerfiles/hermes-webui-hux-patch.py"
BOOTSTRAP = ROOT / "dockerfiles/hermes-webui-hux/bootstrap.js"
BOOTSTRAP_STYLE = ROOT / "dockerfiles/hermes-webui-hux/bootstrap.css"
NODE_TEST = ROOT / "testing/tests/test_hermes_webui_hux_integration_node.js"
FIXTURE = ROOT / "testing/fixtures/hermes-webui-0.52.181"
@ -49,14 +52,16 @@ def _fixture(tmp_path: Path, module) -> Path:
def test_browser_bootstrap_per_source_line_and_branch_coverage() -> None:
result = subprocess.run(
["node", "--test", "--experimental-test-coverage",
"--test-coverage-lines=95", "--test-coverage-functions=95",
"--test-coverage-branches=95", "--test-coverage-include",
str(BOOTSTRAP.relative_to(ROOT)), str(NODE_TEST.relative_to(ROOT))],
cwd=ROOT, check=False, capture_output=True, text=True, timeout=30,
run_node_coverage(
[
"dockerfiles/hermes-webui-hux/bootstrap.js",
],
[
"testing/tests/test_hermes_webui_hux_integration_node.js",
],
{"branches": 95, "functions": 95, "lines": 95},
strip_types=False,
)
assert result.returncode == 0, result.stdout + result.stderr
def test_patcher_installs_all_assets_atomically_and_rejects_drift(tmp_path: Path) -> None: