aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_mail_is_not_a_relay.py
diff options
context:
space:
mode:
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.py75
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"]