diff options
Diffstat (limited to 'packages/meshbay-hub/tests/test_mail_is_not_a_relay.py')
| -rw-r--r-- | packages/meshbay-hub/tests/test_mail_is_not_a_relay.py | 75 |
1 files changed, 75 insertions, 0 deletions
diff --git a/packages/meshbay-hub/tests/test_mail_is_not_a_relay.py b/packages/meshbay-hub/tests/test_mail_is_not_a_relay.py index 7c1a9f4..4e5bd6d 100644 --- a/packages/meshbay-hub/tests/test_mail_is_not_a_relay.py +++ b/packages/meshbay-hub/tests/test_mail_is_not_a_relay.py @@ -542,3 +542,78 @@ def test_the_hub_refuses_to_start_with_more_than_one_worker(): with pytest.raises(SystemExit) as exit_: single_worker_or_exit(workers) assert exit_.value.code == 2 + + +# ── The operator is told, not left to notice ───────────────────────────────── + +@pytest.mark.asyncio +async def test_spending_the_sign_up_share_notifies_the_administrators( + client, db_session): + """ + A refusal is otherwise a line in the journal. A hub that has stopped + sending sign-up codes looks, from every screen anyone opens, exactly like + one nobody is signing up to. + """ + from meshbay_hub.config import MailConfig + + headers = await _admin(client, db_session, "mailwatcher") + await client.delete("/v1/notifications", headers=headers) + + cfg = MailConfig() + general = cfg.hourly_budget - cfg.hourly_reserved_for_recovery + for i in range(general): + await _charge(db_session, "registration", f"ceiling{i}@example.test") + assert not await _charge(db_session, "registration", "over@example.test") + + r = await client.get("/v1/notifications", headers=headers) + assert r.status_code == 200, r.text + kinds = [n["kind"] for n in r.json()["notifications"]] + assert "mail_budget_general" in kinds, ( + "the sign-up ceiling fell and nobody was told") + assert "mail_budget_all" not in kinds, ( + "recovery still has its share; saying otherwise would be alarming and " + "wrong") + + +@pytest.mark.asyncio +async def test_the_administrators_are_told_once_an_hour_not_once_a_refusal( + client, db_session): + """A flood is what spends the budget, so a notification per refusal would + bury the one that matters under the thing that caused it.""" + from meshbay_hub.config import MailConfig + + headers = await _admin(client, db_session, "mailwatcher2") + await client.delete("/v1/notifications", headers=headers) + + cfg = MailConfig() + general = cfg.hourly_budget - cfg.hourly_reserved_for_recovery + for i in range(general): + await _charge(db_session, "registration", f"burst{i}@example.test") + for i in range(5): + assert not await _charge(db_session, "registration", f"over{i}@example.test") + + r = await client.get("/v1/notifications", headers=headers) + general_alerts = [n for n in r.json()["notifications"] + if n["kind"] == "mail_budget_general"] + assert len(general_alerts) == 1, f"{len(general_alerts)} alerts for one hour" + + +@pytest.mark.asyncio +async def test_the_panel_says_which_ceiling_has_fallen(client, db_session): + """Two states, and the difference matters to whoever is reading: one means + newcomers are turned away, the other means somebody locked out of their + account cannot get back in.""" + from meshbay_hub.config import MailConfig + + headers = await _admin(client, db_session, "mailwatcher3") + cfg = MailConfig() + + for i in range(cfg.hourly_budget - cfg.hourly_reserved_for_recovery): + await _charge(db_session, "registration", f"state{i}@example.test") + body = (await client.get("/v1/admin/mail", headers=headers)).json() + assert body["general_exhausted"] and not body["all_exhausted"] + + for i in range(cfg.hourly_reserved_for_recovery): + await _charge(db_session, "password_reset", f"rec{i}@example.test") + body = (await client.get("/v1/admin/mail", headers=headers)).json() + assert body["all_exhausted"] |