"""Pattern-library coverage (#2692) — the extractor that mirrors the hook, the shape/record matcher, and the end-to-end measurement on real Postgres. The extractor here and the hook's awk program (scribe_prior_art.sh ARM 1) must agree on what counts as "a definition" — the metric and the write-path backstop are two views of the same doctrine. The EXTRACTION_VECTORS below deliberately reuse the definitions test_write_path_trigger stages for the hook; extending one detector means extending both, and this comment is the tripwire. """ import io import json import tarfile import pytest import pytest_asyncio from scribe.services.coverage import ( ArchiveScan, class_references, coverage_line, scan_archive, extract_shapes, largest_gaps, scannable, shapes_from_archive, ) from scribe.services.shape_ledger import location_covers from tests.helpers import ensure_user # --- unit: the definition extractor (shared vectors with the hook) ----------- EXTRACTION_VECTORS = [ # (id, source text, expected (kind, name) list) ("python", "def make_app():\n pass\nclass Config:\n pass\n", [("sym", "make_app"), ("sym", "Config")]), ("python-dunder-skip", "class C:\n def __init__(self):\n pass\n", [("sym", "C")]), ("go-func", "func Resolve(x int) error {\n\treturn nil\n}\n", [("sym", "Resolve")]), ("go-method", "func (s *Scanner) Resolve(x int) error {\n\treturn nil\n}\n", [("sym", "Resolve")]), ("kotlin-fun", "suspend fun refreshQueue(id: Long) {\n}\n", [("sym", "refreshQueue")]), ("rust-fn", "pub async fn fetch_all() -> u32 {\n 0\n}\n", [("sym", "fetch_all")]), ("go-type", "type ForgeAdapter struct {\n\tname string\n}\n", [("sym", "ForgeAdapter")]), ("rust-pub-crate", "pub(crate) struct Widget {}\n", [("sym", "Widget")]), ("js-export-default", "export default function App() {}\n", [("sym", "App")]), ("js-arrow", "const useThing = (id) => id;\nlet fetcher = async () => 0;\n", [("sym", "useThing"), ("sym", "fetcher")]), # Every selector line in a group counts — .btn-ghost, and .btn-text { } # both announce a class, exactly as the hook's awk sees them. ("css", ".btn-primary {\n color: red;\n}\n.btn-ghost,\n.btn-text { }\n", [("css", "btn-primary"), ("css", "btn-ghost"), ("css", "btn-text")]), # Call sites, imports, and impl blocks are NOT definitions — matching # them would drown the metric exactly as it would drown the hook. ("non-definitions", "make_app()\nimpl Widget {\nreturn fetch_all\nimport os\nx = 1\n", []), ("dedup-within-file", "def f():\n pass\ndef f():\n pass\n", [("sym", "f")]), ] @pytest.mark.parametrize( ("text", "expected"), [(t, e) for _i, t, e in EXTRACTION_VECTORS], ids=[i for i, _t, _e in EXTRACTION_VECTORS], ) def test_extractor_agrees_with_the_hook_on_what_defines(text, expected): assert extract_shapes(text) == expected def test_scannable_gates_prose_vendored_and_sourcemaps(): assert scannable("src/app.py") assert scannable("web/button.css") assert scannable(".gitea/workflows/ci.yml") # config IS worth recording assert not scannable("README.md") assert not scannable("dist/bundle.js.map") assert not scannable("node_modules/x/index.js") assert not scannable("web/node_modules/y/util.ts") # A FILE named like a skip-dir is not a directory hit. assert scannable("src/vendor.py") # --- unit: reading shapes out of a forge tarball ----------------------------- def _tarball(files: dict[str, bytes], top: str = "widget") -> bytes: buf = io.BytesIO() with tarfile.open(fileobj=buf, mode="w:gz") as tar: for path, data in files.items(): info = tarfile.TarInfo(f"{top}/{path}") info.size = len(data) tar.addfile(info, io.BytesIO(data)) return buf.getvalue() TREE = { "src/app.py": b"def make_app():\n pass\nclass Config:\n def __init__(self):\n pass\n", "src/util.py": b"def helper():\n pass\n", "web/button.css": b".btn {\n color: red;\n}\n", "README.md": b"def not_code(): pass\n", "node_modules/x/index.js": b"function vendored() {}\n", "data.bin": b"\xff\xfe\x00\x01", } # What TREE holds once the gates run: 4 shapes, none from the skipped files. TREE_SHAPES = [ ("src/app.py", "sym", "make_app"), ("src/app.py", "sym", "Config"), ("src/util.py", "sym", "helper"), ("web/button.css", "css", "btn"), ] def test_shapes_from_archive_strips_the_wrapper_and_gates_files(): assert shapes_from_archive(_tarball(TREE)) == TREE_SHAPES # --- unit: template class references — the CSS consumer map (milestone 302) -- def test_class_references_reads_vue_static_and_dynamic_forms_only(): """A template's class attributes name the classes it consumes: the static `class=`, the Vue dynamic object/array/ternary forms (string literals and bare object keys), never a selector in \n" ) assert class_references("a/B.vue", vue) == { "card": 2, "card--wide": 1, "active": 1, "is-error": 1, "pill": 1, "pill-on": 1, "pill-off": 1, "closed": 1, } def test_class_references_reads_framework_transition_names(): """A transition `name=` is a class reference: the framework applies `.toast-enter-active` and friends at runtime, so a stylesheet that defines them is consumed even though no template spells one out (#2970). Every spelling of the tag counts; a bound `:name` stays unknowable.""" refs = class_references( "a/T.vue", '
', ) for suffix in ("-enter-from", "-enter-active", "-leave-to", "-move"): assert refs["toast" + suffix] == 1, suffix assert refs["toast-item"] == 1 # the static attribute still counts assert "peek-enter-active" in class_references("a/T.vue", '') assert "g-move" in class_references("a/T.vue", '') # React's CSSTransition names the same idea with a different suffix set react = class_references("a/T.jsx", '') assert {"fade-enter-active", "fade-exit-active", "fade-exit-done"} <= set(react) # A bound name is a variable, not a name we can read assert class_references("a/T.vue", '') == {} def test_class_references_reads_a_concatenated_name_as_a_prefix(): """`status-${s}` cannot be resolved to one class, but its static head is real information: it is emitted as the prefix reference `status-*` so the rows it could have built are not reported unused (#2970). A head too short to mean anything, or a bare separator, says nothing.""" vue = ( '
' "" '' ) assert class_references("a/P.vue", vue) == {"status-*": 1, "pri-*": 1} # a server template interpolating into the middle of a name, same reading assert class_references("t/p.html", '') == {"card-*": 1} # an ordinary name never picks up the marker assert class_references("a/P.vue", '