Files
EvoScientist/tests/test_cli_startup_gates.py
T
m4 bb9bed82e1 feat(runtime)!: remove auxiliary model role, resolve all roles from run snapshot
ModelRole collapses to "primary": every role (main, tool selector, memory
agents, subagents, summarizer) resolves to the snapshot's frozen primary
model, per design 6.1/8.3 — users typically configure a single usable LLM,
so compile-time auxiliary bindings were bypassing run snapshots and
mis-attributing usage. Legacy auxiliary keys in stored snapshots, registry
JSON, and thread metadata are tolerated on read and dropped.

BREAKING CHANGE: ThreadModelSelection no longer carries an auxiliary ref;
snapshot selection_hash is computed over {primary, reasoning_effort} only;
ConfigurableModelMiddleware(role="auxiliary") is rejected.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-23 10:18:44 +08:00

73 lines
2.5 KiB
Python

"""Tests for the CLI startup gates (design doc §10 step 4, §8.1).
``_startup_gates`` is the shared refuse-startup path for the chat entry and
``serve``: legacy model-configuration artifacts abort with the reset guide,
and a bootstrap registry aborts with ``MODEL_REGISTRY_NOT_READY`` — both
with exit code 1, never a silent fall-through to an implicit model.
"""
from __future__ import annotations
from unittest.mock import MagicMock
import pytest
import typer
import EvoScientist.config.legacy_artifacts as legacy_artifacts
import EvoScientist.model_registry.runtime as runtime_module
from EvoScientist.cli.commands import _startup_gates
from EvoScientist.config.legacy_artifacts import LegacyArtifactsError
from EvoScientist.model_registry.errors import (
MODEL_REGISTRY_NOT_READY,
ModelRegistryError,
)
def test_legacy_artifacts_abort_startup(monkeypatch):
monkeypatch.setattr(
legacy_artifacts,
"assert_no_legacy_artifacts",
MagicMock(side_effect=LegacyArtifactsError("legacy providers.yaml found")),
)
with pytest.raises(typer.Exit) as excinfo:
_startup_gates()
assert excinfo.value.exit_code == 1
def test_bootstrap_registry_aborts_startup(monkeypatch):
monkeypatch.setattr(
legacy_artifacts, "assert_no_legacy_artifacts", MagicMock()
)
runtime = MagicMock()
runtime.registry_default.side_effect = ModelRegistryError(
MODEL_REGISTRY_NOT_READY, "bootstrap"
)
monkeypatch.setattr(runtime_module, "get_snapshot_runtime", lambda: runtime)
with pytest.raises(typer.Exit) as excinfo:
_startup_gates()
assert excinfo.value.exit_code == 1
def test_clean_environment_passes(monkeypatch):
monkeypatch.setattr(
legacy_artifacts, "assert_no_legacy_artifacts", MagicMock()
)
runtime = MagicMock()
runtime.registry_default.return_value = (MagicMock(), 2)
monkeypatch.setattr(runtime_module, "get_snapshot_runtime", lambda: runtime)
_startup_gates() # must not raise
def test_non_bootstrap_registry_error_propagates(monkeypatch):
"""Only NOT_READY is converted to a guided exit; other errors bubble up."""
monkeypatch.setattr(
legacy_artifacts, "assert_no_legacy_artifacts", MagicMock()
)
runtime = MagicMock()
runtime.registry_default.side_effect = ModelRegistryError(
"SNAPSHOT_EXPIRED", "expired"
)
monkeypatch.setattr(runtime_module, "get_snapshot_runtime", lambda: runtime)
with pytest.raises(ModelRegistryError):
_startup_gates()