Some checks failed
CI / test (push) Has been cancelled
CI / notebooks-smoke (push) Has been cancelled
CI / lint (push) Has been cancelled
Deploy / notebooks (push) Has been cancelled
Deploy / zotero (push) Has been cancelled
Deploy / docs (push) Has been cancelled
Deploy / api (push) Has been cancelled
Deploy / llm (push) Has been cancelled
Deploy / mc (push) Has been cancelled
Deploy / report (push) Has been cancelled
Infra CI / mc (push) Has been cancelled
Infra CI / notebooks (push) Has been cancelled
Infra CI / zotero (push) Has been cancelled
Infra CI / docs (push) Has been cancelled
Infra CI / api (push) Has been cancelled
Infra CI / llm (push) Has been cancelled
611 lines
21 KiB
Python
611 lines
21 KiB
Python
"""Tests for bib.pincite — pinpoint citation system."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import textwrap
|
|
|
|
import pytest
|
|
|
|
from bib.pincite import (
|
|
Pincite,
|
|
_classify_locator,
|
|
build_citation_graph,
|
|
check_pincite_keys,
|
|
format_pincite_block,
|
|
inject_pincites_into_source,
|
|
list_pincites,
|
|
parse_pincites,
|
|
upsert_pincites,
|
|
)
|
|
from bib.store import Store
|
|
|
|
# ── Fixtures ─────────────────────────────────────────────────────────
|
|
|
|
|
|
@pytest.fixture
|
|
def store(tmp_path):
|
|
"""In-memory-like store using a temp database."""
|
|
db = tmp_path / "test_bib.sqlite"
|
|
s = Store(database=str(db))
|
|
return s
|
|
|
|
|
|
@pytest.fixture
|
|
def store_with_items(store):
|
|
"""Store with two items for pincite testing."""
|
|
from bib.item import Source
|
|
|
|
store.create(Source(key="JX46GQ9K", title="Tag vocabulary item"))
|
|
store.create(Source(key="9ASETKJ4", title="Nature citation standards"))
|
|
return store
|
|
|
|
|
|
# ── Parse ────────────────────────────────────────────────────────────
|
|
|
|
|
|
class TestParsePincites:
|
|
def test_basic(self):
|
|
doc = ":pincite:`JX46GQ9K p.14` -- NDC validation rules"
|
|
result = parse_pincites(doc, "mod.fn")
|
|
assert len(result) == 1
|
|
assert result[0].item_key == "JX46GQ9K"
|
|
assert result[0].locator == "p.14"
|
|
assert result[0].note == "NDC validation rules"
|
|
assert result[0].fn_path == "mod.fn"
|
|
|
|
def test_no_locator(self):
|
|
doc = ":pincite:`JX46GQ9K`"
|
|
result = parse_pincites(doc, "mod.fn")
|
|
assert len(result) == 1
|
|
assert result[0].locator == ""
|
|
|
|
def test_no_note(self):
|
|
doc = ":pincite:`JX46GQ9K p.14`"
|
|
result = parse_pincites(doc, "mod.fn")
|
|
assert len(result) == 1
|
|
assert result[0].note == ""
|
|
|
|
def test_section_locator(self):
|
|
doc = ":pincite:`9ASETKJ4 §3.2` — exclusion criteria"
|
|
result = parse_pincites(doc, "mod.fn")
|
|
assert result[0].locator == "§3.2"
|
|
assert result[0].locator_type == "section"
|
|
assert result[0].note == "exclusion criteria"
|
|
|
|
def test_cfr_locator(self):
|
|
doc = ":pincite:`ABC23456 42 CFR §414.22` -- payment rates"
|
|
result = parse_pincites(doc, "mod.fn")
|
|
assert result[0].locator == "42 CFR §414.22"
|
|
assert result[0].locator_type == "cfr"
|
|
|
|
def test_fr_locator(self):
|
|
doc = ":pincite:`DEF78923 90 FR 86252` -- final rule"
|
|
result = parse_pincites(doc, "mod.fn")
|
|
assert result[0].locator == "90 FR 86252"
|
|
assert result[0].locator_type == "fr"
|
|
|
|
def test_multiple(self):
|
|
doc = textwrap.dedent("""
|
|
:pincite:`JX46GQ9K p.14` -- first
|
|
:pincite:`9ASETKJ4 §3.2` -- second
|
|
:pincite:`ABC23456 Ch. 12` -- third
|
|
""")
|
|
result = parse_pincites(doc, "mod.fn")
|
|
assert len(result) == 3
|
|
|
|
def test_deduplication(self):
|
|
doc = ":pincite:`JX46GQ9K p.14` -- first\n:pincite:`JX46GQ9K p.14` -- dupe"
|
|
result = parse_pincites(doc, "mod.fn")
|
|
assert len(result) == 1
|
|
|
|
def test_empty_docstring(self):
|
|
assert parse_pincites("", "mod.fn") == []
|
|
assert parse_pincites(None, "mod.fn") == []
|
|
|
|
def test_no_pincites(self):
|
|
doc = "Just a plain docstring with no citations."
|
|
assert parse_pincites(doc, "mod.fn") == []
|
|
|
|
def test_invalid_key_charset(self):
|
|
# 0, 1, O, L are not valid Zotero key chars
|
|
doc = ":pincite:`00000000 p.1` -- bad key"
|
|
assert parse_pincites(doc, "mod.fn") == []
|
|
|
|
def test_em_dash(self):
|
|
doc = ":pincite:`JX46GQ9K p.14` — em dash note"
|
|
result = parse_pincites(doc, "mod.fn")
|
|
assert result[0].note == "em dash note"
|
|
|
|
|
|
# ── Classify locator ─────────────────────────────────────────────────
|
|
|
|
|
|
class TestClassifyLocator:
|
|
def test_page(self):
|
|
assert _classify_locator("p.14") == "page"
|
|
assert _classify_locator("pp.8-12") == "page"
|
|
|
|
def test_section(self):
|
|
assert _classify_locator("§3.2") == "section"
|
|
|
|
def test_cfr(self):
|
|
assert _classify_locator("42 CFR §414.22") == "cfr"
|
|
|
|
def test_fr(self):
|
|
assert _classify_locator("90 FR 86252") == "fr"
|
|
|
|
def test_chapter(self):
|
|
assert _classify_locator("Ch. 12") == "chapter"
|
|
|
|
def test_paragraph(self):
|
|
assert _classify_locator("¶4") == "paragraph"
|
|
|
|
def test_empty(self):
|
|
assert _classify_locator("") == ""
|
|
|
|
|
|
# ── Store operations ─────────────────────────────────────────────────
|
|
|
|
|
|
class TestUpsertPincites:
|
|
def test_insert(self, store_with_items):
|
|
pincites = [
|
|
Pincite(fn_path="mod.fn", item_key="JX46GQ9K", locator="p.14", note="test"),
|
|
]
|
|
count = upsert_pincites(store_with_items, pincites)
|
|
assert count == 1
|
|
|
|
result = list_pincites(store_with_items, fn_path="mod.fn")
|
|
assert len(result) == 1
|
|
assert result[0].item_key == "JX46GQ9K"
|
|
|
|
def test_upsert_idempotent(self, store_with_items):
|
|
p = Pincite(fn_path="mod.fn", item_key="JX46GQ9K", locator="p.14")
|
|
upsert_pincites(store_with_items, [p])
|
|
upsert_pincites(store_with_items, [p])
|
|
result = list_pincites(store_with_items, fn_path="mod.fn")
|
|
assert len(result) == 1
|
|
|
|
def test_skips_missing_item(self, store_with_items):
|
|
p = Pincite(fn_path="mod.fn", item_key="ZZZZZZZZ", locator="p.1")
|
|
count = upsert_pincites(store_with_items, [p])
|
|
assert count == 0
|
|
|
|
def test_tags_added(self, store_with_items):
|
|
p = Pincite(fn_path="mod.fn", item_key="JX46GQ9K", locator="p.14")
|
|
upsert_pincites(store_with_items, [p])
|
|
item = store_with_items.get("JX46GQ9K")
|
|
assert any("pin:" in t for t in item.tags)
|
|
|
|
|
|
class TestListPincites:
|
|
def test_by_fn(self, store_with_items):
|
|
upsert_pincites(
|
|
store_with_items,
|
|
[
|
|
Pincite(fn_path="a.b", item_key="JX46GQ9K", locator="p.1"),
|
|
Pincite(fn_path="c.d", item_key="9ASETKJ4", locator="§2"),
|
|
],
|
|
)
|
|
result = list_pincites(store_with_items, fn_path="a.b")
|
|
assert len(result) == 1
|
|
assert result[0].item_key == "JX46GQ9K"
|
|
|
|
def test_by_item(self, store_with_items):
|
|
upsert_pincites(
|
|
store_with_items,
|
|
[
|
|
Pincite(fn_path="a.b", item_key="JX46GQ9K", locator="p.1"),
|
|
Pincite(fn_path="c.d", item_key="JX46GQ9K", locator="p.2"),
|
|
],
|
|
)
|
|
result = list_pincites(store_with_items, item_key="JX46GQ9K")
|
|
assert len(result) == 2
|
|
|
|
def test_empty_store(self, store):
|
|
assert list_pincites(store) == []
|
|
|
|
|
|
# ── Check keys ───────────────────────────────────────────────────────
|
|
|
|
|
|
class TestCheckKeys:
|
|
def test_valid(self, store_with_items):
|
|
p = Pincite(fn_path="mod.fn", item_key="JX46GQ9K")
|
|
errors = check_pincite_keys([p], store_with_items)
|
|
assert len(errors) == 0
|
|
|
|
def test_invalid(self, store_with_items):
|
|
p = Pincite(fn_path="mod.fn", item_key="NOTFOUND")
|
|
errors = check_pincite_keys([p], store_with_items)
|
|
assert len(errors) == 1
|
|
assert "not found" in errors[0][1]
|
|
|
|
|
|
# ── Knowledge graph ──────────────────────────────────────────────────
|
|
|
|
|
|
class TestCitationGraph:
|
|
def test_build(self, store_with_items):
|
|
upsert_pincites(
|
|
store_with_items,
|
|
[
|
|
Pincite(fn_path="mod.fn_a", item_key="JX46GQ9K", locator="p.1"),
|
|
Pincite(fn_path="mod.fn_b", item_key="JX46GQ9K", locator="p.2"),
|
|
Pincite(fn_path="mod.fn_a", item_key="9ASETKJ4", locator="§3"),
|
|
],
|
|
)
|
|
graph = build_citation_graph(store_with_items)
|
|
assert len(graph.nodes) == 4 # 2 functions + 2 items
|
|
assert len(graph.edges) == 3
|
|
|
|
def test_functions_citing(self, store_with_items):
|
|
upsert_pincites(
|
|
store_with_items,
|
|
[
|
|
Pincite(fn_path="a.b", item_key="JX46GQ9K"),
|
|
Pincite(fn_path="c.d", item_key="JX46GQ9K"),
|
|
],
|
|
)
|
|
graph = build_citation_graph(store_with_items)
|
|
fns = graph.functions_citing("JX46GQ9K")
|
|
assert set(fns) == {"a.b", "c.d"}
|
|
|
|
def test_items_cited_by(self, store_with_items):
|
|
upsert_pincites(
|
|
store_with_items,
|
|
[
|
|
Pincite(fn_path="a.b", item_key="JX46GQ9K"),
|
|
Pincite(fn_path="a.b", item_key="9ASETKJ4"),
|
|
],
|
|
)
|
|
graph = build_citation_graph(store_with_items)
|
|
items = graph.items_cited_by("a.b")
|
|
assert set(items) == {"JX46GQ9K", "9ASETKJ4"}
|
|
|
|
def test_to_mermaid(self, store_with_items):
|
|
upsert_pincites(
|
|
store_with_items,
|
|
[
|
|
Pincite(fn_path="mod.fn", item_key="JX46GQ9K", locator="p.1"),
|
|
],
|
|
)
|
|
graph = build_citation_graph(store_with_items)
|
|
mermaid = graph.to_mermaid()
|
|
assert "graph LR" in mermaid
|
|
assert "mod_fn" in mermaid
|
|
|
|
def test_to_networkx_dict(self, store_with_items):
|
|
upsert_pincites(
|
|
store_with_items,
|
|
[
|
|
Pincite(fn_path="mod.fn", item_key="JX46GQ9K"),
|
|
],
|
|
)
|
|
graph = build_citation_graph(store_with_items)
|
|
d = graph.to_networkx_dict()
|
|
assert d["directed"] is True
|
|
assert len(d["nodes"]) == 2
|
|
assert len(d["links"]) == 1
|
|
|
|
|
|
# ── Format + inject ──────────────────────────────────────────────────
|
|
|
|
|
|
class TestFormatPinciteBlock:
|
|
def test_single(self):
|
|
p = Pincite(fn_path="m.f", item_key="JX46GQ9K", locator="p.14", note="test")
|
|
block = format_pincite_block([p])
|
|
assert ":pincite:`JX46GQ9K p.14` -- test" in block
|
|
assert block.startswith("References")
|
|
|
|
def test_no_locator(self):
|
|
p = Pincite(fn_path="m.f", item_key="JX46GQ9K")
|
|
block = format_pincite_block([p])
|
|
assert ":pincite:`JX46GQ9K`" in block
|
|
|
|
def test_empty(self):
|
|
assert format_pincite_block([]) == ""
|
|
|
|
|
|
class TestInjectPincites:
|
|
def test_append_to_docstring(self, tmp_path):
|
|
source = textwrap.dedent('''
|
|
def foo():
|
|
"""Existing docstring."""
|
|
return 1
|
|
''').lstrip()
|
|
f = tmp_path / "test_mod.py"
|
|
f.write_text(source)
|
|
|
|
block = "References\n~~~~~~~~~~\n:pincite:`JX46GQ9K p.14` -- test"
|
|
result = inject_pincites_into_source(f, "foo", block, dry_run=True)
|
|
assert result is not None
|
|
assert ":pincite:`JX46GQ9K p.14`" in result
|
|
assert "Existing docstring." in result
|
|
|
|
def test_skip_no_docstring(self, tmp_path):
|
|
source = "def foo():\n return 1\n"
|
|
f = tmp_path / "test_mod.py"
|
|
f.write_text(source)
|
|
result = inject_pincites_into_source(f, "foo", "block")
|
|
assert result is None
|
|
|
|
def test_skip_missing_function(self, tmp_path):
|
|
source = "def bar():\n pass\n"
|
|
f = tmp_path / "test_mod.py"
|
|
f.write_text(source)
|
|
result = inject_pincites_into_source(f, "foo", "block")
|
|
assert result is None
|
|
|
|
def test_syntax_error_returns_none(self, tmp_path):
|
|
"""Lines 505, 506: inject returns None for syntax errors."""
|
|
f = tmp_path / "bad.py"
|
|
f.write_text("def incomplete(:")
|
|
result = inject_pincites_into_source(f, "incomplete", "block")
|
|
assert result is None
|
|
|
|
def test_replace_existing_references(self, tmp_path):
|
|
"""Lines 542, 543, 545, 553, 557-559: replace existing References block."""
|
|
source = textwrap.dedent('''
|
|
def foo():
|
|
"""Existing docstring.
|
|
|
|
References
|
|
~~~~~~~~~~
|
|
:pincite:`AAAAAAAA p.1` -- old ref
|
|
"""
|
|
return 1
|
|
''').lstrip()
|
|
f = tmp_path / "test_mod.py"
|
|
f.write_text(source)
|
|
block = "References\n~~~~~~~~~~\n:pincite:`JX46GQ9K p.14` -- new ref"
|
|
result = inject_pincites_into_source(f, "foo", block, dry_run=True)
|
|
assert result is not None
|
|
assert "JX46GQ9K" in result
|
|
assert "AAAAAAAA" not in result
|
|
assert "Existing docstring." in result
|
|
|
|
def test_write_when_not_dry_run(self, tmp_path):
|
|
"""Lines 569, 572: inject actually writes when dry_run=False."""
|
|
source = textwrap.dedent('''
|
|
def foo():
|
|
"""Some docstring."""
|
|
return 1
|
|
''').lstrip()
|
|
f = tmp_path / "test_mod.py"
|
|
f.write_text(source)
|
|
block = "References\n~~~~~~~~~~\n:pincite:`JX46GQ9K p.14` -- test"
|
|
result = inject_pincites_into_source(f, "foo", block, dry_run=False)
|
|
assert result is not None
|
|
# File should have been written
|
|
content = f.read_text()
|
|
assert "JX46GQ9K" in content
|
|
|
|
def test_no_change_returns_none(self, tmp_path):
|
|
"""Line 569: if source is unchanged, returns None."""
|
|
source = textwrap.dedent('''
|
|
def foo():
|
|
"""References
|
|
~~~~~~~~~~
|
|
:pincite:`JX46GQ9K p.14` -- test
|
|
"""
|
|
return 1
|
|
''').lstrip()
|
|
f = tmp_path / "test_mod.py"
|
|
f.write_text(source)
|
|
# The exact same block — may or may not change depending on indent
|
|
block = "References\n~~~~~~~~~~\n:pincite:`JX46GQ9K p.14` -- test"
|
|
result = inject_pincites_into_source(f, "foo", block, dry_run=True)
|
|
# Either None (unchanged) or a string (reformatted); both acceptable
|
|
assert result is None or isinstance(result, str)
|
|
|
|
|
|
# ── extract_all_pincites edge cases (lines 255, 260, 275) ───────────
|
|
|
|
|
|
class TestExtractAllPincitesEdge:
|
|
def test_default_src_root(self):
|
|
"""Line 255: extract_all_pincites(None) defaults to src/."""
|
|
from bib.pincite import extract_all_pincites
|
|
|
|
# Just verify it doesn't crash and returns a list
|
|
result = extract_all_pincites()
|
|
assert isinstance(result, list)
|
|
|
|
def test_skips_egg_info(self, tmp_path):
|
|
"""Line 260: skips .egg-info directories."""
|
|
from bib.pincite import extract_all_pincites
|
|
|
|
src = tmp_path / "src"
|
|
(src / "pkg.egg-info").mkdir(parents=True)
|
|
(src / "pkg.egg-info" / "mod.py").write_text(
|
|
textwrap.dedent('''\
|
|
@narwhalify
|
|
def fn():
|
|
""":pincite:`ABCD2345 p.1` -- ref"""
|
|
pass
|
|
''')
|
|
)
|
|
result = extract_all_pincites(src)
|
|
assert len(result) == 0
|
|
|
|
def test_skips_no_docstring(self, tmp_path):
|
|
"""Line 275: narwhalify function with no docstring is skipped."""
|
|
from bib.pincite import extract_all_pincites
|
|
|
|
src = tmp_path / "src"
|
|
src.mkdir()
|
|
(src / "mod.py").write_text(
|
|
textwrap.dedent("""\
|
|
@narwhalify
|
|
def fn():
|
|
pass
|
|
""")
|
|
)
|
|
result = extract_all_pincites(src)
|
|
assert len(result) == 0
|
|
|
|
|
|
# ── build_citation_graph item lookup failure (lines 427, 428) ───────
|
|
|
|
|
|
class TestBuildCitationGraphEdge:
|
|
def test_item_get_failure_uses_key(self, store_with_items):
|
|
"""Lines 427, 428: when store.get fails, label falls back to key."""
|
|
from unittest.mock import patch
|
|
|
|
from bib.pincite import build_citation_graph, upsert_pincites
|
|
|
|
upsert_pincites(
|
|
store_with_items,
|
|
[Pincite(fn_path="mod.fn", item_key="JX46GQ9K", locator="p.1")],
|
|
)
|
|
# Patch store.get to raise KeyError for the item
|
|
orig_get = store_with_items.get
|
|
|
|
def failing_get(key):
|
|
if key == "JX46GQ9K":
|
|
raise KeyError("not found")
|
|
return orig_get(key)
|
|
|
|
with patch.object(store_with_items, "get", side_effect=failing_get):
|
|
graph = build_citation_graph(store_with_items)
|
|
# The item node should use the key as fallback label
|
|
item_nodes = [n for n in graph.nodes if n.kind == "item"]
|
|
assert len(item_nodes) == 1
|
|
assert item_nodes[0].label == "JX46GQ9K"
|
|
|
|
|
|
# ── upsert_pincites tag add_tag failure (lines 338, 339) ────────────
|
|
|
|
|
|
class TestUpsertPincitesTagFailure:
|
|
def test_tag_error_suppressed(self, store_with_items):
|
|
"""Lines 338, 339: add_tag exceptions don't prevent upsert."""
|
|
from unittest.mock import patch
|
|
|
|
from bib.pincite import upsert_pincites
|
|
|
|
with patch.object(
|
|
store_with_items, "add_tag", side_effect=Exception("tag err")
|
|
):
|
|
count = upsert_pincites(
|
|
store_with_items,
|
|
[Pincite(fn_path="mod.fn", item_key="JX46GQ9K", locator="p.1")],
|
|
)
|
|
assert count == 1
|
|
|
|
|
|
# ── FR locators + jump_url (#636) ───────────────────────────────────
|
|
|
|
|
|
class TestFrLocators:
|
|
def test_plain_fr_cite_keeps_fr_type(self) -> None:
|
|
assert _classify_locator("91 FR 44218") == "fr"
|
|
|
|
def test_fr_para_locator(self) -> None:
|
|
assert _classify_locator("91 FR 44218 ¶3") == "fr_para"
|
|
assert _classify_locator("91 FR 44218, para 3") == "fr_para"
|
|
assert _classify_locator("91 FR 44218 p.3") == "fr_para"
|
|
|
|
def test_fr_anchor_locator(self) -> None:
|
|
assert _classify_locator("p-3600") == "fr_anchor"
|
|
|
|
def test_existing_types_unchanged(self) -> None:
|
|
assert _classify_locator("p.14") == "page"
|
|
assert _classify_locator("§3.2") == "section"
|
|
assert _classify_locator("42 CFR 425.400") == "cfr"
|
|
|
|
|
|
class TestJumpUrl:
|
|
def _frlink_store(self):
|
|
from bib import frlink
|
|
from tests.bib.test_frlink import DOC_META, FIXTURE_HTML, _store_with_rule
|
|
|
|
s, key = _store_with_rule()
|
|
frlink.grab(s, key, fetch=lambda _doc: (DOC_META, FIXTURE_HTML))
|
|
return s, key
|
|
|
|
def test_jump_url_resolves_fr_para(self) -> None:
|
|
s, key = self._frlink_store()
|
|
p = Pincite(
|
|
fn_path="mod.fn",
|
|
item_key=key,
|
|
locator="91 FR 100 ¶2",
|
|
locator_type="fr_para",
|
|
)
|
|
assert p.jump_url(s) == "https://example.test/doc#p-2"
|
|
s.close()
|
|
|
|
def test_jump_url_resolves_plain_fr(self) -> None:
|
|
s, key = self._frlink_store()
|
|
p = Pincite(
|
|
fn_path="mod.fn", item_key=key, locator="91 FR 100", locator_type="fr"
|
|
)
|
|
assert p.jump_url(s) == "https://example.test/doc#page-100"
|
|
s.close()
|
|
|
|
def test_jump_url_empty_when_unresolvable(self) -> None:
|
|
s, key = self._frlink_store()
|
|
# non-FR locator → no URL; missing anchor map → no URL, no raise
|
|
p = Pincite(
|
|
fn_path="mod.fn", item_key=key, locator="§3.2", locator_type="section"
|
|
)
|
|
assert p.jump_url(s) == ""
|
|
p2 = Pincite(
|
|
fn_path="mod.fn", item_key=key, locator="91 FR 999", locator_type="fr"
|
|
)
|
|
assert p2.jump_url(s) == ""
|
|
s.close()
|
|
|
|
|
|
class TestGraphJumpUrl:
|
|
def test_edges_carry_jump_url(self) -> None:
|
|
from bib import frlink
|
|
from tests.bib.test_frlink import DOC_META, FIXTURE_HTML, _store_with_rule
|
|
|
|
s, key = _store_with_rule()
|
|
frlink.grab(s, key, fetch=lambda _doc: (DOC_META, FIXTURE_HTML))
|
|
upsert_pincites(
|
|
s,
|
|
[
|
|
Pincite(
|
|
fn_path="mod.fn",
|
|
item_key=key,
|
|
locator="91 FR 100 ¶2",
|
|
locator_type="fr_para",
|
|
)
|
|
],
|
|
)
|
|
g = build_citation_graph(s)
|
|
edge = next(e for e in g.edges if e.source == "mod.fn")
|
|
assert edge.jump_url == "https://example.test/doc#p-2"
|
|
s.close()
|
|
|
|
|
|
class TestCfrJumpUrl:
|
|
def test_cfr_locator_resolves_store_free(self) -> None:
|
|
s = Store(":memory:")
|
|
p = Pincite(
|
|
fn_path="mod.fn",
|
|
item_key="AAAA2222",
|
|
locator="42 CFR 425.400(c)",
|
|
locator_type="cfr",
|
|
)
|
|
assert p.jump_url(s) == (
|
|
"https://www.ecfr.gov/current/title-42/section-425.400#p-425.400(c)"
|
|
)
|
|
s.close()
|
|
|
|
def test_titleless_section_locator_stays_empty(self) -> None:
|
|
s = Store(":memory:")
|
|
p = Pincite(
|
|
fn_path="mod.fn",
|
|
item_key="AAAA2222",
|
|
locator="§3.2",
|
|
locator_type="section",
|
|
)
|
|
assert p.jump_url(s) == ""
|
|
s.close()
|