Files

79 lines
2.9 KiB
Python
Raw Permalink Normal View History

"""G5 -- chaos flags toggle at runtime, without a restart."""
import pytest
from harness.config import POSTGRES_DSN
from harness.pg import statement_count
pytestmark = pytest.mark.contract
ALL_FLAGS = {
"slow_sanctions", "sanctions_errors", "n_plus_one", "pool_starvation",
"row_lock_contention", "blocking_event_loop", "retry_storm", "no_batching",
"unbounded_cache", "slow_consumer", "cold_cache", "poison_data", "chatty_health",
}
async def test_chaos_lists_all_thirteen_flags(chaos):
"""G5: GET /api/admin/chaos lists all thirteen flags by name."""
state = await chaos.get()
assert set(state["flags"]) == ALL_FLAGS, state["flags"]
async def test_toggling_one_flag_changes_only_that_flag(chaos):
"""G5: toggling a single flag changes only that flag."""
before = (await chaos.get())["flags"]
await chaos.set_flag("slow_sanctions", True)
after = (await chaos.get())["flags"]
assert after["slow_sanctions"] is True
for flag in ALL_FLAGS - {"slow_sanctions"}:
assert after[flag] == before[flag], f"{flag} changed unexpectedly"
async def test_profile_clean_sets_all_false(chaos):
"""G5: the `clean` profile sets every flag to false."""
state = await chaos.set_profile("clean")
assert all(v is False for v in state["flags"].values()), state["flags"]
async def test_profile_nightmare_sets_all_true(chaos):
"""G5: the `nightmare` profile sets every flag to true."""
state = await chaos.set_profile("nightmare")
assert all(v is True for v in state["flags"].values()), state["flags"]
async def test_profile_light_sets_exactly_two_flags(chaos):
"""G5: the `light` profile sets exactly slow_sanctions and cold_cache."""
state = await chaos.set_profile("light")
enabled = {flag for flag, v in state["flags"].items() if v}
assert enabled == {"slow_sanctions", "cold_cache"}, enabled
async def test_flag_change_is_live_no_restart(api, chaos, manifest):
"""G5: enabling n_plus_one via the API changes the query count for the very
next insights request -- with no container restart -- proving live
reconfiguration rather than timing noise."""
account_id = manifest["clean_account_id"]
await chaos.set_profile("clean")
with statement_count(POSTGRES_DSN) as baseline:
resp = await api.get(f"/api/accounts/{account_id}/insights")
assert resp.status_code == 200
await chaos.set_flag("n_plus_one", True)
with statement_count(POSTGRES_DSN) as degraded:
resp = await api.get(f"/api/accounts/{account_id}/insights")
assert resp.status_code == 200
assert degraded.calls > baseline.calls, (
f"expected more SQL statements after enabling n_plus_one live "
f"(baseline={baseline.calls}, degraded={degraded.calls})"
)
async def test_unknown_flag_returns_400(chaos):
"""G5: setting an unknown flag name returns 400."""
resp = await chaos.set_flag("not_a_real_flag", True)
assert resp.status_code == 400