2026-04-05 18:33:52 -04:00
|
|
|
"""Tests for configuration and canonical path boundaries."""
|
|
|
|
|
|
|
|
|
|
import os
|
|
|
|
|
from pathlib import Path
|
|
|
|
|
|
|
|
|
|
import atocore.config as config
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_settings_resolve_canonical_directories(tmp_path, monkeypatch):
|
|
|
|
|
monkeypatch.setenv("ATOCORE_DATA_DIR", str(tmp_path / "data"))
|
|
|
|
|
monkeypatch.setenv("ATOCORE_VAULT_SOURCE_DIR", str(tmp_path / "vault-source"))
|
|
|
|
|
monkeypatch.setenv("ATOCORE_DRIVE_SOURCE_DIR", str(tmp_path / "drive-source"))
|
|
|
|
|
monkeypatch.setenv("ATOCORE_LOG_DIR", str(tmp_path / "logs"))
|
|
|
|
|
monkeypatch.setenv("ATOCORE_BACKUP_DIR", str(tmp_path / "backups"))
|
2026-04-06 08:02:13 -04:00
|
|
|
monkeypatch.setenv(
|
|
|
|
|
"ATOCORE_PROJECT_REGISTRY_PATH", str(tmp_path / "config" / "project-registry.json")
|
|
|
|
|
)
|
2026-04-05 18:33:52 -04:00
|
|
|
|
|
|
|
|
settings = config.Settings()
|
|
|
|
|
|
|
|
|
|
assert settings.db_path == (tmp_path / "data" / "db" / "atocore.db").resolve()
|
|
|
|
|
assert settings.chroma_path == (tmp_path / "data" / "chroma").resolve()
|
|
|
|
|
assert settings.cache_path == (tmp_path / "data" / "cache").resolve()
|
|
|
|
|
assert settings.tmp_path == (tmp_path / "data" / "tmp").resolve()
|
|
|
|
|
assert settings.resolved_vault_source_dir == (tmp_path / "vault-source").resolve()
|
|
|
|
|
assert settings.resolved_drive_source_dir == (tmp_path / "drive-source").resolve()
|
|
|
|
|
assert settings.resolved_log_dir == (tmp_path / "logs").resolve()
|
|
|
|
|
assert settings.resolved_backup_dir == (tmp_path / "backups").resolve()
|
|
|
|
|
assert settings.resolved_run_dir == (tmp_path / "run").resolve()
|
2026-04-06 08:02:13 -04:00
|
|
|
assert settings.resolved_project_registry_path == (
|
|
|
|
|
tmp_path / "config" / "project-registry.json"
|
|
|
|
|
).resolve()
|
2026-04-05 18:33:52 -04:00
|
|
|
|
|
|
|
|
|
|
|
|
|
def test_settings_keep_legacy_db_path_when_present(tmp_path, monkeypatch):
|
|
|
|
|
data_dir = tmp_path / "data"
|
|
|
|
|
data_dir.mkdir()
|
|
|
|
|
legacy_db = data_dir / "atocore.db"
|
|
|
|
|
legacy_db.write_text("", encoding="utf-8")
|
|
|
|
|
monkeypatch.setenv("ATOCORE_DATA_DIR", str(data_dir))
|
|
|
|
|
|
|
|
|
|
settings = config.Settings()
|
|
|
|
|
|
|
|
|
|
assert settings.db_path == legacy_db.resolve()
|
|
|
|
|
|
|
|
|
|
|
feat: tunable ranking, refresh status, chroma backup + admin endpoints
Three small improvements that move the operational baseline forward
without changing the existing trust model.
1. Tunable retrieval ranking weights
- rank_project_match_boost, rank_query_token_step,
rank_query_token_cap, rank_path_high_signal_boost,
rank_path_low_signal_penalty are now Settings fields
- all overridable via ATOCORE_* env vars
- retriever no longer hard-codes 2.0 / 1.18 / 0.72 / 0.08 / 1.32
- lets ranking be tuned per environment as Wave 1 is exercised
without code changes
2. /projects/{name}/refresh status
- refresh_registered_project now returns an overall status field
("ingested", "partial", "nothing_to_ingest") plus roots_ingested
and roots_skipped counters
- ProjectRefreshResponse advertises the new fields so callers can
rely on them
- covers the case where every configured root is missing on disk
3. Chroma cold snapshot + admin backup endpoints
- create_runtime_backup now accepts include_chroma and writes a
cold directory copy of the chroma persistence path
- new list_runtime_backups() and validate_backup() helpers
- new endpoints:
- POST /admin/backup create snapshot (optional chroma)
- GET /admin/backup list snapshots
- GET /admin/backup/{stamp}/validate structural validation
- chroma snapshots are taken under exclusive_ingestion() so a refresh
or ingest cannot race with the cold copy
- backup metadata records what was actually included and how big
Tests:
- 8 new tests covering tunable weights, refresh status branches
(ingested / partial / nothing_to_ingest), chroma snapshot, list,
validate, and the API endpoints (including the lock-acquisition path)
- existing fake refresh stubs in test_api_storage.py updated for the
expanded ProjectRefreshResponse model
- full suite: 105 passing (was 97)
next-steps doc updated to reflect that the chroma snapshot + restore
validation gap from current-state.md is now closed in code; only the
operational retention policy remains.
2026-04-06 18:42:19 -04:00
|
|
|
def test_ranking_weights_are_tunable_via_env(monkeypatch):
|
|
|
|
|
monkeypatch.setenv("ATOCORE_RANK_PROJECT_MATCH_BOOST", "3.5")
|
|
|
|
|
monkeypatch.setenv("ATOCORE_RANK_QUERY_TOKEN_STEP", "0.12")
|
|
|
|
|
monkeypatch.setenv("ATOCORE_RANK_QUERY_TOKEN_CAP", "1.5")
|
|
|
|
|
monkeypatch.setenv("ATOCORE_RANK_PATH_HIGH_SIGNAL_BOOST", "1.25")
|
|
|
|
|
monkeypatch.setenv("ATOCORE_RANK_PATH_LOW_SIGNAL_PENALTY", "0.5")
|
|
|
|
|
|
|
|
|
|
settings = config.Settings()
|
|
|
|
|
|
|
|
|
|
assert settings.rank_project_match_boost == 3.5
|
|
|
|
|
assert settings.rank_query_token_step == 0.12
|
|
|
|
|
assert settings.rank_query_token_cap == 1.5
|
|
|
|
|
assert settings.rank_path_high_signal_boost == 1.25
|
|
|
|
|
assert settings.rank_path_low_signal_penalty == 0.5
|
|
|
|
|
|
|
|
|
|
|
2026-04-05 18:33:52 -04:00
|
|
|
def test_ensure_runtime_dirs_creates_machine_dirs_only(tmp_path, monkeypatch):
|
|
|
|
|
monkeypatch.setenv("ATOCORE_DATA_DIR", str(tmp_path / "data"))
|
|
|
|
|
monkeypatch.setenv("ATOCORE_VAULT_SOURCE_DIR", str(tmp_path / "vault-source"))
|
|
|
|
|
monkeypatch.setenv("ATOCORE_DRIVE_SOURCE_DIR", str(tmp_path / "drive-source"))
|
|
|
|
|
monkeypatch.setenv("ATOCORE_LOG_DIR", str(tmp_path / "logs"))
|
|
|
|
|
monkeypatch.setenv("ATOCORE_BACKUP_DIR", str(tmp_path / "backups"))
|
2026-04-06 09:52:19 -04:00
|
|
|
monkeypatch.setenv(
|
|
|
|
|
"ATOCORE_PROJECT_REGISTRY_PATH", str(tmp_path / "config" / "project-registry.json")
|
|
|
|
|
)
|
2026-04-05 18:33:52 -04:00
|
|
|
|
|
|
|
|
original_settings = config.settings
|
|
|
|
|
try:
|
|
|
|
|
config.settings = config.Settings()
|
|
|
|
|
config.ensure_runtime_dirs()
|
|
|
|
|
|
|
|
|
|
assert config.settings.db_path.parent.exists()
|
|
|
|
|
assert config.settings.chroma_path.exists()
|
|
|
|
|
assert config.settings.cache_path.exists()
|
|
|
|
|
assert config.settings.tmp_path.exists()
|
|
|
|
|
assert config.settings.resolved_log_dir.exists()
|
|
|
|
|
assert config.settings.resolved_backup_dir.exists()
|
|
|
|
|
assert config.settings.resolved_run_dir.exists()
|
2026-04-06 09:52:19 -04:00
|
|
|
assert config.settings.resolved_project_registry_path.parent.exists()
|
2026-04-05 18:33:52 -04:00
|
|
|
assert not config.settings.resolved_vault_source_dir.exists()
|
|
|
|
|
assert not config.settings.resolved_drive_source_dir.exists()
|
|
|
|
|
finally:
|
|
|
|
|
config.settings = original_settings
|