# SPDX-License-Identifier: AGPL-3.0-only # Copyright (c) 2026 sol pbc from __future__ import annotations import importlib import json from collections.abc import Callable from dataclasses import dataclass from datetime import datetime, timezone from pathlib import Path from types import SimpleNamespace from typing import Any import pytest from solstone.convey import create_app from tests.helpers.journal_config import seed_journal_config class _FixedDateTime(datetime): @classmethod def now(cls, tz=None): return datetime(2026, 7, 19, 12, 0, tzinfo=tz or timezone.utc) def _base_config(**updates: Any) -> dict[str, Any]: config: dict[str, Any] = { "setup": {"completed_at": 1700000000000}, "identity": {"name": "Before", "preferred": "Before"}, "journal": {"name": "Before"}, "env": {}, "providers": { "active": {"provider": "google", "model": "gemini-3.5-flash"}, "key_validation": {}, "local": {}, }, "retention": {"raw_media": "keep", "raw_media_days": None}, } config.update(updates) return config def _spy_changed(monkeypatch: pytest.MonkeyPatch, module: Any) -> list[bool]: real_mutate = module.mutate_journal_config changes: list[bool] = [] def wrapped(mutator, *args, **kwargs): result = real_mutate(mutator, *args, **kwargs) changes.append(result.changed) return result monkeypatch.setattr(module, "mutate_journal_config", wrapped) return changes def _client(journal: Path, monkeypatch: pytest.MonkeyPatch): monkeypatch.setenv("SOLSTONE_JOURNAL", str(journal)) monkeypatch.setenv("SOL_SKIP_SUPERVISOR_CHECK", "1") monkeypatch.setenv("SOLSTONE_DISABLE_CONVEY_SIDE_RUNTIMES", "1") app = create_app(str(journal)) app.config["TESTING"] = True return app.test_client() @dataclass(frozen=True) class RouteCase: path_id: str module_name: str method: str path: str payload: dict[str, Any] | None changed_seed: dict[str, Any] noop_seed: dict[str, Any] patch: Callable[[pytest.MonkeyPatch, Any], None] | None = None def _patch_noop_action(monkeypatch: pytest.MonkeyPatch, module: Any) -> None: if hasattr(module, "log_app_action"): monkeypatch.setattr(module, "log_app_action", lambda **_kwargs: None) def _patch_settings_key_validation( monkeypatch: pytest.MonkeyPatch, module: Any ) -> None: _patch_noop_action(monkeypatch, module) monkeypatch.setattr(module, "datetime", _FixedDateTime) plaud = importlib.import_module("solstone.think.importers.plaud") monkeypatch.setattr( plaud, "validate_token", lambda token: {"valid": True, "account": token}, ) def _patch_thinking_key_validation( monkeypatch: pytest.MonkeyPatch, module: Any ) -> None: _patch_noop_action(monkeypatch, module) monkeypatch.setattr(module, "datetime", _FixedDateTime) monkeypatch.setattr( module, "validate_key", lambda provider, key: {"valid": True, "provider": provider, "key": key}, ) def _patch_settings_validate_all(monkeypatch: pytest.MonkeyPatch, module: Any) -> None: monkeypatch.setattr( module, "_compute_key_validation", lambda _config: {"plaud": {"valid": True}}, ) def _patch_thinking_validate_all(monkeypatch: pytest.MonkeyPatch, module: Any) -> None: monkeypatch.setattr( module, "_compute_ai_key_validation", lambda _config: {"google": {"valid": True}}, ) ROUTE_CASES: tuple[RouteCase, ...] = ( RouteCase( "settings.update_config.identity", "solstone.apps.settings.routes", "put", "/app/settings/api/config", {"section": "identity", "data": {"name": "After"}}, _base_config(), _base_config(identity={"name": "After", "preferred": "Before"}), ), RouteCase( "settings.update_config.journal", "solstone.apps.settings.routes", "put", "/app/settings/api/config", {"section": "journal", "data": {"name": "After"}}, _base_config(), _base_config(journal={"name": "After"}), ), RouteCase( "settings.update_config.transcribe", "solstone.apps.settings.routes", "put", "/app/settings/api/config", {"section": "transcribe", "data": {"backend": "parakeet-cpp"}}, _base_config(transcribe={"backend": "parakeet"}), _base_config(transcribe={"backend": "parakeet-cpp"}), ), RouteCase( "settings.update_config.support", "solstone.apps.settings.routes", "put", "/app/settings/api/config", {"section": "support", "data": {"enabled": True}}, _base_config(support={"enabled": False}), _base_config(support={"enabled": True}), ), RouteCase( "settings.update_config.agent", "solstone.apps.settings.routes", "put", "/app/settings/api/config", {"section": "agent", "data": {"name": "helper"}}, _base_config(agent={"name": "sol"}), _base_config(agent={"name": "helper"}), ), RouteCase( "settings.update_config.processing", "solstone.apps.settings.routes", "put", "/app/settings/api/config", { "section": "processing", "data": { "mode": "deferred", "gate": {"time_window": {"start": "01:00", "end": "05:00"}}, }, }, _base_config(), _base_config( processing={ "mode": "deferred", "gate": { "time_window": { "enabled": True, "start": "01:00", "end": "05:00", }, "display_powersave": {"enabled": False}, }, } ), ), RouteCase( "settings.update_config.env.set", "solstone.apps.settings.routes", "put", "/app/settings/api/config", {"section": "env", "data": {"PLAUD_ACCESS_TOKEN": "plaud-token"}}, _base_config(env={}), _base_config( env={"PLAUD_ACCESS_TOKEN": "plaud-token"}, service_key_validation={ "plaud": { "valid": True, "account": "plaud-token", "timestamp": "2026-07-19T12:00:00+00:00", } }, ), _patch_settings_key_validation, ), RouteCase( "settings.update_config.env.clear", "solstone.apps.settings.routes", "put", "/app/settings/api/config", {"section": "env", "data": {"PLAUD_ACCESS_TOKEN": ""}}, _base_config( env={"PLAUD_ACCESS_TOKEN": "plaud-token"}, service_key_validation={"plaud": {"valid": True}}, ), _base_config(env={"PLAUD_ACCESS_TOKEN": ""}, service_key_validation={}), _patch_settings_key_validation, ), RouteCase( "settings.validate_all_keys", "solstone.apps.settings.routes", "post", "/app/settings/api/validate-keys", None, _base_config(env={"PLAUD_ACCESS_TOKEN": "plaud-token"}), _base_config( env={"PLAUD_ACCESS_TOKEN": "plaud-token"}, service_key_validation={"plaud": {"valid": True}}, ), _patch_settings_validate_all, ), RouteCase( "settings.update_vision", "solstone.apps.settings.routes", "put", "/app/settings/api/vision", {"max_extractions": 42}, _base_config(describe={"max_extractions": 10}), _base_config(describe={"max_extractions": 42}), ), RouteCase( "settings.update_observe", "solstone.apps.settings.routes", "put", "/app/settings/api/observe", {"tmux": {"enabled": False}}, _base_config(observe={"tmux": {"enabled": True}}), _base_config(observe={"tmux": {"enabled": False}}), ), RouteCase( "settings.update_storage", "solstone.apps.settings.routes", "put", "/app/settings/api/storage", {"raw_media": "days", "raw_media_days": 14}, _base_config(retention={"raw_media": "keep", "raw_media_days": None}), _base_config(retention={"raw_media": "days", "raw_media_days": 14}), ), RouteCase( "thinking.keys.set", "solstone.apps.thinking.routes", "put", "/app/thinking/api/keys", {"env_var": "GOOGLE_API_KEY", "value": "google-key"}, _base_config(env={}), _base_config( env={"GOOGLE_API_KEY": "google-key"}, providers={ "active": {"provider": "google", "model": "gemini-3.5-flash"}, "key_validation": { "google": { "valid": True, "provider": "google", "key": "google-key", "timestamp": "2026-07-19T12:00:00+00:00", } }, "local": {}, }, ), _patch_thinking_key_validation, ), RouteCase( "thinking.keys.clear", "solstone.apps.thinking.routes", "put", "/app/thinking/api/keys", {"env_var": "GOOGLE_API_KEY", "value": ""}, _base_config( env={"GOOGLE_API_KEY": "google-key"}, providers={ "active": {"provider": "google", "model": "gemini-3.5-flash"}, "key_validation": {"google": {"valid": True}}, "local": {}, }, ), _base_config( env={}, providers={ "active": {"provider": "google", "model": "gemini-3.5-flash"}, "key_validation": {}, "local": {}, }, ), _patch_thinking_key_validation, ), RouteCase( "thinking.validate_all_keys", "solstone.apps.thinking.routes", "post", "/app/thinking/api/validate-keys", None, _base_config(env={"GOOGLE_API_KEY": "google-key"}), _base_config( env={"GOOGLE_API_KEY": "google-key"}, providers={ "active": {"provider": "google", "model": "gemini-3.5-flash"}, "key_validation": {"google": {"valid": True}}, "local": {}, }, ), _patch_thinking_validate_all, ), RouteCase( "thinking.update_local_endpoint", "solstone.apps.thinking.routes", "post", "/app/thinking/api/local/endpoint", { "endpoint_url": "https://local.example/v1", "served_model_id": "model", "credential": "secret", }, _base_config(), _base_config( providers={ "active": {"provider": "google", "model": "gemini-3.5-flash"}, "key_validation": {}, "local": { "endpoint_url": "https://local.example", "served_model_id": "model", "credential": "secret", }, } ), ), RouteCase( "thinking.clear_local_endpoint", "solstone.apps.thinking.routes", "delete", "/app/thinking/api/local/endpoint", None, _base_config( providers={ "active": {"provider": "google", "model": "gemini-3.5-flash"}, "key_validation": {}, "local": { "endpoint_url": "https://local.example", "served_model_id": "model", "credential": "secret", }, } ), _base_config( providers={ "active": {"provider": "google", "model": "gemini-3.5-flash"}, "key_validation": {}, "local": {}, } ), ), RouteCase( "thinking.update_providers", "solstone.apps.thinking.routes", "put", "/app/thinking/api/providers", {"lane": "byo", "provider": "openai"}, _base_config(), _base_config( providers={ "active": {"provider": "openai", "model": "gpt-4o"}, "key_validation": {}, "local": {}, } ), ), RouteCase( "thinking.update_generators", "solstone.apps.thinking.routes", "put", "/app/thinking/api/generators", {"summary": {"disabled": True}}, _base_config(), _base_config(talent_overrides={"talent.system.summary": {"disabled": True}}), ), RouteCase( "sol.api_set_name", "solstone.apps.sol.routes", "post", "/app/sol/api/set-name", {"name": "helper", "status": "chosen"}, _base_config( agent={"name": "sol", "name_status": "default", "named_date": None} ), _base_config( agent={ "name": "helper", "name_status": "chosen", "named_date": "2026-07-19", } ), ), RouteCase( "sol.api_reset", "solstone.apps.sol.routes", "post", "/app/sol/api/reset", None, _base_config( agent={ "name": "helper", "name_status": "chosen", "named_date": "2026-07-19", } ), _base_config( agent={"name": "sol", "name_status": "default", "named_date": None} ), ), RouteCase( "sol.api_set_owner", "solstone.apps.sol.routes", "post", "/app/sol/api/set-owner", {"name": "Owner", "bio": "Bio"}, _base_config(identity={"name": "Before"}), _base_config(identity={"name": "Owner", "bio": "Bio"}), ), ) @pytest.mark.parametrize("case", ROUTE_CASES, ids=lambda case: case.path_id) def test_route_mutation_paths_report_changed_and_noop( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, case: RouteCase ) -> None: module = importlib.import_module(case.module_name) _patch_noop_action(monkeypatch, module) if case.patch is not None: case.patch(monkeypatch, module) if case.module_name == "solstone.apps.sol.routes": monkeypatch.setattr(module, "datetime", _FixedDateTime) changes = _spy_changed(monkeypatch, module) client = _client(tmp_path, monkeypatch) seed_journal_config(case.changed_seed, tmp_path) response = getattr(client, case.method)(case.path, json=case.payload) assert response.status_code == 200 assert changes[-1] is True seed_journal_config(case.noop_seed, tmp_path) response = getattr(client, case.method)(case.path, json=case.payload) assert response.status_code == 200 assert changes[-1] is False def test_tools_retention_mutation_paths_report_changed_and_noop( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: module = importlib.import_module("solstone.think.tools.call") monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path)) monkeypatch.setattr(module, "log_call_action", lambda **_kwargs: None) changes = _spy_changed(monkeypatch, module) cases = [ ( "clear", { "retention": { "per_stream": { "desktop": {"raw_media": "days", "raw_media_days": 7} } } }, {"retention": {}}, lambda: module.config(mode=None, days=None, stream="desktop", clear=True), ), ( "stream", {"retention": {}}, { "retention": { "per_stream": { "desktop": {"raw_media": "days", "raw_media_days": 7} } } }, lambda: module.config(mode="days", days=7, stream="desktop", clear=False), ), ( "default", {"retention": {"raw_media": "keep", "raw_media_days": None}}, {"retention": {"raw_media": "processed", "raw_media_days": None}}, lambda: module.config( mode="processed", days=None, stream=None, clear=False ), ), ] for _path_id, changed_seed, noop_seed, invoke in cases: seed_journal_config(changed_seed, tmp_path) invoke() assert changes[-1] is True seed_journal_config(noop_seed, tmp_path) invoke() assert changes[-1] is False def test_backup_state_mutation_paths_report_changed_and_noop( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: from solstone.think.backup import state from solstone.think.backup.destination import Destination monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path)) monkeypatch.setattr(state, "generate_daily_key", lambda: "D" * 64) monkeypatch.setattr(state, "generate_recovery_key", lambda: "R" * 64) changes = _spy_changed(monkeypatch, state) destination = Destination("repo", "local", {"k": "v"}) retention = {"hourly": 1, "daily": 2, "weekly": 3, "monthly": 4} offload = {"enabled": True, "budget_bytes": 10, "floor_bytes": 5} cases: list[Callable[[], None]] = [ lambda: state.generate_and_store_keys(), lambda: state.set_destination(destination), lambda: state.set_enabled(True), lambda: state.set_mode("operated"), lambda: state.set_recovery_key_confirmed(True), lambda: state.set_retention(retention), lambda: state.set_offload(offload), lambda: state.set_recovery_key("B" * 64), lambda: state.clear_backup_config(), lambda: state.record_backup_result(status="ok", time=1, snapshot_id="s"), lambda: state.record_prune_result(status="ok", time=1), lambda: state.record_offload_result( status="ok", time=1, files_offloaded=1, bytes_offloaded=2, ran_out_of_media=False, ), lambda: state.record_verification_result( status="ok", time=1, checked_subset="all" ), lambda: state.record_restore_result( status="ok", time=1, reason=None, scope="all", day=None, segments_selected=1, segments_restored=1, files_expected=1, files_restored=1, bytes_expected=1, bytes_restored=1, ), ] for invoke in cases: seed_journal_config({}, tmp_path) invoke() assert changes[-1] is True invoke() assert changes[-1] is False def test_provider_install_migration_reports_changed_and_noop( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path)) install_state = importlib.import_module("solstone.think.providers.install_state") changes = _spy_changed(monkeypatch, install_state) seed_journal_config( { "providers": { "bundled": { "local": { "install_state": "installed", "model_id": "local/model", "vulkan_device_index": "1", }, "parakeet": {"model_repo": "repo"}, }, "local": {}, } }, tmp_path, ) install_state.migrate_legacy_provider_install_state(journal_path=tmp_path) assert changes[-1] is True install_state.migrate_legacy_provider_install_state(journal_path=tmp_path) assert changes[-1] is False def test_pairing_oura_and_sol_voice_paths_report_changed_and_noop( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path)) pairing = importlib.import_module("solstone.think.pairing.config") oura_auth = importlib.import_module("solstone.think.importers.oura_auth") sol_voice = importlib.import_module( "solstone.convey." + "sol" + "_initiated.settings" ) tokens = SimpleNamespace( access_token="a", refresh_token="r", expires_at="2026-07-19T00:00:00Z", token_type="Bearer", ) cases = [ (pairing, {"pairing": {}}, lambda: pairing.set_home_address("https://home")), ( pairing, {"pairing": {"home_address": "https://home"}}, pairing.clear_home_address, ), ( oura_auth, {"oura": {}}, lambda: oura_auth.save_oura_tokens(tokens, journal_root=tmp_path), ), (sol_voice, {}, lambda: sol_voice.save_settings({"daily_cap": 3})), ] for module, seed, invoke in cases: seed_journal_config(seed, tmp_path) changes = _spy_changed(monkeypatch, module) invoke() assert changes[-1] is True invoke() assert changes[-1] is False def test_service_mutation_paths_report_changed_and_noop( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path)) spl = importlib.import_module("solstone.think.services.spl") spp = importlib.import_module("solstone.think.services.spp") monkeypatch.setattr(spp, "_now_iso", lambda: "2026-07-19T12:00:00+00:00") monkeypatch.setattr( spl, "LinkState", SimpleNamespace( load_or_create=lambda: SimpleNamespace(instance_id="i", home_label="h") ), ) monkeypatch.setattr( spl, "load_or_generate_ca", lambda *_args, **_kwargs: SimpleNamespace(pubkey_spki_pem="pem"), ) monkeypatch.setattr(spl, "enroll_home", lambda *_args, **_kwargs: "token") monkeypatch.setattr(spl, "save_service_token", lambda _token: None) from solstone.think.models import LOCAL_MODEL spp_payload = { "account_id": "acct", "endpoint_url": "https://local.example/v1", "served_model_id": "model", "credential": "credential", "created_at": "2026-07-01T00:00:00+00:00", } spp_noop = _base_config( providers={ "active": {"provider": "local", "model": LOCAL_MODEL}, "local": { "endpoint_url": "https://local.example", "served_model_id": "model", "credential": "credential", }, }, services={ "confidential": { "enabled_at": "2026-07-19T12:00:00+00:00", "account_id": "acct", "endpoint_url": "https://local.example", "served_model_id": "model", "credential_created_at": "2026-07-01T00:00:00+00:00", spp.CREDENTIAL_FINGERPRINT_FIELD: spp._fingerprint_key("credential"), "prior_active": {"provider": "local", "model": LOCAL_MODEL}, "prior_local_endpoint": { "endpoint_url": "https://local.example", "served_model_id": "model", "credential": "credential", }, } }, ) cases = [ ( spl, _base_config(link={"posture": "direct"}), _base_config(link={"posture": "spl"}), spl.enable_spl, ), ( spl, _base_config(link={"posture": "spl"}), _base_config(link={"posture": "direct"}), spl.disable_spl, ), ( spp, _base_config(), spp_noop, lambda: spp.provision_confidential_handoff(spp_payload), ), (spp, spp_noop, _base_config(), spp.disable_confidential), ] for module, changed_seed, noop_seed, invoke in cases: seed_journal_config(changed_seed, tmp_path) changes = _spy_changed(monkeypatch, module) invoke() assert changes[-1] is True seed_journal_config(noop_seed, tmp_path) invoke() assert changes[-1] is False def test_root_finalize_reports_changed_and_noop( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: root = importlib.import_module("solstone.convey.root") utils = importlib.import_module("solstone.think.utils") establish = importlib.import_module("solstone.think.link.establish") monkeypatch.setattr(establish, "is_committed", lambda: True) monkeypatch.setattr(utils, "now_ms", lambda: 1700000000000) monkeypatch.setattr( root, "locked_modify_convey_config", lambda *_args, **_kwargs: None ) monkeypatch.setattr(root, "start_secure_listener", lambda *_args, **_kwargs: None) changes = _spy_changed(monkeypatch, root) client = _client(tmp_path, monkeypatch) payload = {"name": "Owner", "preferred": "owner", "retention_mode": "keep"} seed_journal_config(_base_config(convey={"allow_network_access": True}), tmp_path) response = client.post("/init/finalize", json=payload) assert response.status_code == 200 assert changes[-1] is True seed_journal_config( _base_config( identity={"name": "Owner", "preferred": "owner"}, setup={"completed_at": 1700000000000}, retention={"raw_media": "keep", "raw_media_days": None}, ), tmp_path, ) response = client.post("/init/finalize", json=payload) assert response.status_code == 200 assert changes[-1] is False def test_import_resolve_config_reports_changed_and_noop( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: module = importlib.import_module("solstone.apps.import.resolve") monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path)) changes = _spy_changed(monkeypatch, module) def write_diff() -> Path: state_dir = tmp_path / "imports" / "pending" module._write_config_diff( state_dir / "config" / "diff.json", { "identity.name": { "source": "After", "target": "Before", "category": "preference", } }, ) return state_dir seed_journal_config(_base_config(identity={"name": "Before"}), tmp_path) module.resolve_config(write_diff(), "identity.name", "apply") assert changes[-1] is True seed_journal_config(_base_config(identity={"name": "After"}), tmp_path) module.resolve_config(write_diff(), "identity.name", "apply") assert changes[-1] is False def test_maintenance_mutation_paths_report_changed_and_noop( tmp_path: Path, monkeypatch: pytest.MonkeyPatch, capsys: pytest.CaptureFixture[str] ) -> None: monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path)) observer = importlib.import_module( "solstone.apps.observer.maint.000_migrate_remote_to_observer" ) settings = importlib.import_module( "solstone.apps.settings.maint.008_migrate_pairing_home_address" ) thinking = importlib.import_module( "solstone.apps.thinking.maint.000_unify_provider_config" ) cases = [ ( "observer", observer, _base_config(observe={"remote": {"enabled": True}}), _base_config(observe={}), lambda: observer._migrate_config(tmp_path), ), ( "settings", settings, _base_config(pairing={"host_url": "https://home.example"}), _base_config(pairing={"home_address": "home.example"}), settings.main, ), ( "thinking", thinking, _base_config( providers={ "google_vertex": {"project_id": "p"}, "key_validation": {"google_vertex": {"valid": True}}, } ), _base_config( providers={"active": {"provider": "local", "model": "local/qwen3.5-4b"}} ), thinking.main, ), ] for path_id, module, changed_seed, noop_seed, invoke in cases: changes = _spy_changed(monkeypatch, module) seed_journal_config(changed_seed, tmp_path) invoke() capsys.readouterr() assert changes[-1] is True, path_id seed_journal_config(noop_seed, tmp_path) invoke() capsys.readouterr() assert changes[-1] is False, path_id def test_setup_brain_reports_changed_and_noop( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: setup = importlib.import_module("solstone.think.setup") journal_config = importlib.import_module("solstone.think.journal_config") real_mutate = journal_config.mutate_journal_config changes: list[bool] = [] def wrapped(mutator, *args, **kwargs): result = real_mutate(mutator, *args, **kwargs) changes.append(result.changed) return result monkeypatch.setattr(journal_config, "mutate_journal_config", wrapped) monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path)) ctx = setup.SetupContext( mode=setup.SetupMode.NON_INTERACTIVE, project_root=tmp_path, is_source_checkout=True, journal_path=tmp_path, journal_source="test", config_path=tmp_path / "config" / "journal.json", manifest_path=tmp_path / "setup.json", port=5015, port_source="test", port_supplied=False, step_timeout_seconds=1, variant="cpu", variant_source="test", yes=True, skip_models=True, skip_brain=True, skip_skills=True, skip_service=True, skip_wrapper=True, accept_existing_journal=True, force=False, stdin_is_tty=False, stdout_is_tty=False, args_resolved={}, doctor_advisories=[], ) seed_journal_config(_base_config(providers={}), tmp_path) setup.step_brain(ctx, 1) assert changes[-1] is True from solstone.think.models import LOCAL_MODEL seed_journal_config( _base_config(providers={"active": {"provider": "local", "model": LOCAL_MODEL}}), tmp_path, ) setup.step_brain(ctx, 1) assert changes[-1] is False def test_push_reach_reports_changed_and_noop( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: reach = importlib.import_module("solstone.think.push.reach") now = int(datetime(2026, 6, 20, 11, 30, tzinfo=timezone.utc).timestamp()) expires_at = "2026-06-20T12:00:00Z" expires_epoch = int(datetime(2026, 6, 20, 12, tzinfo=timezone.utc).timestamp()) monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path)) monkeypatch.setenv("SERVICES_PORTAL_URL", "https://portal.test") monkeypatch.setattr(reach.time, "time", lambda: now) monkeypatch.setattr( reach, "LinkState", SimpleNamespace(load=lambda: SimpleNamespace(instance_id="instance")), ) monkeypatch.setattr( reach, "load_or_generate_ca", lambda *_args, **_kwargs: SimpleNamespace(pubkey_spki_pem="pem"), ) monkeypatch.setattr(reach, "mint_reach_assertion", lambda *_args, **_kwargs: "jwt") state = { "token": "reach-token", "expires_at": expires_at, "expires_epoch": expires_epoch, "instance_id": "instance", } class Response: status = 200 def __enter__(self): return self def __exit__(self, *_args): return False def read(self): return json.dumps( { "token": state["token"], "token_type": "Bearer", "expires_at": expires_at, "expires_in": 1800, "instance_id": state["instance_id"], } ).encode("utf-8") def getcode(self): return 200 monkeypatch.setattr( reach.urllib_request, "urlopen", lambda *_args, **_kwargs: Response() ) changes = _spy_changed(monkeypatch, reach) seed_journal_config({}, tmp_path) assert reach.ensure_reach_token() == "reach-token" assert changes[-1] is True seed_journal_config({"services": {"push": {"reach_token": state}}}, tmp_path) assert reach.ensure_reach_token() == "reach-token" assert changes[-1] is False