Files
obsidian-wiki/tests/test_upstream_compat.py
T
Codex e943bb329e
setup / install-leaves-tree-clean (push) Waiting to run
feat(wiki): port upstream knowledge quality improvements
Selectively port source coverage and redirect lint, ignore-aware provenance, staging and trust checks from upstream 055d720. Preserve local archive metadata and adapt source-date and defuddle skill guidance.

Keep the existing skills directory, file index and MCP interfaces. Record per-module upstream baselines and exclusions without migrating vault data or changing services.

Validation: 625 tests passed; git diff --check passed.
2026-11-07 21:49:41 +00:00

114 lines
5.1 KiB
Python

"""Local data contracts that the selected upstream port must preserve."""
import json
from pathlib import Path
import pytest
from obsidian_wiki.cli import main
from obsidian_wiki.graph_analysis import parse_vault_graph
from obsidian_wiki.lint import lint_vault
from obsidian_wiki.provenance import archive_wikilink_relpath, resolve_source_key
from obsidian_wiki.vault import iter_md
@pytest.mark.parametrize("shape", ["dict", "list"])
def test_cache_cli_preserves_curated_metadata_and_page_associations(tmp_path, shape):
source = tmp_path / "source.pdf"
source.write_bytes(b"new revision")
entry = {
"path": "source.pdf", "pages_produced": ["concepts/a.md"],
"archive_path": "_sources/archive/a.pdf", "content_hash": "old",
"bjd_part_numbers": ["BJD-123"], "competitor_part_numbers": ["OTHER-456"],
"series": ["WTB"], "pin_count": [6], "pitch": [2.0],
"catalog_paths": ["_sources/catalog/a.md"], "visibility": "internal",
}
sources = {"source.pdf": entry} if shape == "dict" else [entry]
manifest = tmp_path / ".manifest.json"
manifest.write_text(json.dumps({"sources": sources, "local_revision": 7}))
assert main(["cache-update", str(tmp_path), str(source)]) == 0
updated = json.loads(manifest.read_text())
actual = updated["sources"]["source.pdf"] if shape == "dict" else updated["sources"][0]
assert updated["local_revision"] == 7
for key, value in entry.items():
if key != "content_hash":
assert actual[key] == value
assert actual["content_hash"] != "old"
def test_local_sources_are_citable_but_not_knowledge_pages(tmp_path):
refs = ["_sources/archive/doc.pdf", "_sources/catalog/products.md"]
for rel in [*refs, "_sources/archive/raw.md", "_meta/taxonomy.md", "_inbox/draft.md", "_bot_exports/copy.md"]:
p = tmp_path / rel
p.parent.mkdir(parents=True, exist_ok=True)
p.write_text("Raw source, not a knowledge page")
page = tmp_path / "concepts" / "a.md"
page.parent.mkdir()
page.write_text(
"---\ntitle: A\ncategory: concepts\ntags: [a]\nsources: []\n"
"created: 2026-10-01\nupdated: 2026-10-01\nsummary: Example\n---\n"
+ " ".join(f"[[{rel}]]" for rel in refs)
)
assert iter_md(tmp_path) == [page]
graph, _ = parse_vault_graph(tmp_path)
assert set(graph) == {"a"}
assert graph["a"] == []
findings = lint_vault(tmp_path, require_trust_ledger=False)["findings"]
assert findings["broken_links"] == []
assert resolve_source_key(tmp_path, refs[0]) == refs[0]
assert archive_wikilink_relpath(tmp_path, refs[0] + "|PDF") == refs[0]
assert archive_wikilink_relpath(tmp_path, "_sources/catalog/products") == refs[1]
@pytest.mark.parametrize("target", ["../_sources/archive/a.pdf", "/tmp/_sources/archive/a.pdf"])
def test_local_source_resolution_refuses_paths_outside_vault(tmp_path, target):
assert resolve_source_key(tmp_path, target) is None
assert archive_wikilink_relpath(tmp_path, target) is None
def test_staging_cli_conflict_keeps_reviewed_and_live_files(tmp_path, capsys):
staged = tmp_path / "_staging/concepts/a.md"
staged.parent.mkdir(parents=True)
staged.write_text("Reviewed proposal")
assert main(["staging", "list", "--vault", str(tmp_path), "--json"]) == 0
inventory = json.loads(capsys.readouterr().out)
# The runtime schema is asserted here so the skill can pin listed revisions.
entry = inventory[0]
live = tmp_path / "concepts/a.md"
live.parent.mkdir()
live.write_text("Concurrent live update")
assert main([
"staging", "promote", entry["staged_path"], "--vault", str(tmp_path),
"--expect-staged", entry["staged_revision"], "--expect-new",
]) == 9
assert live.read_text() == "Concurrent live update"
assert staged.read_text() == "Reviewed proposal"
@pytest.mark.parametrize("shape", ["dict", "list"])
def test_source_coverage_accepts_local_archive_metadata_and_preserves_manifest(tmp_path, shape):
page = tmp_path / "concepts/a.md"
page.parent.mkdir()
page.write_text("---\ntitle: A\n---\n")
entries = [
{"path": "original.pdf", "archive_path": "_sources/archive/a.pdf",
"pages_produced": ["concepts/a.md"], "bjd_part_numbers": ["BJD-123"]},
{"path": "lost.pdf", "pages_updated": ["concepts/deleted.md"]},
{"path": "legacy.pdf", "pages_created": 2},
]
manifest = tmp_path / ".manifest.json"
manifest.write_text(json.dumps({"sources": {e["path"]: e for e in entries} if shape == "dict" else entries}))
before = manifest.read_bytes()
report = lint_vault(tmp_path, require_trust_ledger=False)
assert report["findings"]["uncovered_sources"] == [
{"source": "lost.pdf", "issue": "pages_missing", "missing": ["concepts/deleted.md"]}
]
assert manifest.read_bytes() == before
def test_lint_cli_exposes_source_coverage_in_json_and_strict_exit(tmp_path, capsys):
(tmp_path / ".manifest.json").write_text(json.dumps({"sources": {"lost.pdf": {"pages_produced": []}}}))
assert main(["lint", str(tmp_path), "--json", "--strict"]) == 1
report = json.loads(capsys.readouterr().out)
assert report["stats"]["findings"]["uncovered_sources"] == 1