thermograph/backend/tests/notifications/test_send_safety.py
admin_emi bb0a35b55f
All checks were successful
secrets-guard / encrypted (push) Successful in 12s
shell-lint / shellcheck (push) Successful in 12s
Build + push images (Forgejo registry) / build-push (frontend) (push) Successful in 44s
Build + push images (Forgejo registry) / build-push (backend) (push) Successful in 2m27s
Deploy / deploy (backend) (push) Successful in 2m55s
Deploy / deploy (frontend) (push) Successful in 3m6s
Testing plan wave 0: close two live defects, hard-block outbound sends, gate the prod path (#88)
Co-authored-by: admin_emi <admin_emi@noreply.dev.jinemi.com>
Co-committed-by: admin_emi <admin_emi@noreply.dev.jinemi.com>
2026-08-02 01:28:12 +00:00

108 lines
4.4 KiB
Python

"""The guarantee that a test run cannot reach a real subscriber.
tests/conftest.py hard-blocks every outbound transport. That block is worth
nothing unless something asserts it, because it fails in exactly one direction:
if a future edit weakens it, every other test still passes and the only symptom
is a real Discord message, a real email, or a real push to somebody's phone —
delivered silently, because notify.py and discord.py swallow send exceptions.
So this file tests the test harness. If anything here fails, do not weaken the
assertion; fix the block in conftest.py.
"""
import smtplib
import pytest
import indexnow
from notifications import discord, mailer, push
from tests.conftest import OutboundBlocked
# --- layer 1: every send gate reports "not configured" ------------------------
def test_no_discord_surface_is_enabled():
"""A stray THERMOGRAPH_DISCORD_* in the operator's shell must not arm the
bot. These four gates are what stand between run_pass() and a real post to
the subscription channel, which notifies real people."""
assert discord.enabled() is False
assert discord.dm_enabled() is False
assert discord.subscription_enabled() is False
assert discord.weather_enabled() is False
def test_no_discord_credentials_are_live():
assert discord.BOT_TOKEN == ""
assert discord.WEBHOOK_URL == ""
assert discord.SUBSCRIPTION_CHANNEL_ID == ""
assert discord.WEATHER_CHANNEL_ID == ""
def test_mail_never_goes_over_smtp():
"""`console` prints; `smtp` hands the message to a real host. digest.py
branches on exactly this value."""
assert mailer.BACKEND == "console"
assert mailer.enabled() is False
# --- layer 2: the transports themselves raise --------------------------------
#
# Layer 1 is config, and config can be re-enabled by a test that monkeypatches a
# token to exercise a branch. These are the backstop for that case.
def test_discord_transport_raises_rather_than_posting():
with pytest.raises(OutboundBlocked):
discord._client.post("https://discord.com/api/v10/channels/1/messages",
json={"content": "should never leave the box"})
def test_indexnow_transport_raises_rather_than_posting():
with pytest.raises(OutboundBlocked):
indexnow._client.post("https://api.indexnow.org/indexnow", json={})
def test_push_transport_raises_rather_than_sending():
with pytest.raises(OutboundBlocked):
push.webpush(subscription_info={}, data="{}")
def test_smtp_raises_rather_than_connecting():
with pytest.raises(OutboundBlocked):
smtplib.SMTP("127.0.0.1", 25)
with pytest.raises(OutboundBlocked):
smtplib.SMTP_SSL("127.0.0.1", 465)
# --- the paths that used to be able to send silently -------------------------
def test_bot_post_cannot_reach_discord_even_with_a_token(monkeypatch):
"""_bot_post is the funnel for DMs and channel posts. Re-arm the token the
way a careless test might and confirm the transport still refuses.
Note _bot_post swallows exceptions by design, so it returns None rather than
raising — the assertion that matters is that the sentinel ran at all, i.e.
that no HTTP request was attempted against the real API.
"""
monkeypatch.setattr(discord, "BOT_TOKEN", "not-a-real-token")
attempted = []
monkeypatch.setattr(discord._client, "post",
lambda *a, **kw: attempted.append(a) or (_ for _ in ()).throw(
OutboundBlocked("blocked")))
assert discord._bot_post("/channels/1/messages", {"content": "x"}) is None
assert attempted, "_bot_post should have gone through _client.post"
def test_run_pass_sends_nothing_when_nothing_is_configured():
"""The regression this whole block exists for: notify.run_pass() reaches
discord.post_subscription_alert() for every notification it creates, and
test_notify.py calls it seven times without patching discord. With the gates
closed it must complete without touching a transport."""
from accounts import db
from notifications import notify
# The app lifespan (which normally creates these) does not run under
# TestClient-less tests, so build the schema the way test_notify.py does.
db.Base.metadata.create_all(db.sync_engine)
# No subscriptions in the throwaway DB => no candidates, no sends. The point
# is that this completes without raising OutboundBlocked.
notify.run_pass()