"""Tests for lint.py backlink + wikilink behavior. Covers audit assertion #6 (aliased wikilinks pass lint cleanly) and the C9 fix (qa/sources don't generate backlink noise on concepts). """ from __future__ import annotations from pathlib import Path import pytest @pytest.fixture def kb_factory(tmp_path: Path, monkeypatch): """Return a function that sets up a mini knowledge base under tmp_path. Yields the knowledge dir; all paths are monkeypatched so lint.py reads from the temp KB. """ kb = tmp_path / "knowledge" kb.mkdir() (kb / "concepts").mkdir() (kb / "connections").mkdir() (kb / "qa").mkdir() # Monkeypatch both lint.py's view and utils.py's view (lint uses utils). import config import utils import lint as lint_mod monkeypatch.setattr(config, "KNOWLEDGE_DIR", kb) monkeypatch.setattr(config, "CONCEPTS_DIR", kb / "concepts") monkeypatch.setattr(config, "CONNECTIONS_DIR", kb / "connections") monkeypatch.setattr(config, "QA_DIR", kb / "qa") monkeypatch.setattr(utils, "KNOWLEDGE_DIR", kb) monkeypatch.setattr(utils, "CONCEPTS_DIR", kb / "concepts") monkeypatch.setattr(utils, "CONNECTIONS_DIR", kb / "connections") monkeypatch.setattr(utils, "QA_DIR", kb / "qa") monkeypatch.setattr(lint_mod, "KNOWLEDGE_DIR", kb) return kb def test_aliased_wikilinks_are_not_broken(kb_factory: Path) -> None: """[[concepts/foo|Display]] pointing to an existing article = not broken.""" from lint import check_broken_links (kb_factory / "concepts" / "foo.md").write_text( "---\ntitle: foo\n---\nLinks to [[concepts/bar]].\n" ) (kb_factory / "concepts" / "bar.md").write_text( "---\ntitle: bar\n---\nRefers back to [[concepts/foo|The Foo Article]].\n" ) issues = check_broken_links() assert issues == [], f"unexpected broken-link issues: {issues}" def test_aliased_backlink_counts_as_inbound(kb_factory: Path) -> None: """An aliased link to A counts as a valid inbound link on A.""" from lint import check_missing_backlinks (kb_factory / "concepts" / "foo.md").write_text( "---\ntitle: foo\n---\nLinks to [[concepts/bar]].\n" ) (kb_factory / "concepts" / "bar.md").write_text( "---\ntitle: bar\n---\nAliased backlink: [[concepts/foo|Foo Display]].\n" ) issues = check_missing_backlinks() # foo links to bar, bar links (aliased) to foo → symmetric → zero. assert issues == [], f"unexpected backlink issues: {issues}" def test_qa_sources_dont_trigger_backlink_suggestions(kb_factory: Path) -> None: """QA articles referencing concepts should NOT demand a reciprocal link.""" from lint import check_missing_backlinks (kb_factory / "concepts" / "foo.md").write_text( "---\ntitle: foo\n---\nJust content.\n" ) # Q&A article cites foo; should not produce a backlink suggestion. (kb_factory / "qa" / "how-do-i-use-foo.md").write_text( "---\ntitle: how-do-i-use-foo\n---\nConsulted [[concepts/foo]].\n" ) issues = check_missing_backlinks() # The QA source is skipped → no missing-backlink suggestion about it. foo_to_qa_suggestions = [ i for i in issues if "concepts/foo" in i["detail"] and "qa/how-do-i-use-foo" in i["detail"] ] assert foo_to_qa_suggestions == [], ( f"qa article should not trigger backlink suggestion: {foo_to_qa_suggestions}" ) def test_concept_to_concept_backlinks_still_checked(kb_factory: Path) -> None: """The C9 skip is narrow — concept-to-concept asymmetry still reported.""" from lint import check_missing_backlinks # foo links to bar; bar does NOT link back. (kb_factory / "concepts" / "foo.md").write_text( "---\ntitle: foo\n---\nLinks to [[concepts/bar]].\n" ) (kb_factory / "concepts" / "bar.md").write_text( "---\ntitle: bar\n---\nNo links.\n" ) issues = check_missing_backlinks() assert any( "concepts/foo" in i["detail"] and "concepts/bar" in i["detail"] for i in issues ), f"expected concept-to-concept asymmetry report; got {issues}"