Files
zeekay d696a015aa fix(brand): one name for the script language, and a guard that keeps it
The rename runs on the way in and nothing checked it afterwards, so paths and
content drifted apart in three places, and each one broke something quietly.

The script language had three names at once.
Files on disk carried `.iql`/`.iqle`, the source matchers said `.script`, and
the bytecode matchers said `.hoge`, so the test harness globbed zero files and
the STL compiler regenerated an empty table.
It is `script` now, in one place: `.script` source, `.scriptc` bytecode,
`bin/script` to run one and `bin/scriptc` to compile one.

190 TypeScript specifiers named files that had been renamed away, which is the
whole CDP consumer graph -- the event worker, not the web boot.
`--check` only ever read Python imports; it reads both module systems now, and
repoints both.

`-hog-` became `-mascot-` in paths while content said `script`, so the code
review product's skill directories stopped matching the prefix that discovers
them and it ran with no perspectives at all.

PATH_REGEX is CONTENT_REGEX -- the same list, not a copy -- so a rule can no
longer land in one and miss the other, and the four disagreeing `hog` path
rules collapse into the one word rule content already had.

`HOGLI_*` becomes `INSIGHTSCLI_*` at every reader and writer, with no alias: an
env var read under two names is two names.
The seven casings of `hogql` and five of `hogvm` collapse into one rule per
prefix casing, which leaves the suffix alone because `QL` is SQL's and `VM` is
a virtual machine's.

`--check` now refuses every retired spelling, in content and in paths, and
draws the line the rules draw: `insights_hogfunction` is a Postgres table so it
is left alone, and a migration filename is its identity in `django_migrations`
so it is frozen rather than failed.
Third-party names we cannot rename are reported as pending, never exempted.
2026-08-08 18:38:11 -07:00

210 lines
9.6 KiB
Python

from __future__ import annotations
import json
import pytest
from unittest.mock import MagicMock, patch
from click.testing import CliRunner
from insightscli.cli import cli
from insightscli_commands.ci_preflight import DIFF_CHECKS, _pnpm_workspace_root, _run_workspace_scoped, _staleness_risks
runner = CliRunner()
class TestKillSwitch:
@patch("insightscli_commands.ci_preflight._emit_telemetry")
@patch("insightscli_commands.ci_preflight.changed_files")
def test_short_circuits_before_any_git_work(self, mock_changed: MagicMock, mock_emit: MagicMock) -> None:
result = runner.invoke(cli, ["ci:preflight"], env={"INSIGHTSCLI_PREFLIGHT_DISABLED": "1"})
assert result.exit_code == 0
assert "disabled" in result.output
mock_changed.assert_not_called()
assert mock_emit.call_args[0][0]["mode"] == "disabled"
@patch("insightscli_commands.ci_preflight._emit_telemetry")
@patch("insightscli_commands.ci_preflight.changed_files")
def test_json_output_stays_parseable(self, mock_changed: MagicMock, mock_emit: MagicMock) -> None:
result = runner.invoke(cli, ["ci:preflight", "--json"], env={"INSIGHTSCLI_PREFLIGHT_DISABLED": "1"})
assert result.exit_code == 0
assert json.loads(result.output)["mode"] == "disabled"
mock_changed.assert_not_called()
class TestStrictAndFixContracts:
@patch("insightscli_commands.ci_preflight._emit_telemetry")
@patch("insightscli_commands.ci_preflight._staleness", return_value=("pass", "even with master", {}))
@patch("insightscli_commands.ci_preflight._fetch_master")
@patch("insightscli_commands.ci_preflight.changed_files", return_value=["insights/api/does_not_exist.py"])
def test_strict_never_blocks_on_advisory(
self, mock_changed: MagicMock, mock_fetch: MagicMock, mock_stale: MagicMock, mock_emit: MagicMock
) -> None:
result = runner.invoke(cli, ["ci:preflight", "--strict"])
assert "build:openapi" in result.output
assert result.exit_code == 0
@patch("insightscli_commands.ci_preflight._emit_telemetry")
@patch("insightscli_commands.ci_preflight._staleness", return_value=("pass", "even with master", {}))
@patch("insightscli_commands.ci_preflight._fetch_master")
@patch("insightscli_commands.ci_preflight.shutil.which", return_value="/usr/bin/insightscli")
@patch("insightscli_commands.ci_preflight.subprocess.run")
@patch("insightscli_commands.ci_preflight.changed_files", return_value=[".github/workflows/ci-foo.yml"])
def test_strict_exits_nonzero_on_failure(
self,
mock_changed: MagicMock,
mock_run: MagicMock,
mock_which: MagicMock,
mock_fetch: MagicMock,
mock_stale: MagicMock,
mock_emit: MagicMock,
) -> None:
mock_run.return_value = MagicMock(returncode=1, stdout="workflow convention violated", stderr="")
result = runner.invoke(cli, ["ci:preflight", "--strict"])
assert result.exit_code == 1
@patch("insightscli_commands.ci_preflight._emit_telemetry")
@patch("insightscli_commands.ci_preflight._capability_met", return_value=False)
@patch("insightscli_commands.ci_preflight._staleness", return_value=("pass", "even with master", {}))
@patch("insightscli_commands.ci_preflight._fetch_master")
@patch("insightscli_commands.ci_preflight.changed_files", return_value=["insights/api/does_not_exist.py"])
def test_fix_without_stack_still_advises_openapi(
self,
mock_changed: MagicMock,
mock_fetch: MagicMock,
mock_stale: MagicMock,
mock_capability: MagicMock,
mock_emit: MagicMock,
) -> None:
result = runner.invoke(cli, ["ci:preflight", "--fix"])
assert result.exit_code == 0
assert "run `insightscli build:openapi` and commit before pushing" in result.output
@patch("insightscli_commands.ci_preflight._emit_telemetry")
@patch("insightscli_commands.ci_preflight._staleness", return_value=("pass", "even with master", {}))
@patch("insightscli_commands.ci_preflight._fetch_master")
@patch("insightscli_commands.ci_preflight.subprocess.run")
@patch("insightscli_commands.ci_preflight.changed_files", return_value=["insights/api/does_not_exist.py"])
def test_type_check_names_the_mypy_command_without_running_it(
self,
mock_changed: MagicMock,
mock_run: MagicMock,
mock_fetch: MagicMock,
mock_stale: MagicMock,
mock_emit: MagicMock,
) -> None:
result = runner.invoke(cli, ["ci:preflight", "--strict"])
assert result.exit_code == 0
assert "uv run mypy --cache-fine-grained ." in result.output
# Giving this check a `verify` would tax every Python push with a repo-wide run.
assert not any("mypy" in call.args[0] for call in mock_run.call_args_list)
class TestStalenessRisks:
@pytest.mark.parametrize(
"branch_files,master_files,conflicts,expected_fragments",
[
(["insights/models/team.py"], ["frontend/src/lib/utils.tsx"], [], []),
(["insights/models/team.py"], ["frontend/src/lib/utils.tsx"], None, []),
(["a.py"], ["b.py"], ["insights/api/insight.py"], ["conflicts in 1 file"]),
(
["insights/migrations/0700_ours.py"],
["insights/migrations/0700_theirs.py"],
[],
["migrations added on both sides in insights/migrations"],
),
(["insights/api/ours.py"], ["insights/api/theirs.py"], [], ["master also changed build:openapi"]),
(["a.py"], [".github/workflows/ci-backend.yml"], [], ["CI workflows changed on master (1 file(s))"]),
],
)
def test_risk_derivation(
self,
branch_files: list[str],
master_files: list[str],
conflicts: list[str] | None,
expected_fragments: list[str],
) -> None:
risks = _staleness_risks(branch_files, master_files, conflicts)
assert len(risks) == len(expected_fragments)
for fragment, risk in zip(expected_fragments, risks):
assert fragment in risk
class TestWorkspaceScopedLockfile:
"""The lockfile check must validate each pnpm workspace against its own lockfile.
These hit the real repo layout, like TestDetectTestType in test_test_runner.py."""
@pytest.mark.parametrize(
"file_path,expected_workspace",
[
("package.json", "."),
("frontend/package.json", "."),
("nodejs/package.json", "."),
("products/desktop/package.json", "products/desktop"),
("products/desktop/pnpm-lock.yaml", "products/desktop"),
("products/desktop/packages/core/package.json", "products/desktop"),
# agent has a publish-only pnpm-lock.yaml but is a desktop workspace member
("products/desktop/packages/agent/package.json", "products/desktop"),
("tools/hedgebox-dummy/package.json", "tools/hedgebox-dummy"),
],
)
def test_workspace_root_resolution(self, file_path: str, expected_workspace: str) -> None:
assert _pnpm_workspace_root(file_path) == expected_workspace
@pytest.mark.parametrize(
"changed,expected_dirs",
[
(["products/desktop/package.json"], ["products/desktop"]),
(["package.json"], ["."]),
(["package.json", "products/desktop/packages/core/package.json"], [".", "products/desktop"]),
],
)
@patch("insightscli_commands.ci_preflight._workspace_install_present", return_value=True)
@patch("insightscli_commands.ci_preflight.shutil.which", return_value="/usr/bin/pnpm")
@patch("insightscli_commands.ci_preflight.subprocess.run")
def test_lockfile_runs_once_per_workspace(
self,
mock_run: MagicMock,
mock_which: MagicMock,
mock_install: MagicMock,
changed: list[str],
expected_dirs: list[str],
) -> None:
mock_run.return_value = MagicMock(returncode=0, stdout="", stderr="")
lockfile = next(chk for chk in DIFF_CHECKS if chk.key == "lockfile")
assert lockfile.workspace_scoped
lockfile.matched = changed
status, detail = _run_workspace_scoped(lockfile, do_fix=False)
assert status == "pass"
ran_dirs = [call.kwargs["cwd"] for call in mock_run.call_args_list]
from insightscli.manifest import REPO_ROOT
assert ran_dirs == [REPO_ROOT if d == "." else REPO_ROOT / d for d in expected_dirs]
@patch("insightscli_commands.ci_preflight._workspace_install_present", return_value=True)
@patch("insightscli_commands.ci_preflight.shutil.which", return_value="/usr/bin/pnpm")
@patch("insightscli_commands.ci_preflight.subprocess.run")
def test_lockfile_failure_names_the_workspace(
self, mock_run: MagicMock, mock_which: MagicMock, mock_install: MagicMock
) -> None:
mock_run.return_value = MagicMock(returncode=1, stdout="ERR_PNPM_OUTDATED_LOCKFILE", stderr="")
lockfile = next(chk for chk in DIFF_CHECKS if chk.key == "lockfile")
lockfile.matched = ["products/desktop/package.json"]
status, detail = _run_workspace_scoped(lockfile, do_fix=False)
assert status == "fail"
assert "products/desktop: ERR_PNPM_OUTDATED_LOCKFILE" in detail
@patch("insightscli_commands.ci_preflight._workspace_install_present", return_value=False)
def test_lockfile_skips_workspace_without_install(self, mock_install: MagicMock) -> None:
lockfile = next(chk for chk in DIFF_CHECKS if chk.key == "lockfile")
lockfile.matched = ["products/desktop/package.json"]
status, detail = _run_workspace_scoped(lockfile, do_fix=False)
assert status == "skipped"
assert "products/desktop: needs node" in detail