Skip to content

tests/spa/test_apply_group_settings.py

Source from this local checkout, regenerated when the reader rebuilds. Line links use #L<number>; a GitHub line range opens its first line.

1 """Contract tests for SpaCommander.apply_group_settings — recomposition, generation.2 3 Design sections 3, 5, 9; test matrix T6, T15, T27. The stage is a real vertex4 with one real group and no worker started: what these tests ask is what the5 effective configuration BECOMES, and no process is needed to answer that.6 """7 8 import pytest9 10 from genro_asgi_multiworker_spa.orchestration import GroupHandler, SpaCommander11 12 MEMORY_CEILING = 1_000_00013 14 15 @pytest.fixture16 def commander(tmp_path):17     """A vertex with one group, a profiles folder and the two immutable levels."""18     spa_commander = SpaCommander(19         tmp_path / "frozen_users",20         profiles_path=tmp_path / "profiles",21         recipe_settings={"worker_memory_admission_percent": 70.0, "worker_max_number": 3},22         env_settings={"worker_max_users": 16},23     )24     GroupHandler(25         spa_commander,26         "standard",27         memory_concession_bytes=MEMORY_CEILING,28         worker_memory_admission_percent=70.0,29         worker_max_number=3,30         worker_max_users=16,31         instance_dir=tmp_path / "i",32         frozen_users_path=tmp_path / "frozen_users",33     )34     return spa_commander35 36 37 async def test_recompute_independent_of_previous_profile(commander):38     # wf:contract: T6 — apply(P2) after apply(P1) equals apply(P2) alone, key by39     # wf:contract: key: every apply recomposes defaults ⊕ recipe_settings ⊕40     # wf:contract: profile ⊕ env_settings; a key present in P1 and absent in P241     # wf:contract: falls back to env, recipe or default, in that order (T5 sibling42     # wf:contract: at the HTTP level lives in phase 6).43     first = {"worker_memory_admission_percent": 60.0, "worker_min_life_seconds": 5.0}44     second = {"worker_min_life_seconds": 9.0}45     commander.profile_store.write("p1", first)46     commander.profile_store.write("p2", second)47 48     await commander.apply_group_settings(profile_name="p1", source="reload")49     after_both = await commander.apply_group_settings(profile_name="p2", source="reload")50 51     # The recipe level is back in charge of what P1 had overridden, and the env52     # level still wins over the recipe on its own key.53     assert after_both["effective_settings"]["worker_memory_admission_percent"] == 70.054     assert after_both["effective_settings"]["worker_min_life_seconds"] == 9.055     assert after_both["effective_settings"]["worker_max_users"] == 1656     # A key nobody names anywhere is the dataclass default.57     assert after_both["effective_settings"]["cpu_close_percent"] is None58 59     alone = SpaCommander(60         commander.freeze_handler.root_path,61         profiles_path=commander.profile_store.folder,62         recipe_settings=dict(commander.recipe_settings),63         env_settings=dict(commander.env_settings),64     )65     GroupHandler(66         alone,67         "standard",68         memory_concession_bytes=MEMORY_CEILING,69         instance_dir=commander.configured_group.worker_settings["instance_dir"],70         frozen_users_path=commander.freeze_handler.root_path,71     )72     fresh = await alone.apply_group_settings(profile_name="p2", source="reload")73 74     assert fresh["effective_settings"] == after_both["effective_settings"]75     assert fresh["active_profile"] == "p2"76     assert alone.active_profile == "p2"77 78 79 async def test_derived_setpoint_worker_memory_max_percent(commander):80     # wf:contract: T15 — applying worker_max_number without an explicit81     # wf:contract: worker_memory_max_percent makes the next judgment read82     # wf:contract: quota/N; an explicit value wins; removing the explicit value83     # wf:contract: restores the derivation.84     group = commander.configured_group85 86     await commander.apply_group_settings(profile={"worker_max_number": 4})87     assert group.worker_memory_max_percent == 100.0 / 488 89     await commander.apply_group_settings(90         profile={"worker_max_number": 4, "worker_memory_max_percent": 80.0}91     )92     assert group.worker_memory_max_percent == 80.093 94     await commander.apply_group_settings(profile={"worker_max_number": 4})95     assert group.worker_memory_max_percent == 100.0 / 496 97 98 async def test_generation_advances_on_idempotent_apply(commander):99     # wf:contract: T27 — applying the same profile twice yields empty100     # wf:contract: changed_settings, outcome "applied", and a generation that101     # wf:contract: advances anyway: the audit counts successful attempts, not102     # wf:contract: differences.103     commander.profile_store.write("steady", {"cpu_close_percent": 30.0})104 105     first = await commander.apply_group_settings(profile_name="steady", source="reload")106     second = await commander.apply_group_settings(profile_name="steady", source="reload")107 108     assert first["changed_settings"] == {"cpu_close_percent": 30.0}109     assert second["changed_settings"] == {}110     assert second["outcome"] == "applied"111     assert (first["generation"], second["generation"]) == (2, 3)112     assert commander.configuration_generation == 3113     # Same settings, same fingerprint: the digest says what is in force, not114     # how many times it was asked for.115     assert commander.last_apply["generation"] == 3116     assert commander.last_apply["outcome"] == "applied"117     assert commander.last_apply["digest"] == commander._settings_digest(118         second["effective_settings"]119     )