"""The `docs/` measurement scripts are contract consumers, and nothing pinned them.
`docs/fp-sweep.py` and `docs/rawhtml-census.py` produce the numbers published in
`docs/LIMITATIONS.md` and the README. Both reach past the public API into private
module state — `active_content._ACTIVE_TAGS`, `calibration.RISK_RANK` — so a rename
inside `src/` breaks them while this suite stays green. The breakage then surfaces
at the worst possible moment: the next time someone tries to re-measure a published
claim, months later, with no memory of what the script was supposed to import.
WHAT THIS FILE DELIBERATELY DOES NOT DO. The corpora these scripts consume live
outside this repo, in private consumer repos (`docs/CONSUMER-MAP.local.md`), so no
test here can run either script end to end, and building a stand-in corpus would
just pin a fiction. The contract under test is therefore narrower and honest:
1. every name the scripts import still exists with the shape they use;
2. the in-process patch point still moves the gate (a census that patched a dead
symbol would print six identical rows and read as a finding, not a failure);
3. the census's `PRODUCTION` row still equals its `A + base-url` candidate, which
the script's own docstring calls the drift alarm for every number it prints;
4. the argument-less invocation still refuses rather than measuring nothing.
"""
from __future__ import annotations
import importlib.util
import subprocess
import sys
from pathlib import Path
import pytest
from llm_ingestion_guard import Disposition, PRESET_USER_UPLOAD, Risk, screen_output
from llm_ingestion_guard import active_content as ac
_DOCS = Path(__file__).resolve().parent.parent / "docs"
def _load(filename: str):
"""Import a hyphenated script from `docs/` under a module name Python allows."""
path = _DOCS / filename
name = f"_docs_{path.stem.replace('-', '_')}"
spec = importlib.util.spec_from_file_location(name, path)
assert spec and spec.loader, f"cannot load {path}"
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
return module
# Import at collection time on purpose: a rename in `src/` that breaks either
# script's import list fails the whole file loudly instead of one quiet test.
fp_sweep = _load("fp-sweep.py")
census = _load("rawhtml-census.py")
# --- docs/fp-sweep.py --------------------------------------------------------
def test_fp_sweep_reaches_calibration_risk_rank_for_every_risk():
# `check_metric_is_a_risk_statement` indexes RISK_RANK by `Risk(...).value`.
# A risk tier added to the enum without a rank would raise KeyError mid-sweep,
# after the corpus had already been read.
from llm_ingestion_guard.calibration import RISK_RANK
assert {r.value for r in Risk} <= set(RISK_RANK)
assert RISK_RANK is fp_sweep.RISK_RANK
def test_fp_sweep_metric_guard_accepts_the_shipped_action_map():
fp_sweep.check_metric_is_a_risk_statement() # must not raise
def test_fp_sweep_metric_guard_is_not_vacuous():
# The guard exists to catch a re-mapped action map silently changing what the
# published number *claims*. If it cannot fail, it protects nothing — so make
# it fail, here, on a map where ELEVATED has become benign.
remapped = dict(fp_sweep.DEFAULT_ACTION_MAP)
remapped[Risk.ELEVATED] = fp_sweep.BENIGN
original = fp_sweep.DEFAULT_ACTION_MAP
fp_sweep.DEFAULT_ACTION_MAP = remapped
try:
with pytest.raises(SystemExit, match="metric invalid"):
fp_sweep.check_metric_is_a_risk_statement()
finally:
fp_sweep.DEFAULT_ACTION_MAP = original
def test_fp_sweep_documents_honours_extension_hidden_and_include(tmp_path):
(tmp_path / "keep.md").write_text("a", encoding="utf-8")
(tmp_path / "skip.rst").write_text("a", encoding="utf-8")
(tmp_path / ".hidden").mkdir()
(tmp_path / ".hidden" / "buried.md").write_text("a", encoding="utf-8")
(tmp_path / "sub").mkdir()
(tmp_path / "sub" / "nested.md").write_text("a", encoding="utf-8")
names = [p.name for p in fp_sweep.documents(tmp_path, (".md",))]
assert names == ["keep.md", "nested.md"], "extension or hidden-path filter moved"
scoped = fp_sweep.documents(tmp_path, (".md",), include="/sub/")
assert [p.name for p in scoped] == ["nested.md"]
def test_fp_sweep_measure_reads_the_result_fields_it_publishes(tmp_path):
# Two files, not a corpus: this pins the *shape* the script consumes — that
# `screen_output` still returns `.disposition` and `.assessment`, that empty
# files are excluded from the denominator, and that a non-WARN document is
# attributed to the labels at its worst severity rather than to all of them.
(tmp_path / "benign.md").write_text("A plain note about deployment.", encoding="utf-8")
(tmp_path / "empty.md").write_text(" \n", encoding="utf-8")
(tmp_path / "active.md").write_text(
'Read more ', encoding="utf-8"
)
m = fp_sweep.measure("probe", tmp_path, (".md",))
assert m["n"] == 2, "empty documents must not enter the denominator"
assert m["empty"] == 1
assert m["non_warn"] == m["n"] - m["dispositions"][fp_sweep.BENIGN.value]
assert m["non_warn"] >= 1, "the active-content document should not be waved through"
assert sum(m["assessments"].values()) == m["n"]
assert sum(m["trusted"].values()) == m["n"]
assert m["drivers"], "a non-WARN document must be attributed to a driver label"
assert all(isinstance(k, str) for k in m["drivers"])
fp_sweep.report(m) # the reporter reads every key above; a rename crashes here
# --- docs/rawhtml-census.py --------------------------------------------------
def test_census_private_active_content_names_still_exist():
# Each of these is reached by name from the census. They are private, so
# nothing else in the suite would notice them being renamed.
assert isinstance(ac._ACTIVE_TAGS, frozenset) and "base" in ac._ACTIVE_TAGS
assert ac._EVENT_ATTR_RE.search(' onclick="x()"')
assert ac._URL_ATTR_RE.search(' href="/x"')
assert ac._has_external_target("//evil.example") is True
assert ac._has_external_target("/relative") is False
assert ac._url_attr_is_external(' href="//evil.example"') is True
assert ac._url_attr_is_external(' href="/relative"') is False
assert callable(ac.is_active_tag)
assert callable(ac.active_tag_class)
# 0.7.0's two name sets, both reached by name from the census's `_variant`.
assert "a" in ac._LINK_TAGS and "img" not in ac._LINK_TAGS
assert {"a", "frame", "img"} <= ac._URL_AFFORDANCE_TAGS
assert "script" not in ac._URL_AFFORDANCE_TAGS, "an execute-class tag needs no URL"
def test_census_masking_pipeline_matches_the_scanner_symbols():
# `masked_text` reproduces `scan_active_content`'s masking by name. It blanks
# with equal-length spaces so later offsets stay meaningful; a substitution
# that changed length would silently move every tag position it reports.
text = "See [docs](https://example.com/a) and ."
masked = census.masked_text(text)
assert len(masked) == len(text)
assert "https://example.com/a" not in masked
assert not any(m.group("name") for m in ac.HTML_TAG_RE.finditer(masked))
def test_census_html_tag_regex_still_exposes_the_groups_it_reads():
m = ac.HTML_TAG_RE.search('