Merge pull request '[sovereignty] Cut the Cloud Umbilical — closes #94' (#107) from gemini/operational-hygiene into main
This commit was merged in pull request #107.
This commit is contained in:
@@ -17,5 +17,6 @@ def test_config_defaults_to_local_llama_cpp_runtime() -> None:
|
||||
)
|
||||
assert local_provider["model"] == "hermes4:14b"
|
||||
|
||||
assert config["fallback_model"]["provider"] == "custom"
|
||||
assert config["fallback_model"]["model"] == "gemini-2.5-pro"
|
||||
assert config["fallback_model"]["provider"] == "ollama"
|
||||
assert config["fallback_model"]["model"] == "hermes3:latest"
|
||||
assert "localhost" in config["fallback_model"]["base_url"]
|
||||
|
||||
202
tests/test_sovereignty_enforcement.py
Normal file
202
tests/test_sovereignty_enforcement.py
Normal file
@@ -0,0 +1,202 @@
|
||||
"""Sovereignty enforcement tests.
|
||||
|
||||
These tests implement the acceptance criteria from issue #94:
|
||||
[p0] Cut cloud inheritance from active harness config and cron
|
||||
|
||||
Every test in this file catches a specific way that cloud
|
||||
dependency can creep back into the active config. If any test
|
||||
fails, Timmy is phoning home.
|
||||
|
||||
These tests are designed to be run in CI and to BLOCK any commit
|
||||
that reintroduces cloud defaults.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
|
||||
import yaml
|
||||
import pytest
|
||||
|
||||
REPO_ROOT = Path(__file__).parent.parent
|
||||
CONFIG_PATH = REPO_ROOT / "config.yaml"
|
||||
CRON_PATH = REPO_ROOT / "cron" / "jobs.json"
|
||||
|
||||
# Cloud URLs that should never appear in default/fallback paths
|
||||
CLOUD_URLS = [
|
||||
"generativelanguage.googleapis.com",
|
||||
"api.openai.com",
|
||||
"chatgpt.com",
|
||||
"api.anthropic.com",
|
||||
"openrouter.ai",
|
||||
]
|
||||
|
||||
CLOUD_MODELS = [
|
||||
"gpt-4",
|
||||
"gpt-5",
|
||||
"gpt-4o",
|
||||
"claude",
|
||||
"gemini",
|
||||
]
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def config():
|
||||
return yaml.safe_load(CONFIG_PATH.read_text())
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def cron_jobs():
|
||||
data = json.loads(CRON_PATH.read_text())
|
||||
return data.get("jobs", data) if isinstance(data, dict) else data
|
||||
|
||||
|
||||
# ── Config defaults ──────────────────────────────────────────────────
|
||||
|
||||
class TestDefaultModelIsLocal:
|
||||
"""The default model must point to localhost."""
|
||||
|
||||
def test_default_model_is_not_cloud(self, config):
|
||||
"""model.default should be a local model identifier."""
|
||||
model = config["model"]["default"]
|
||||
for cloud in CLOUD_MODELS:
|
||||
assert cloud not in model.lower(), \
|
||||
f"Default model '{model}' looks like a cloud model"
|
||||
|
||||
def test_default_base_url_is_localhost(self, config):
|
||||
"""model.base_url should point to localhost."""
|
||||
base_url = config["model"]["base_url"]
|
||||
assert "localhost" in base_url or "127.0.0.1" in base_url, \
|
||||
f"Default base_url '{base_url}' is not local"
|
||||
|
||||
def test_default_provider_is_local(self, config):
|
||||
"""model.provider should be 'custom' or 'ollama'."""
|
||||
provider = config["model"]["provider"]
|
||||
assert provider in ("custom", "ollama", "local"), \
|
||||
f"Default provider '{provider}' may route to cloud"
|
||||
|
||||
|
||||
class TestFallbackIsLocal:
|
||||
"""The fallback model must also be local — this is the #94 fix."""
|
||||
|
||||
def test_fallback_base_url_is_localhost(self, config):
|
||||
"""fallback_model.base_url must point to localhost."""
|
||||
fb = config.get("fallback_model", {})
|
||||
base_url = fb.get("base_url", "")
|
||||
if base_url:
|
||||
assert "localhost" in base_url or "127.0.0.1" in base_url, \
|
||||
f"Fallback base_url '{base_url}' is not local — cloud leak!"
|
||||
|
||||
def test_fallback_has_no_cloud_url(self, config):
|
||||
"""fallback_model must not contain any cloud API URLs."""
|
||||
fb = config.get("fallback_model", {})
|
||||
base_url = fb.get("base_url", "")
|
||||
for cloud_url in CLOUD_URLS:
|
||||
assert cloud_url not in base_url, \
|
||||
f"Fallback model routes to cloud: {cloud_url}"
|
||||
|
||||
def test_fallback_model_name_is_local(self, config):
|
||||
"""fallback_model.model should not be a cloud model name."""
|
||||
fb = config.get("fallback_model", {})
|
||||
model = fb.get("model", "")
|
||||
for cloud in CLOUD_MODELS:
|
||||
assert cloud not in model.lower(), \
|
||||
f"Fallback model name '{model}' looks like cloud"
|
||||
|
||||
|
||||
# ── Cron jobs ────────────────────────────────────────────────────────
|
||||
|
||||
class TestCronSovereignty:
|
||||
"""Enabled cron jobs must never inherit cloud defaults."""
|
||||
|
||||
def test_enabled_crons_have_explicit_model(self, cron_jobs):
|
||||
"""Every enabled cron job must have a non-null model field.
|
||||
|
||||
When model is null, the job inherits from config.yaml's default.
|
||||
Even if the default is local today, a future edit could change it.
|
||||
Explicit is always safer than implicit.
|
||||
"""
|
||||
for job in cron_jobs:
|
||||
if not isinstance(job, dict):
|
||||
continue
|
||||
if not job.get("enabled", False):
|
||||
continue
|
||||
|
||||
model = job.get("model")
|
||||
name = job.get("name", job.get("id", "?"))
|
||||
assert model is not None and model != "", \
|
||||
f"Enabled cron job '{name}' has null model — will inherit default"
|
||||
|
||||
def test_enabled_crons_have_explicit_provider(self, cron_jobs):
|
||||
"""Every enabled cron job must have a non-null provider field."""
|
||||
for job in cron_jobs:
|
||||
if not isinstance(job, dict):
|
||||
continue
|
||||
if not job.get("enabled", False):
|
||||
continue
|
||||
|
||||
provider = job.get("provider")
|
||||
name = job.get("name", job.get("id", "?"))
|
||||
assert provider is not None and provider != "", \
|
||||
f"Enabled cron job '{name}' has null provider — will inherit default"
|
||||
|
||||
def test_no_enabled_cron_uses_cloud_url(self, cron_jobs):
|
||||
"""No enabled cron job should have a cloud base_url."""
|
||||
for job in cron_jobs:
|
||||
if not isinstance(job, dict):
|
||||
continue
|
||||
if not job.get("enabled", False):
|
||||
continue
|
||||
|
||||
base_url = job.get("base_url", "")
|
||||
name = job.get("name", job.get("id", "?"))
|
||||
for cloud_url in CLOUD_URLS:
|
||||
assert cloud_url not in (base_url or ""), \
|
||||
f"Cron '{name}' routes to cloud: {cloud_url}"
|
||||
|
||||
|
||||
# ── Custom providers ─────────────────────────────────────────────────
|
||||
|
||||
class TestCustomProviders:
|
||||
"""Cloud providers can exist but must not be the default path."""
|
||||
|
||||
def test_local_provider_exists(self, config):
|
||||
"""At least one custom provider must be local."""
|
||||
providers = config.get("custom_providers", [])
|
||||
has_local = any(
|
||||
"localhost" in p.get("base_url", "") or "127.0.0.1" in p.get("base_url", "")
|
||||
for p in providers
|
||||
)
|
||||
assert has_local, "No local custom provider defined"
|
||||
|
||||
def test_first_provider_is_local(self, config):
|
||||
"""The first custom_provider should be the local one.
|
||||
|
||||
Hermes resolves 'custom' provider by scanning the list in order.
|
||||
If a cloud provider is listed first, it becomes the implicit default.
|
||||
"""
|
||||
providers = config.get("custom_providers", [])
|
||||
if providers:
|
||||
first = providers[0]
|
||||
base_url = first.get("base_url", "")
|
||||
assert "localhost" in base_url or "127.0.0.1" in base_url, \
|
||||
f"First custom_provider '{first.get('name')}' is not local"
|
||||
|
||||
|
||||
# ── TTS/STT ──────────────────────────────────────────────────────────
|
||||
|
||||
class TestVoiceSovereignty:
|
||||
"""Voice services should prefer local providers."""
|
||||
|
||||
def test_tts_default_is_local(self, config):
|
||||
"""TTS provider should be local (edge or neutts)."""
|
||||
tts_provider = config.get("tts", {}).get("provider", "")
|
||||
assert tts_provider in ("edge", "neutts", "local"), \
|
||||
f"TTS provider '{tts_provider}' may use cloud"
|
||||
|
||||
def test_stt_default_is_local(self, config):
|
||||
"""STT provider should be local."""
|
||||
stt_provider = config.get("stt", {}).get("provider", "")
|
||||
assert stt_provider in ("local", "whisper", ""), \
|
||||
f"STT provider '{stt_provider}' may use cloud"
|
||||
Reference in New Issue
Block a user