personal memory agent
0

Configure Feed

Select the types of activity you want to include in your feed.

solstone / tests / test_convey_config.py
7.5 kB 258 lines
1# SPDX-License-Identifier: AGPL-3.0-only 2# Copyright (c) 2026 sol pbc 3 4"""Tests for convey configuration validation and defaults.""" 5 6from __future__ import annotations 7 8import json 9import logging 10from pathlib import Path 11 12 13def _set_convey_journal(monkeypatch, tmp_path: Path) -> Path: 14 from solstone.convey import state 15 16 monkeypatch.setenv("SOLSTONE_JOURNAL", str(tmp_path)) 17 monkeypatch.setattr(state, "journal_root", str(tmp_path)) 18 return tmp_path 19 20 21def _write_convey_config(journal: Path, payload: dict) -> None: 22 config_path = journal / "config" / "convey.json" 23 config_path.parent.mkdir(parents=True, exist_ok=True) 24 config_path.write_text(json.dumps(payload), encoding="utf-8") 25 26 27def _read_convey_config(journal: Path) -> dict: 28 return json.loads((journal / "config" / "convey.json").read_text("utf-8")) 29 30 31def _commit_journal_identity() -> None: 32 from solstone.think.link.ca import load_or_generate_ca 33 from solstone.think.link.paths import ca_dir 34 35 load_or_generate_ca(ca_dir()) 36 37 38def test_reporting_enabled_defaults_true_when_absent(monkeypatch, tmp_path): 39 from solstone.convey.config import reporting_enabled 40 41 _set_convey_journal(monkeypatch, tmp_path) 42 43 assert reporting_enabled() is True 44 45 46def test_seed_default_app_navigation_seeds_absent_apps(): 47 from solstone.convey.config import ( 48 DEFAULT_APP_ORDER, 49 DEFAULT_RAIL_APPS, 50 seed_default_app_navigation, 51 ) 52 53 config = {} 54 55 changed = seed_default_app_navigation(config) 56 57 assert changed is True 58 assert config["apps"]["starred"] == DEFAULT_RAIL_APPS 59 assert config["apps"]["order"] == DEFAULT_APP_ORDER 60 61 62def test_curation_pinned_in_default_rail(): 63 from solstone.convey.config import DEFAULT_APP_ORDER, DEFAULT_RAIL_APPS 64 65 assert "curation" in DEFAULT_RAIL_APPS 66 assert DEFAULT_RAIL_APPS.index("curation") == 3 67 assert "curation" in DEFAULT_APP_ORDER 68 69 70def test_services_absent_from_default_app_order(): 71 from solstone.convey.config import DEFAULT_APP_ORDER, DEFAULT_RAIL_APPS 72 73 assert "services" not in DEFAULT_RAIL_APPS 74 assert "services" not in DEFAULT_APP_ORDER 75 76 77def test_apply_app_order_skips_missing_persisted_app_ids(): 78 from solstone.convey.config import apply_app_order 79 80 apps = { 81 "home": {"label": "home"}, 82 "news": {"label": "news"}, 83 "reflections": {"label": "reflections"}, 84 } 85 config = { 86 "apps": { 87 "order": ["home", "services", "news"], 88 "starred": ["home", "services"], 89 } 90 } 91 92 ordered = apply_app_order(apps, config) 93 94 assert list(ordered) == ["home", "news", "reflections"] 95 assert "services" not in ordered 96 97 98def test_seed_default_app_navigation_preserves_present_empty_lists(): 99 from solstone.convey.config import seed_default_app_navigation 100 101 config = {"apps": {"starred": [], "order": []}} 102 103 changed = seed_default_app_navigation(config) 104 105 assert changed is False 106 assert config["apps"]["starred"] == [] 107 assert config["apps"]["order"] == [] 108 109 110def test_seed_default_app_navigation_preserves_curated_starred(): 111 from solstone.convey.config import ( 112 DEFAULT_APP_ORDER, 113 seed_default_app_navigation, 114 ) 115 116 config = {"apps": {"starred": ["chat"]}} 117 118 changed = seed_default_app_navigation(config) 119 120 assert changed is True 121 assert config["apps"]["starred"] == ["chat"] 122 assert config["apps"]["order"] == DEFAULT_APP_ORDER 123 124 125def test_seed_default_app_navigation_preserves_curated_order(): 126 from solstone.convey.config import ( 127 DEFAULT_RAIL_APPS, 128 seed_default_app_navigation, 129 ) 130 131 config = {"apps": {"order": ["chat"]}} 132 133 changed = seed_default_app_navigation(config) 134 135 assert changed is True 136 assert config["apps"]["starred"] == DEFAULT_RAIL_APPS 137 assert config["apps"]["order"] == ["chat"] 138 139 140def test_seed_default_app_navigation_is_idempotent(): 141 from solstone.convey.config import ( 142 DEFAULT_APP_ORDER, 143 DEFAULT_RAIL_APPS, 144 seed_default_app_navigation, 145 ) 146 147 config = {} 148 149 first_changed = seed_default_app_navigation(config) 150 first_config = json.loads(json.dumps(config)) 151 second_changed = seed_default_app_navigation(config) 152 153 assert first_changed is True 154 assert second_changed is False 155 assert config == first_config 156 assert config["apps"]["starred"] == DEFAULT_RAIL_APPS 157 assert config["apps"]["order"] == DEFAULT_APP_ORDER 158 159 160def test_init_finalize_seeds_default_app_navigation(journal_copy): 161 from solstone.convey import create_app 162 from solstone.convey.config import DEFAULT_APP_ORDER, DEFAULT_RAIL_APPS 163 164 (journal_copy / "config" / "convey.json").unlink() 165 app = create_app(str(journal_copy)) 166 app.config["TESTING"] = True 167 _commit_journal_identity() 168 169 resp = app.test_client().post( 170 "/init/finalize", 171 json={}, 172 content_type="application/json", 173 ) 174 175 assert resp.status_code == 200 176 data = resp.get_json() 177 assert data["success"] is True 178 assert data["redirect"] == "/app/thinking/" 179 180 config = _read_convey_config(journal_copy) 181 assert config["apps"]["starred"] == DEFAULT_RAIL_APPS 182 assert config["apps"]["order"] == DEFAULT_APP_ORDER 183 184 185def test_init_finalize_logs_convey_seed_persist_failure( 186 journal_copy, monkeypatch, caplog 187): 188 from solstone.convey import create_app 189 from solstone.convey import root as root_module 190 191 (journal_copy / "config" / "convey.json").unlink() 192 193 def _fail_seed(_transform): 194 raise OSError("simulated persist failure") 195 196 monkeypatch.setattr(root_module, "locked_modify_convey_config", _fail_seed) 197 caplog.set_level(logging.ERROR, logger="solstone.convey.root") 198 app = create_app(str(journal_copy)) 199 app.config["TESTING"] = True 200 _commit_journal_identity() 201 202 resp = app.test_client().post( 203 "/init/finalize", 204 json={}, 205 content_type="application/json", 206 ) 207 208 assert resp.status_code == 500 209 data = resp.get_json() 210 assert data["committed"] is True 211 assert data["secondary_effect"] == "default_app_navigation" 212 assert "default app navigation seed convey-config PERSIST failed" in caplog.text 213 214 215def test_reporting_enabled_round_trips_false(monkeypatch, tmp_path): 216 from solstone.convey.config import ( 217 load_convey_config, 218 reporting_enabled, 219 validate_config, 220 ) 221 222 journal = _set_convey_journal(monkeypatch, tmp_path) 223 payload = {"reporting": {"enabled": False}} 224 valid, error = validate_config(payload) 225 assert valid is True 226 assert error is None 227 228 _write_convey_config(journal, payload) 229 230 assert load_convey_config() == payload 231 assert reporting_enabled() is False 232 233 234def test_validate_config_rejects_non_dict_reporting(): 235 from solstone.convey.config import validate_config 236 237 valid, error = validate_config({"reporting": "yes"}) 238 239 assert valid is False 240 assert error == "reporting must be an object" 241 242 243def test_validate_config_rejects_non_bool_enabled(): 244 from solstone.convey.config import validate_config 245 246 valid, error = validate_config({"reporting": {"enabled": "no"}}) 247 248 assert valid is False 249 assert error == "reporting.enabled must be a boolean" 250 251 252def test_validate_config_rejects_unknown_reporting_key(): 253 from solstone.convey.config import validate_config 254 255 valid, error = validate_config({"reporting": {"enabled": True, "bogus": 1}}) 256 257 assert valid is False 258 assert error == "reporting contains unknown key(s): bogus"