Python · 4338 bytes Raw Blame History
1 """StoreInspection named-adapter discovery (audit-07 M2)."""
2
3 from __future__ import annotations
4
5 from pathlib import Path
6
7 from dlm.store.inspect import NamedAdapterState, inspect_store
8 from dlm.store.manifest import Manifest, save_manifest
9 from dlm.store.paths import StorePath
10
11
12 def _seed_store(tmp_path: Path, dlm_id: str = "01HZ4X7TGZM3J1A2B3C4D5E6FC") -> StorePath:
13 store = StorePath(root=tmp_path / "s")
14 store.ensure_layout()
15 save_manifest(
16 store.manifest,
17 Manifest(dlm_id=dlm_id, base_model="smollm2-135m"),
18 )
19 return store
20
21
22 class TestFlatStoreHasEmptyNamedAdapters:
23 def test_no_named_subdirs_yields_empty_list(self, tmp_path: Path) -> None:
24 store = _seed_store(tmp_path)
25 inspection = inspect_store(store)
26 assert inspection.named_adapters == []
27
28 def test_flat_versions_directory_ignored(self, tmp_path: Path) -> None:
29 """`adapter/versions/` is the flat layout, not a named adapter."""
30 store = _seed_store(tmp_path)
31 store.adapter_versions.mkdir(parents=True, exist_ok=True)
32 (store.adapter_version(1)).mkdir(parents=True, exist_ok=True)
33 inspection = inspect_store(store)
34 assert inspection.named_adapters == []
35
36
37 class TestMultiAdapterDiscovery:
38 def test_single_named_adapter_surfaces(self, tmp_path: Path) -> None:
39 store = _seed_store(tmp_path)
40 store.ensure_adapter_layout("knowledge")
41 v1 = store.adapter_version_for("knowledge", 1)
42 v1.mkdir(parents=True)
43 store.set_current_adapter_for("knowledge", v1)
44
45 inspection = inspect_store(store)
46 assert inspection.named_adapters == [
47 NamedAdapterState(name="knowledge", has_current=True, latest_version=1)
48 ]
49
50 def test_two_named_adapters_sorted_alphabetically(self, tmp_path: Path) -> None:
51 store = _seed_store(tmp_path)
52 for name, version in [("tone", 2), ("knowledge", 1)]:
53 store.ensure_adapter_layout(name)
54 for v in range(1, version + 1):
55 store.adapter_version_for(name, v).mkdir(parents=True, exist_ok=True)
56 store.set_current_adapter_for(name, store.adapter_version_for(name, version))
57
58 inspection = inspect_store(store)
59 names = [n.name for n in inspection.named_adapters]
60 assert names == ["knowledge", "tone"]
61
62 by_name = {n.name: n for n in inspection.named_adapters}
63 assert by_name["knowledge"].latest_version == 1
64 assert by_name["tone"].latest_version == 2
65
66 def test_adapter_without_current_pointer(self, tmp_path: Path) -> None:
67 """A named adapter dir with versions but no current pointer still surfaces."""
68 store = _seed_store(tmp_path)
69 store.ensure_adapter_layout("knowledge")
70 store.adapter_version_for("knowledge", 1).mkdir(parents=True)
71 # Don't set the current.txt pointer.
72
73 inspection = inspect_store(store)
74 assert inspection.named_adapters == [
75 NamedAdapterState(name="knowledge", has_current=False, latest_version=1)
76 ]
77
78 def test_empty_adapter_dir_without_versions_skipped(self, tmp_path: Path) -> None:
79 """A bare `adapter/<name>/` with no `versions/` subdir is skipped
80 (not a valid named-adapter shape — could be stray)."""
81 store = _seed_store(tmp_path)
82 (store.adapter / "ghost").mkdir(parents=True)
83
84 inspection = inspect_store(store)
85 assert inspection.named_adapters == []
86
87
88 class TestCoexistenceWithFlatLayout:
89 def test_flat_state_preserved_alongside_named(self, tmp_path: Path) -> None:
90 """The flat `has_adapter_current` + `adapter_version` are
91 independent of the named-adapter list."""
92 store = _seed_store(tmp_path)
93 # Flat setup.
94 flat_v1 = store.adapter_version(1)
95 flat_v1.mkdir(parents=True)
96 store.set_current_adapter(flat_v1)
97 # Named adapter alongside.
98 store.ensure_adapter_layout("tone")
99 named_v1 = store.adapter_version_for("tone", 1)
100 named_v1.mkdir(parents=True)
101 store.set_current_adapter_for("tone", named_v1)
102
103 inspection = inspect_store(store)
104 assert inspection.has_adapter_current is True
105 assert len(inspection.named_adapters) == 1
106 assert inspection.named_adapters[0].name == "tone"