personal memory agent
0

Configure Feed

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

solstone / tests / test_native_sol_journal_host_commands.py
16 kB 482 lines
1# SPDX-License-Identifier: AGPL-3.0-only 2# Copyright (c) 2026 sol pbc 3from __future__ import annotations 4 5import ast 6 7import pytest 8 9import scripts.build_native_sol_journal_host_commands as journal_host 10 11SERVICE_COMMANDS = ("setup", "think") + tuple(f"svc{i:02d}" for i in range(41)) 12UNIVERSAL_COMMANDS = ("check", "contract", "doctor", "link") 13SERVICE_ALIASES = ("down", "up") 14 15 16def _raw_source(lines: list[str]) -> str: 17 return "\n".join([*lines, ""]) 18 19 20def _source(commands: dict[str, str], aliases: dict[str, str]) -> str: 21 command_lines = [ 22 f' "{name}": Command("module.{name.replace("-", "_")}", "{surface}"),' 23 for name, surface in sorted(commands.items()) 24 ] 25 alias_lines = [ 26 f' "{name}": Alias("module.{name.replace("-", "_")}", [], "{surface}"),' 27 for name, surface in sorted(aliases.items()) 28 ] 29 return _raw_source( 30 [ 31 "COMMANDS = {", 32 *command_lines, 33 "}", 34 "", 35 "ALIASES = {", 36 *alias_lines, 37 "}", 38 ] 39 ) 40 41 42def _base_commands() -> dict[str, str]: 43 return { 44 **{name: "service" for name in SERVICE_COMMANDS}, 45 **{name: "universal" for name in UNIVERSAL_COMMANDS}, 46 } 47 48 49def _base_aliases() -> dict[str, str]: 50 return {name: "service" for name in SERVICE_ALIASES} 51 52 53def _error(commands: dict[str, str], aliases: dict[str, str]) -> str: 54 return _raw_error(_source(commands, aliases)) 55 56 57def _raw_error(source: str) -> str: 58 with pytest.raises(RuntimeError) as error: 59 journal_host.extract_partitions(source) 60 message = str(error.value) 61 assert "\n" not in message 62 return message 63 64 65def _old_combined_service_surface_count( 66 commands: dict[str, str], aliases: dict[str, str] 67) -> int: 68 service_commands = { 69 name for name, surface in commands.items() if surface == "service" 70 } 71 service_aliases = { 72 name for name, surface in aliases.items() if surface == "service" 73 } 74 return len(service_commands | service_aliases) 75 76 77def _raw_service_command_literal_count(source: str) -> int: 78 tree = ast.parse(source) 79 count = 0 80 for registry_literal in journal_host.scan_registry_literals(tree): 81 if registry_literal.name != "COMMANDS": 82 continue 83 for key, value in zip( 84 registry_literal.node.keys, registry_literal.node.values, strict=True 85 ): 86 key_value = journal_host.literal_key(key) 87 if ( 88 key_value is not None 89 and journal_host.call_surface(value, registry_literal.surface_position) 90 == "service" 91 ): 92 count += 1 93 return count 94 95 96def test_extract_keeps_service_commands_and_aliases_as_sorted_moved_list() -> None: 97 moved = journal_host.extract(_source(_base_commands(), _base_aliases())) 98 99 assert len(moved) == 45 100 assert moved == sorted((*SERVICE_COMMANDS, *SERVICE_ALIASES)) 101 102 103def test_duplicate_commands_rejected_before_partition_validation() -> None: 104 source = _raw_source( 105 [ 106 "COMMANDS = {", 107 ' "ghost": Command("module.ghost", SURFACE),', 108 ' "ghost": Command("module.ghost_again", SURFACE),', 109 " **EXTRA_COMMANDS,", 110 ' NAME: Command("module.dynamic", "service"),', 111 "}", 112 "", 113 "ALIASES = {", 114 "}", 115 ] 116 ) 117 118 message = _raw_error(source) 119 120 assert ( 121 message == "journal-host duplicate registry keys: COMMANDS 'ghost' " 122 "[line 2 surface=<unavailable>, line 3 surface=<unavailable>]" 123 ) 124 125 126def test_duplicate_service_command_preserves_old_count_but_is_rejected() -> None: 127 source = _raw_source( 128 [ 129 "COMMANDS = {", 130 ' "think": Command("module.think", "service"),', 131 ' "setup": Command("module.setup", "service"),', 132 ' "think": Command("module.think_again", "service"),', 133 ' "svc00": Command("module.svc00", "service"),', 134 ' "svc01": Command("module.svc01", "service"),', 135 ' "svc02": Command("module.svc02", "service"),', 136 ' "svc03": Command("module.svc03", "service"),', 137 ' "svc04": Command("module.svc04", "service"),', 138 ' "svc05": Command("module.svc05", "service"),', 139 ' "svc06": Command("module.svc06", "service"),', 140 ' "svc07": Command("module.svc07", "service"),', 141 ' "svc08": Command("module.svc08", "service"),', 142 ' "svc09": Command("module.svc09", "service"),', 143 ' "svc10": Command("module.svc10", "service"),', 144 ' "svc11": Command("module.svc11", "service"),', 145 ' "svc12": Command("module.svc12", "service"),', 146 ' "svc13": Command("module.svc13", "service"),', 147 ' "svc14": Command("module.svc14", "service"),', 148 ' "svc15": Command("module.svc15", "service"),', 149 ' "svc16": Command("module.svc16", "service"),', 150 ' "svc17": Command("module.svc17", "service"),', 151 ' "svc18": Command("module.svc18", "service"),', 152 ' "svc19": Command("module.svc19", "service"),', 153 ' "svc20": Command("module.svc20", "service"),', 154 ' "svc21": Command("module.svc21", "service"),', 155 ' "svc22": Command("module.svc22", "service"),', 156 ' "svc23": Command("module.svc23", "service"),', 157 ' "svc24": Command("module.svc24", "service"),', 158 ' "svc25": Command("module.svc25", "service"),', 159 ' "svc26": Command("module.svc26", "service"),', 160 ' "svc27": Command("module.svc27", "service"),', 161 ' "svc28": Command("module.svc28", "service"),', 162 ' "svc29": Command("module.svc29", "service"),', 163 ' "svc30": Command("module.svc30", "service"),', 164 ' "svc31": Command("module.svc31", "service"),', 165 ' "svc32": Command("module.svc32", "service"),', 166 ' "svc33": Command("module.svc33", "service"),', 167 ' "svc34": Command("module.svc34", "service"),', 168 ' "svc35": Command("module.svc35", "service"),', 169 ' "svc36": Command("module.svc36", "service"),', 170 ' "svc37": Command("module.svc37", "service"),', 171 ' "svc38": Command("module.svc38", "service"),', 172 ' "svc39": Command("module.svc39", "service"),', 173 ' "check": Command("module.check", "universal"),', 174 ' "contract": Command("module.contract", "universal"),', 175 ' "doctor": Command("module.doctor", "universal"),', 176 ' "link": Command("module.link", "universal"),', 177 "}", 178 "", 179 "ALIASES = {", 180 ' "down": Alias("module.down", [], "service"),', 181 ' "up": Alias("module.up", [], "service"),', 182 "}", 183 ] 184 ) 185 186 assert _raw_service_command_literal_count(source) == 43 187 message = _raw_error(source) 188 189 assert ( 190 message == "journal-host duplicate registry keys: COMMANDS 'think' " 191 "[line 2 surface=service, line 4 surface=service]" 192 ) 193 194 195def test_duplicate_universal_command_is_rejected() -> None: 196 source = _raw_source( 197 [ 198 "COMMANDS = {", 199 ' "doctor": Command("module.doctor", "universal"),', 200 ' "doctor": Command("module.doctor_again", "universal"),', 201 "}", 202 "", 203 "ALIASES = {", 204 "}", 205 ] 206 ) 207 208 message = _raw_error(source) 209 210 assert ( 211 message == "journal-host duplicate registry keys: COMMANDS 'doctor' " 212 "[line 2 surface=universal, line 3 surface=universal]" 213 ) 214 215 216def test_duplicate_service_alias_is_rejected() -> None: 217 source = _raw_source( 218 [ 219 "COMMANDS = {", 220 "}", 221 "", 222 "ALIASES = {", 223 ' "up": Alias("module.up", [], "service"),', 224 ' "up": Alias("module.up_again", [], "service"),', 225 "}", 226 ] 227 ) 228 229 message = _raw_error(source) 230 231 assert ( 232 message == "journal-host duplicate registry keys: ALIASES 'up' " 233 "[line 5 surface=service, line 6 surface=service]" 234 ) 235 236 237def test_duplicate_keys_sort_key_names_before_source_order() -> None: 238 source = _raw_source( 239 [ 240 "COMMANDS = {", 241 ' "zeta": Command("module.zeta", "service"),', 242 ' "zeta": Command("module.zeta_again", "service"),', 243 ' "alpha": Command("module.alpha", "service"),', 244 ' "alpha": Command("module.alpha_again", "service"),', 245 "}", 246 "", 247 "ALIASES = {", 248 "}", 249 ] 250 ) 251 252 message = _raw_error(source) 253 254 assert ( 255 message == "journal-host duplicate registry keys: COMMANDS 'alpha' " 256 "[line 4 surface=service, line 5 surface=service]; COMMANDS 'zeta' " 257 "[line 2 surface=service, line 3 surface=service]" 258 ) 259 260 261def test_duplicate_command_reports_disagreeing_surfaces() -> None: 262 source = _raw_source( 263 [ 264 "COMMANDS = {", 265 ' "think": Command("module.think", "service"),', 266 ' "think": Command("module.think_again", "universal"),', 267 "}", 268 "", 269 "ALIASES = {", 270 "}", 271 ] 272 ) 273 274 message = _raw_error(source) 275 276 assert ( 277 message == "journal-host duplicate registry keys: COMMANDS 'think' " 278 "[line 2 surface=service, line 3 surface=universal]" 279 ) 280 281 282def test_duplicates_aggregate_across_plain_and_annotated_assignments() -> None: 283 source = _raw_source( 284 [ 285 "COMMANDS = {", 286 ' "think": Command("module.think", "service"),', 287 "}", 288 "COMMANDS: dict[str, Command] = {", 289 ' "think": Command("module.think_again", "service"),', 290 "}", 291 "ALIASES: dict[str, Alias] = {", 292 ' "up": Alias("module.up", [], "service"),', 293 "}", 294 "ALIASES = {", 295 ' "up": Alias("module.up_again", [], "service"),', 296 "}", 297 ] 298 ) 299 300 message = _raw_error(source) 301 302 assert ( 303 message == "journal-host duplicate registry keys: COMMANDS 'think' " 304 "[line 2 surface=service, line 5 surface=service]; ALIASES 'up' " 305 "[line 8 surface=service, line 11 surface=service]" 306 ) 307 308 309def test_simultaneous_duplicates_report_unavailable_surface() -> None: 310 source = _raw_source( 311 [ 312 "COMMANDS = {", 313 ' "think": Command("module.think", "service"),', 314 ' "think": Command("module.think_again", SURFACE),', 315 "}", 316 "ALIASES = {", 317 ' "up": Alias("module.up", [], "service"),', 318 ' "up": Alias("module.up_again", [], alias_surface()),', 319 " **EXTRA_ALIASES,", 320 ' ALIAS_NAME: Alias("module.dynamic", [], "service"),', 321 "}", 322 ] 323 ) 324 325 message = _raw_error(source) 326 327 assert ( 328 message == "journal-host duplicate registry keys: COMMANDS 'think' " 329 "[line 2 surface=service, line 3 surface=<unavailable>]; ALIASES 'up' " 330 "[line 6 surface=service, line 7 surface=<unavailable>]" 331 ) 332 333 334def test_spread_and_non_literal_keys_are_skipped_on_a_valid_source() -> None: 335 source = _raw_source( 336 [ 337 "COMMANDS = {", 338 *[ 339 f' "{name}": Command("module.{name.replace("-", "_")}", "service"),' 340 for name in SERVICE_COMMANDS 341 ], 342 " **EXTRA_COMMANDS,", 343 ' NAME: Command("module.dynamic", "service"),', 344 *[ 345 f' "{name}": Command("module.{name.replace("-", "_")}", "universal"),' 346 for name in UNIVERSAL_COMMANDS 347 ], 348 "}", 349 "", 350 "ALIASES = {", 351 *[ 352 f' "{name}": Alias("module.{name.replace("-", "_")}", [], "service"),' 353 for name in SERVICE_ALIASES 354 ], 355 " **EXTRA_ALIASES,", 356 ' ALIAS_NAME: Alias("module.dynamic", [], "service"),', 357 "}", 358 ] 359 ) 360 361 moved = journal_host.extract(source) 362 363 assert len(moved) == 45 364 assert moved == sorted((*SERVICE_COMMANDS, *SERVICE_ALIASES)) 365 366 367def test_production_registry_extracts_expected_partitions() -> None: 368 partitions = journal_host.extract_partitions() 369 370 assert len(partitions.service_commands) == 43 371 assert set(partitions.service_aliases) == {"up", "down"} 372 assert set(partitions.universal_commands) == { 373 "doctor", 374 "check", 375 "contract", 376 "link", 377 } 378 assert partitions.universal_aliases == () 379 assert len(journal_host.extract()) == 45 380 381 382def test_service_command_count_rejects_44_service_commands_and_1_alias() -> None: 383 commands = _base_commands() 384 commands["audio"] = "service" 385 aliases = _base_aliases() 386 del aliases["down"] 387 388 assert _old_combined_service_surface_count(commands, aliases) == 45 389 message = _error(commands, aliases) 390 391 assert message.startswith("journal-host service COMMANDS count 44 != 43: ") 392 assert "'audio'" in message 393 394 395def test_service_command_count_rejects_42_service_commands_and_3_aliases() -> None: 396 commands = _base_commands() 397 del commands["svc00"] 398 aliases = _base_aliases() 399 aliases["extra"] = "service" 400 401 assert _old_combined_service_surface_count(commands, aliases) == 45 402 message = _error(commands, aliases) 403 404 assert message.startswith("journal-host service COMMANDS count 42 != 43: ") 405 406 407@pytest.mark.parametrize("alias", ["up", "down"]) 408def test_service_aliases_reject_missing_expected_alias(alias: str) -> None: 409 aliases = _base_aliases() 410 del aliases[alias] 411 412 message = _error(_base_commands(), aliases) 413 414 assert ( 415 message 416 == f"journal-host service ALIASES drifted; missing=['{alias}']; extra=[]; changed_surface=[]" 417 ) 418 419 420@pytest.mark.parametrize("alias", ["up", "down"]) 421def test_service_aliases_report_reclassified_alias(alias: str) -> None: 422 aliases = _base_aliases() 423 aliases[alias] = "universal" 424 425 message = _error(_base_commands(), aliases) 426 427 assert ( 428 message 429 == f"journal-host service ALIASES drifted; missing=['{alias}']; extra=[]; changed_surface=['{alias}']" 430 ) 431 432 433@pytest.mark.parametrize("command", ["doctor", "check", "contract", "link"]) 434def test_universal_commands_reject_missing_expected_command(command: str) -> None: 435 commands = _base_commands() 436 del commands[command] 437 438 message = _error(commands, _base_aliases()) 439 440 assert ( 441 message 442 == f"journal-host universal COMMANDS drifted; missing=['{command}']; extra=[]; changed_surface=[]" 443 ) 444 445 446def test_universal_commands_report_reclassified_command() -> None: 447 commands = _base_commands() 448 commands["doctor"] = "service" 449 del commands["svc00"] 450 451 message = _error(commands, _base_aliases()) 452 453 assert ( 454 message 455 == "journal-host universal COMMANDS drifted; missing=['doctor']; extra=[]; changed_surface=['doctor']" 456 ) 457 458 459def test_commands_and_aliases_overlap_is_rejected() -> None: 460 aliases = _base_aliases() 461 aliases["svc00"] = "service" 462 463 message = _error(_base_commands(), aliases) 464 465 assert message == "journal-host COMMANDS and ALIASES overlap: ['svc00']" 466 467 468def test_empty_extraction_is_rejected() -> None: 469 message = _error({}, {}) 470 471 assert message == "journal-host command extraction is empty" 472 473 474@pytest.mark.parametrize("sentinel", ["think", "setup"]) 475def test_service_command_sentinel_is_required(sentinel: str) -> None: 476 commands = _base_commands() 477 del commands[sentinel] 478 commands["aaa"] = "service" 479 480 message = _error(commands, _base_aliases()) 481 482 assert message == f"journal-host service COMMANDS missing sentinels: ['{sentinel}']"