102 lines
3.3 KiB
Python
102 lines
3.3 KiB
Python
|
|
from __future__ import annotations
|
||
|
|
|
||
|
|
from pathlib import Path
|
||
|
|
|
||
|
|
import pytest
|
||
|
|
import yaml
|
||
|
|
|
||
|
|
from infospace_bench import (
|
||
|
|
ArchiveRecord,
|
||
|
|
InfospaceError,
|
||
|
|
add_artifact,
|
||
|
|
archive_infospace,
|
||
|
|
create_infospace,
|
||
|
|
list_archives,
|
||
|
|
)
|
||
|
|
from infospace_bench.archive import (
|
||
|
|
ARCHIVE_INDEX_PATH,
|
||
|
|
ARCHIVE_STORE_DIR,
|
||
|
|
DEFAULT_RETENTION_CLASS,
|
||
|
|
PRODUCER,
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def _seed_infospace(workspace: Path, slug: str = "demo") -> Path:
|
||
|
|
create_infospace(workspace, slug, name="Demo", topic_domain="Test")
|
||
|
|
root = workspace / "infospaces" / slug
|
||
|
|
source = workspace / "source.md"
|
||
|
|
source.write_text("# source\n", encoding="utf-8")
|
||
|
|
add_artifact(root, source, kind="source", title="Source One")
|
||
|
|
(root / "reports" / "summary.md").write_text("# summary\n", encoding="utf-8")
|
||
|
|
return root
|
||
|
|
|
||
|
|
|
||
|
|
def test_archive_infospace_writes_index_and_finalizes_package(tmp_path: Path) -> None:
|
||
|
|
root = _seed_infospace(tmp_path)
|
||
|
|
|
||
|
|
record = archive_infospace(root, note="first archive")
|
||
|
|
|
||
|
|
assert isinstance(record, ArchiveRecord)
|
||
|
|
assert record.package_id
|
||
|
|
assert record.manifest_digest.startswith("blake3:")
|
||
|
|
assert record.retention_class == DEFAULT_RETENTION_CLASS
|
||
|
|
assert record.producer == PRODUCER
|
||
|
|
assert record.subject == "demo"
|
||
|
|
assert record.note == "first archive"
|
||
|
|
assert record.file_count >= 4 # infospace.yaml, index.yaml, source.md, summary.md
|
||
|
|
|
||
|
|
index_path = root / ARCHIVE_INDEX_PATH
|
||
|
|
assert index_path.is_file()
|
||
|
|
data = yaml.safe_load(index_path.read_text(encoding="utf-8"))
|
||
|
|
assert isinstance(data, dict)
|
||
|
|
assert len(data["archives"]) == 1
|
||
|
|
assert data["archives"][0]["package_id"] == record.package_id
|
||
|
|
|
||
|
|
store_root = root / ARCHIVE_STORE_DIR
|
||
|
|
assert (store_root / "registry.sqlite").is_file()
|
||
|
|
assert (store_root / "storage").is_dir()
|
||
|
|
|
||
|
|
|
||
|
|
def test_list_archives_returns_recorded_entries(tmp_path: Path) -> None:
|
||
|
|
root = _seed_infospace(tmp_path)
|
||
|
|
|
||
|
|
assert list_archives(root) == []
|
||
|
|
first = archive_infospace(root, note="alpha")
|
||
|
|
second = archive_infospace(root, note="beta")
|
||
|
|
|
||
|
|
archives = list_archives(root)
|
||
|
|
assert [a.package_id for a in archives] == [first.package_id, second.package_id]
|
||
|
|
assert [a.note for a in archives] == ["alpha", "beta"]
|
||
|
|
|
||
|
|
|
||
|
|
def test_archive_excludes_store_dir_to_avoid_recursive_capture(tmp_path: Path) -> None:
|
||
|
|
root = _seed_infospace(tmp_path)
|
||
|
|
|
||
|
|
first = archive_infospace(root)
|
||
|
|
second = archive_infospace(root)
|
||
|
|
|
||
|
|
# The store dir grows on the first call; the second call must not pick up
|
||
|
|
# any of its bytes (otherwise file_count would balloon).
|
||
|
|
assert second.file_count == first.file_count
|
||
|
|
second_record = list_archives(root)[1]
|
||
|
|
assert all(
|
||
|
|
not path.startswith(ARCHIVE_STORE_DIR)
|
||
|
|
for path in second_record.included_paths
|
||
|
|
)
|
||
|
|
|
||
|
|
|
||
|
|
def test_archive_respects_caller_supplied_include_set(tmp_path: Path) -> None:
|
||
|
|
root = _seed_infospace(tmp_path)
|
||
|
|
|
||
|
|
record = archive_infospace(root, include=["infospace.yaml"])
|
||
|
|
assert record.included_paths == ["infospace.yaml"]
|
||
|
|
assert record.file_count == 1
|
||
|
|
|
||
|
|
|
||
|
|
def test_archive_rejects_empty_include(tmp_path: Path) -> None:
|
||
|
|
root = _seed_infospace(tmp_path)
|
||
|
|
|
||
|
|
with pytest.raises(InfospaceError) as excinfo:
|
||
|
|
archive_infospace(root, include=["does-not-exist"])
|
||
|
|
assert excinfo.value.code == "empty_archive"
|