generated from coulomb/repo-seed
212 lines
8.3 KiB
Python
212 lines
8.3 KiB
Python
import json
|
|
from pathlib import Path
|
|
|
|
from phase_memory.lifecycle import plan_compaction
|
|
from phase_memory.models import LifecycleAction, LifecycleActionKind, LifecycleState, MemoryNode
|
|
from phase_memory.service import (
|
|
HEALTH_REPORT_SCHEMA,
|
|
KONTEXTUAL_DELEGATION_SCHEMA,
|
|
SERVICE_CONTRACT_SCHEMA,
|
|
LocalServiceRunner,
|
|
RuntimeConfig,
|
|
assert_audit_sink_conformance,
|
|
assert_context_compiler_conformance,
|
|
assert_event_log_conformance,
|
|
assert_graph_store_conformance,
|
|
assert_policy_gateway_conformance,
|
|
assert_runtime_registry_conformance,
|
|
assert_semantic_index_conformance,
|
|
default_conformance_adapters,
|
|
health_report,
|
|
kontextual_delegation_envelope,
|
|
resolve_runtime_adapters,
|
|
runtime_from_config,
|
|
service_contracts,
|
|
)
|
|
|
|
FIXTURES = Path(__file__).parent / "fixtures"
|
|
|
|
|
|
def _load(name: str):
|
|
return json.loads((FIXTURES / name).read_text(encoding="utf-8"))
|
|
|
|
|
|
def test_service_contracts_list_runtime_operations() -> None:
|
|
contracts = service_contracts()
|
|
|
|
assert contracts["schema_version"] == SERVICE_CONTRACT_SCHEMA
|
|
assert "profile.plan" in contracts["operations"]
|
|
assert "health.check" in contracts["operations"]
|
|
|
|
|
|
def test_runtime_config_validation_and_health_report() -> None:
|
|
runner = LocalServiceRunner()
|
|
runner.runtime.graph_store.save_node(MemoryNode("node.stale", "episode", lifecycle=LifecycleState.STALE))
|
|
report = health_report(runner.runtime, config=RuntimeConfig.local_default())
|
|
|
|
assert report["schema_version"] == HEALTH_REPORT_SCHEMA
|
|
assert report["ok"] is True
|
|
assert report["store"]["stale_memory_count"] == 1
|
|
assert report["adapters"]["graph_store"] == "InMemoryMemoryGraphStore"
|
|
|
|
|
|
def test_service_runner_handles_health() -> None:
|
|
runner = LocalServiceRunner()
|
|
|
|
response = runner.handle("health.check")
|
|
|
|
assert response["schema_version"] == HEALTH_REPORT_SCHEMA
|
|
assert response["ok"] is True
|
|
|
|
|
|
def test_service_runner_handles_profile_driven_lifecycle_plan() -> None:
|
|
runner = LocalServiceRunner()
|
|
|
|
response = runner.handle(
|
|
"graph.lifecycle.plan",
|
|
{
|
|
"profile": _load("memory-profile.json"),
|
|
"graph": _load("memory-graph.json"),
|
|
"parameters": {"refresh_digests": {"event.restart": "new"}},
|
|
},
|
|
)
|
|
|
|
actions = {(action["target_id"], action["action"]) for action in response["data"]["dry_run_actions"]}
|
|
assert response["operation"] == "graph.lifecycle.plan"
|
|
assert response["data"]["profile_id"] == "phase-memory-fixture-profile"
|
|
assert ("event.restart", "refresh") in actions
|
|
|
|
|
|
def test_service_runner_handles_package_compile_and_audit_query() -> None:
|
|
runner = LocalServiceRunner()
|
|
selection = {
|
|
"schema_version": "markitect.memory.selection.v1",
|
|
"id": "selection.service",
|
|
"nodes": ["decision.boundary"],
|
|
"events": ["event.activation"],
|
|
}
|
|
|
|
compiled = runner.handle("package.compile", {"selection": selection, "source_ref": "service-test"})
|
|
audit = runner.handle("audit.query", {"filters": {"operation": "package.compile"}})
|
|
|
|
assert compiled["operation"] == "package.compile"
|
|
assert compiled["data"]["package_response"]["package_ref"] == "package:selection.service"
|
|
assert audit["operation"] == "audit.query"
|
|
assert audit["count"] == 1
|
|
assert audit["events"][0]["source"]["ref"] == "service-test"
|
|
assert audit["retention"]["mode"] == "in_memory"
|
|
|
|
|
|
def test_service_runner_handles_review_gated_lifecycle_apply() -> None:
|
|
runner = LocalServiceRunner()
|
|
node = runner.runtime.graph_store.save_node(MemoryNode("node.review", "episode", "Review gated content"))
|
|
compact = plan_compaction([node]).to_dict()
|
|
|
|
denied = runner.handle("lifecycle.apply", {"actions": [compact]})
|
|
applied = runner.handle("lifecycle.apply", {"actions": [compact], "approval_marker": "review:service"})
|
|
audit = runner.handle("audit.query", {"filters": {"operation": "lifecycle.apply", "dry_run": False}})
|
|
|
|
assert denied["valid"] is False
|
|
assert denied["data"]["denied"][0]["reason"] == "review_required"
|
|
assert applied["valid"] is True
|
|
assert runner.runtime.graph_store.get_node(applied["data"]["applied"][0]["target_id"]).kind == "summary"
|
|
assert audit["count"] == 2
|
|
|
|
|
|
def test_service_runner_handles_non_review_lifecycle_apply() -> None:
|
|
runner = LocalServiceRunner()
|
|
runner.runtime.graph_store.save_node(MemoryNode("node.stale.service", "episode"))
|
|
action = LifecycleAction(
|
|
LifecycleActionKind.MARK_STALE,
|
|
"node.stale.service",
|
|
from_state=LifecycleState.ACTIVE,
|
|
to_state=LifecycleState.STALE,
|
|
)
|
|
|
|
applied = runner.handle("lifecycle.apply", {"actions": [action.to_dict()]})
|
|
|
|
assert applied["valid"] is True
|
|
assert runner.runtime.graph_store.get_node("node.stale.service").lifecycle == LifecycleState.STALE
|
|
|
|
|
|
def test_profile_driven_runtime_config_resolves_file_backed_adapters(tmp_path) -> None:
|
|
config = RuntimeConfig.from_profile(
|
|
{
|
|
"schema_version": "markitect.memory.profile.v1",
|
|
"id": "profile.config",
|
|
"stores": {
|
|
"graph_store": "file",
|
|
"event_log": "jsonl",
|
|
},
|
|
"activation": {"semantic_index": "memory"},
|
|
"policy": {"mode": "allow-all", "trust_zones": ["local", "team"]},
|
|
"observability": {"audit_sink": "jsonl", "runtime_registry": "memory"},
|
|
"metadata": {"runtime": {"local_store_path": str(tmp_path / "memory-store")}},
|
|
}
|
|
)
|
|
|
|
bundle = resolve_runtime_adapters(config)
|
|
runtime = runtime_from_config(config)
|
|
runtime.graph_store.save_node(MemoryNode("node.config", "decision", "Config-driven node"))
|
|
|
|
assert config.adapter_mode("graph_store") == "file"
|
|
assert config.adapter_mode("event_log") == "jsonl"
|
|
assert config.trust_zone_labels == ("local", "team")
|
|
assert bundle.to_dict()["graph_store"] == "FileBackedMemoryGraphStore"
|
|
assert bundle.to_dict()["event_log"] == "JsonlMemoryEventLog"
|
|
assert bundle.to_dict()["semantic_index"] == "InMemorySemanticIndex"
|
|
assert (tmp_path / "memory-store" / "nodes" / "node.config.json").exists()
|
|
|
|
|
|
def test_runtime_config_from_fixture_profile_understands_local_aliases() -> None:
|
|
config = RuntimeConfig.from_profile(_load("memory-profile.json"), local_store_path=".phase-memory-test")
|
|
|
|
assert config.adapter_mode("graph_store") == "file"
|
|
assert config.adapter_mode("event_log") == "jsonl"
|
|
assert config.adapter_mode("package_compiler") == "noop"
|
|
assert config.trust_zone_labels == ("project-local",)
|
|
|
|
|
|
def test_missing_external_adapter_blocks_runtime_resolution() -> None:
|
|
registry = RuntimeConfig.local_default().adapter_registry.copy()
|
|
registry["policy_gateway"] = "external"
|
|
config = RuntimeConfig(adapter_registry=registry, policy_mode="external")
|
|
|
|
bundle = resolve_runtime_adapters(config)
|
|
|
|
assert "external_adapter_declared" in [diagnostic.code for diagnostic in bundle.diagnostics]
|
|
assert "missing_external_adapter" in [diagnostic.code for diagnostic in bundle.diagnostics]
|
|
try:
|
|
runtime_from_config(config)
|
|
except ValueError as exc:
|
|
assert "missing_external_adapter" in str(exc)
|
|
else:
|
|
raise AssertionError("runtime_from_config should require supplied external adapters")
|
|
|
|
|
|
def test_default_adapter_conformance_helpers() -> None:
|
|
adapters = default_conformance_adapters()
|
|
|
|
assert_graph_store_conformance(adapters["graph_store"])
|
|
assert_event_log_conformance(adapters["event_log"])
|
|
assert_context_compiler_conformance(adapters["context_compiler"])
|
|
assert_policy_gateway_conformance(adapters["policy_gateway"])
|
|
assert_audit_sink_conformance(adapters["audit_sink"])
|
|
assert_semantic_index_conformance(adapters["semantic_index"])
|
|
assert_runtime_registry_conformance(adapters["runtime_registry"])
|
|
|
|
|
|
def test_kontextual_delegation_envelope_is_explicit() -> None:
|
|
envelope = kontextual_delegation_envelope(
|
|
operation="persist_graph",
|
|
graph_id="graph.a",
|
|
profile_id="profile.a",
|
|
policy_decision={"allowed": True},
|
|
audit_ref="audit.a",
|
|
)
|
|
|
|
assert envelope["schema_version"] == KONTEXTUAL_DELEGATION_SCHEMA
|
|
assert "phase_policy" in envelope["phase_memory_owns"]
|
|
assert "durable_records" in envelope["kontextual_owns"]
|
|
assert envelope["imports"]["avoid_circular_imports"] is True
|