Selectively port source hashing, locked manifest/memory writes, path-aware link lint and graph analysis, and revision-checked staging from upstream 4f457e154653756b04802d5aeb25c20f42b4770c. Record source hashes and exclusions. Preserve local archive/catalog citations, curated manifest metadata, config location, file-index and MCP behavior. Wire maintenance guidance into four skills without migrating vault contents or changing running services. Validation: 609 tests passed, including existing MCP/file-asset regressions and imported upstream maintenance tests. Read-only vault status left shared metadata hashes unchanged. Wiki MCP remains inactive; orders MCP is active.
86 lines
3.8 KiB
Python
86 lines
3.8 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"
|