personal memory agent
at main 186 lines 6.1 kB view raw
1# SPDX-License-Identifier: AGPL-3.0-only 2# Copyright (c) 2026 sol pbc 3 4"""Tests for journal configuration utilities.""" 5 6import json 7import os 8 9import pytest 10 11from think.utils import get_config 12 13 14@pytest.fixture 15def config_journal(tmp_path): 16 """Create a temporary journal with config.""" 17 config_dir = tmp_path / "config" 18 config_dir.mkdir() 19 20 config_data = { 21 "identity": { 22 "name": "Test User", 23 "preferred": "Tester", 24 "bio": "a software engineer and tester", 25 "pronouns": { 26 "subject": "they", 27 "object": "them", 28 "possessive": "their", 29 "reflexive": "themselves", 30 }, 31 "aliases": ["test", "tester"], 32 "email_addresses": ["test@example.com"], 33 "timezone": "America/New_York", 34 } 35 } 36 37 config_file = config_dir / "journal.json" 38 with open(config_file, "w") as f: 39 json.dump(config_data, f, indent=2) 40 f.write("\n") 41 42 return tmp_path 43 44 45def test_get_config_default_structure(tmp_path, monkeypatch): 46 """Test get_config returns default structure when file doesn't exist.""" 47 monkeypatch.setenv("_SOLSTONE_JOURNAL_OVERRIDE", str(tmp_path)) 48 49 config = get_config() 50 51 assert "identity" in config 52 assert config["identity"]["name"] == "" 53 assert config["identity"]["preferred"] == "" 54 assert config["identity"]["pronouns"] == { 55 "subject": "", 56 "object": "", 57 "possessive": "", 58 "reflexive": "", 59 } 60 assert config["identity"]["aliases"] == [] 61 assert config["identity"]["email_addresses"] == [] 62 assert config["identity"]["timezone"] == "" 63 assert config["identity"]["bio"] == "" 64 65 # Describe defaults 66 assert "describe" in config 67 assert isinstance(config["describe"]["redact"], list) 68 assert len(config["describe"]["redact"]) > 0 69 70 71def test_get_config_default_is_deep_copy(tmp_path, monkeypatch): 72 """Test that modifying returned defaults doesn't affect future calls.""" 73 monkeypatch.setenv("_SOLSTONE_JOURNAL_OVERRIDE", str(tmp_path)) 74 75 config1 = get_config() 76 config1["identity"]["name"] = "Modified" 77 config1["describe"]["redact"].append("extra rule") 78 79 config2 = get_config() 80 assert config2["identity"]["name"] == "" 81 assert "extra rule" not in config2["describe"]["redact"] 82 83 84def test_get_config_loads_existing(config_journal, monkeypatch): 85 """Test get_config loads existing configuration.""" 86 monkeypatch.setenv("_SOLSTONE_JOURNAL_OVERRIDE", str(config_journal)) 87 88 config = get_config() 89 90 assert config["identity"]["name"] == "Test User" 91 assert config["identity"]["preferred"] == "Tester" 92 assert config["identity"]["pronouns"] == { 93 "subject": "they", 94 "object": "them", 95 "possessive": "their", 96 "reflexive": "themselves", 97 } 98 assert config["identity"]["aliases"] == ["test", "tester"] 99 assert config["identity"]["email_addresses"] == ["test@example.com"] 100 assert config["identity"]["timezone"] == "America/New_York" 101 assert config["identity"]["bio"] == "a software engineer and tester" 102 103 104def test_get_config_existing_is_master(tmp_path, monkeypatch): 105 """Test that existing journal.json is returned as-is without merging defaults.""" 106 monkeypatch.setenv("_SOLSTONE_JOURNAL_OVERRIDE", str(tmp_path)) 107 108 # Create config with only a name - no other identity fields, no describe 109 config_dir = tmp_path / "config" 110 config_dir.mkdir() 111 112 partial_config = { 113 "identity": { 114 "name": "Partial User", 115 } 116 } 117 118 config_file = config_dir / "journal.json" 119 with open(config_file, "w") as f: 120 json.dump(partial_config, f) 121 122 config = get_config() 123 124 # User's value is preserved 125 assert config["identity"]["name"] == "Partial User" 126 # Missing fields are NOT filled from defaults - journal.json is master 127 assert "preferred" not in config["identity"] 128 assert "describe" not in config 129 130 131def test_get_config_empty_journal(tmp_path, monkeypatch): 132 """Test get_config returns defaults with an empty journal directory.""" 133 monkeypatch.setenv("_SOLSTONE_JOURNAL_OVERRIDE", str(tmp_path)) 134 135 config = get_config() 136 assert "identity" in config 137 assert config["identity"]["name"] == "" 138 139 140def test_get_config_handles_invalid_json(tmp_path, monkeypatch): 141 """Test get_config returns defaults when JSON is invalid.""" 142 monkeypatch.setenv("_SOLSTONE_JOURNAL_OVERRIDE", str(tmp_path)) 143 144 # Create config with invalid JSON 145 config_dir = tmp_path / "config" 146 config_dir.mkdir() 147 148 config_file = config_dir / "journal.json" 149 with open(config_file, "w") as f: 150 f.write("{ invalid json }") 151 152 # Should return default structure and log warning 153 config = get_config() 154 155 assert "identity" in config 156 assert config["identity"]["name"] == "" 157 assert config["identity"]["pronouns"] == { 158 "subject": "", 159 "object": "", 160 "possessive": "", 161 "reflexive": "", 162 } 163 assert config["identity"]["bio"] == "" 164 assert "describe" in config 165 166 167def test_get_config_with_fixtures(): 168 """Test get_config with tests/fixtures/journal path.""" 169 # Set _SOLSTONE_JOURNAL_OVERRIDE to fixtures 170 os.environ["_SOLSTONE_JOURNAL_OVERRIDE"] = "tests/fixtures/journal" 171 172 config = get_config() 173 174 # Fixtures has journal.json - returned as-is 175 assert "identity" in config 176 assert isinstance(config["identity"]["name"], str) 177 assert isinstance(config["identity"]["preferred"], str) 178 assert isinstance(config["identity"]["pronouns"], dict) 179 assert "subject" in config["identity"]["pronouns"] 180 assert "object" in config["identity"]["pronouns"] 181 assert "possessive" in config["identity"]["pronouns"] 182 assert "reflexive" in config["identity"]["pronouns"] 183 assert isinstance(config["identity"]["aliases"], list) 184 assert isinstance(config["identity"]["email_addresses"], list) 185 assert isinstance(config["identity"]["timezone"], str) 186 assert isinstance(config["identity"]["bio"], str)