signal-bot/tests
Krzysztof kuhy Rudnicki 0b539fc327 feat: the management backend, absorbing the tagger
A second process rather than more routes on the bot's own app. This one can
rewrite .env, and the bot's port already serves /_selftest, which posts for
real into a real group; keeping them apart means the surface that can
reconfigure the bot only listens while somebody has the page open.

The tagger's seven routes move here unchanged, out of tool/ and into the
coverage gate -- which is the reason an unconditional ImportError once sat
in tagger.py through two releases. The media route is still indexed by
integer, and that is not a style choice: a filename from a client is a path,
and the directory holds private photographs. FastAPI refuses a name with 422
before the handler runs, which the tests assert.

guard.py is the part that was not asked for. Binding to loopback stops the
network, not the browser already running on this machine, and two attacks
reach a loopback port from an ordinary web page: a cross-origin form, which
can only send urlencoded, multipart or plain text, so requiring
application/json refuses it with no token to keep; and DNS rebinding, whose
request still carries the attacker's hostname in Host. Forty lines instead
of authentication, which is the right trade for something started by hand
and stopped again a few minutes later.

assets.py branches on whether the page has been built, because StaticFiles
raises at construction on a missing directory -- mounting it unconditionally
would take the app down on a fresh clone, and in CI, where pytest never runs
pnpm build. Both branches are tested; "not built" answers with the command.

The container restart goes through signal_bot.external, wrapped in
asyncio.to_thread: it is synchronous with a 60s default, and blocking the
loop would freeze the page showing the restart.

100% branch coverage across all eleven modules, no omits, no pragmas.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01416TegJ3sa8xL5qGGgrygP
2026-08-31 14:38:24 +02:00
..
__init__.py feat: rewrite the bot so it runs, and gate it like the other repos 2026-08-29 13:05:42 +02:00
conftest.py release: 0.2.0 «Urbanus I» — a private photo album, and a tagger for it 2026-08-30 22:35:41 +02:00
gate_helpers.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
manage_fixture.py feat: the management backend, absorbing the tagger 2026-08-31 14:38:24 +02:00
pack_add_fixture.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
pack_fixture.py release: 0.2.0 «Urbanus I» — a private photo album, and a tagger for it 2026-08-30 22:35:41 +02:00
stats_seed.py release: 0.2.0 «Urbanus I» — a private photo album, and a tagger for it 2026-08-30 22:35:41 +02:00
test_allowlist.py feat: refuse to look healthy while deaf, and heal the container by itself 2026-08-29 19:01:55 +02:00
test_animals.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_announce_render.py release: 0.1.7 «Callistus I» — a version now says which pope, and when 2026-08-30 22:12:09 +02:00
test_announce.py fix: a refused attachment no longer costs a group its release notes 2026-08-30 21:33:56 +02:00
test_app.py fix: a signal-api that is not up yet no longer kills the bot at boot 2026-08-30 15:23:42 +02:00
test_bot_catchup.py fix: the evening report is one message, not one per thing it has to say 2026-08-30 22:49:51 +02:00
test_bot_report.py release: 0.1.6 «Zephyrinus» — 21:37 reports today, not yesterday 2026-08-30 22:09:13 +02:00
test_bot.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_changelog.py release: 0.1.7 «Callistus I» — a version now says which pope, and when 2026-08-30 22:12:09 +02:00
test_cli.py feat: discover groups from the account, so adding the bot is the whole setup 2026-08-29 15:30:08 +02:00
test_config_pack.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_config.py feat: the message event log, and the nightly report built on it 2026-08-30 19:14:43 +02:00
test_cooldown_gate.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_counting.py feat: the message event log, and the nightly report built on it 2026-08-30 19:14:43 +02:00
test_decorators.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_discovery.py feat: the message event log, and the nightly report built on it 2026-08-30 19:14:43 +02:00
test_dispatcher_gates.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_dispatcher.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_envelope_shape.py fix: the operator's uuid was still a fixture in the envelope-shape test 2026-08-30 23:04:42 +02:00
test_external.py feat: python tool/tagger.py sets up .venv too, not just tag.sh 2026-08-31 13:34:27 +02:00
test_fake_account.py feat: refuse to look healthy while deaf, and heal the container by itself 2026-08-29 19:01:55 +02:00
test_fake_groups.py feat: discover groups from the account, so adding the bot is the whole setup 2026-08-29 15:30:08 +02:00
test_fake_server.py feat: add a photo to the album from the chat, tagged, stripped and verified 2026-08-31 11:20:00 +02:00
test_folding.py fix: commands resolve when typed on another alphabet's keyboard 2026-08-30 14:22:43 +02:00
test_healthz.py release: 0.1.7 «Callistus I» — a version now says which pope, and when 2026-08-30 22:12:09 +02:00
test_help_command.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_http_client.py feat: !gekon posts a research-grade gecko photo 2026-08-29 23:26:40 +02:00
test_liveness.py feat: refuse to look healthy while deaf, and heal the container by itself 2026-08-29 19:01:55 +02:00
test_logging_setup.py feat: the bot can describe an envelope without quoting anybody 2026-08-30 19:29:06 +02:00
test_main.py feat: the message event log, and the nightly report built on it 2026-08-30 19:14:43 +02:00
test_manage_album.py feat: the management backend, absorbing the tagger 2026-08-31 14:38:24 +02:00
test_manage_assets.py feat: the management backend, absorbing the tagger 2026-08-31 14:38:24 +02:00
test_manage_envfile.py feat: the .env layer the management page will write through 2026-08-31 14:31:29 +02:00
test_manage_envkeys.py feat: the .env layer the management page will write through 2026-08-31 14:31:29 +02:00
test_manage_guard.py feat: the management backend, absorbing the tagger 2026-08-31 14:38:24 +02:00
test_manage_routes.py feat: the management backend, absorbing the tagger 2026-08-31 14:38:24 +02:00
test_media_sniff.py feat: add a photo to the album from the chat, tagged, stripped and verified 2026-08-31 11:20:00 +02:00
test_media_strip.py feat: import a folder of photos without importing it twice 2026-08-31 13:22:57 +02:00
test_media_tags.py feat: add a photo to the album from the chat, tagged, stripped and verified 2026-08-31 11:20:00 +02:00
test_media_tracks.py feat: import a folder of photos without importing it twice 2026-08-31 13:22:57 +02:00
test_membership.py feat: discover groups from the account, so adding the bot is the whole setup 2026-08-29 15:30:08 +02:00
test_models.py feat: weekly, monthly and year-in-review posts, keyed off the day reported 2026-08-30 19:56:25 +02:00
test_multiple_albums.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_pack_add_invariants.py feat: add a photo to the album from the chat, tagged, stripped and verified 2026-08-31 11:20:00 +02:00
test_pack_add.py feat: add a photo to the album from the chat, tagged, stripped and verified 2026-08-31 11:20:00 +02:00
test_pack_album.py feat: import a folder of photos without importing it twice 2026-08-31 13:22:57 +02:00
test_pack_command.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_pack_custom.py feat: add a photo to the album from the chat, tagged, stripped and verified 2026-08-31 11:20:00 +02:00
test_pack_holder.py feat: add a photo to the album from the chat, tagged, stripped and verified 2026-08-31 11:20:00 +02:00
test_pack_import.py feat: import a folder of photos without importing it twice 2026-08-31 13:22:57 +02:00
test_pack_ledger.py feat: import a folder of photos without importing it twice 2026-08-31 13:22:57 +02:00
test_pack_load.py fix: take real people, numbers and a member's uuid out of a public repository 2026-08-30 23:01:26 +02:00
test_pack_people.py feat: import a folder of photos without importing it twice 2026-08-31 13:22:57 +02:00
test_pack_random.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_pack_store.py feat: add a photo to the album from the chat, tagged, stripped and verified 2026-08-31 11:20:00 +02:00
test_pack.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_personal_data_sources.py feat: add a photo to the album from the chat, tagged, stripped and verified 2026-08-31 11:20:00 +02:00
test_popes_command.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_portcheck.py feat: make the bot actually reply on Signal, and gate every way it went silent 2026-08-29 14:37:27 +02:00
test_portraits.py release: 0.0.7 «Telesfor» — !papież, and the bot can test itself 2026-08-30 17:06:25 +02:00
test_quoting.py feat: answer the message that asked, not just the room 2026-08-29 23:01:01 +02:00
test_registry.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_scenario.py feat: add a photo to the album from the chat, tagged, stripped and verified 2026-08-31 11:20:00 +02:00
test_scheduler.py feat: rewrite the bot so it runs, and gate it like the other repos 2026-08-29 13:05:42 +02:00
test_selftest_attachments.py feat: serve several photo albums at once, each scoped to its own groups 2026-08-31 11:58:50 +02:00
test_selftest.py release: 0.0.7 «Telesfor» — !papież, and the bot can test itself 2026-08-30 17:06:25 +02:00
test_startup.py feat: the message event log, and the nightly report built on it 2026-08-30 19:14:43 +02:00
test_stats_backfill.py fix: !ja told everybody their favourite hour was noon 2026-08-30 20:15:20 +02:00
test_stats_commands.py release: 0.2.0 «Urbanus I» — a private photo album, and a tagger for it 2026-08-30 22:35:41 +02:00
test_stats_curiosities.py release: 0.2.0 «Urbanus I» — a private photo album, and a tagger for it 2026-08-30 22:35:41 +02:00
test_stats_events.py feat: the message event log, and the nightly report built on it 2026-08-30 19:14:43 +02:00
test_stats_nightly.py release: 0.1.6 «Zephyrinus» — 21:37 reports today, not yesterday 2026-08-30 22:09:13 +02:00
test_stats_periods.py feat: the message event log, and the nightly report built on it 2026-08-30 19:14:43 +02:00
test_stats_queries_period.py release: 0.2.0 «Urbanus I» — a private photo album, and a tagger for it 2026-08-30 22:35:41 +02:00
test_stats_records.py release: 0.2.0 «Urbanus I» — a private photo album, and a tagger for it 2026-08-30 22:35:41 +02:00
test_stats_render_day.py feat: refuse to commit personal data, learned from outside the repository 2026-08-30 23:28:29 +02:00
test_stats_render_period.py release: 0.1.1 «Pius I» — commands that explain themselves 2026-08-30 20:09:14 +02:00
test_stats_streaks.py release: 0.2.0 «Urbanus I» — a private photo album, and a tagger for it 2026-08-30 22:35:41 +02:00
test_stats_text.py feat: the message event log, and the nightly report built on it 2026-08-30 19:14:43 +02:00
test_store.py feat: the message event log, and the nightly report built on it 2026-08-30 19:14:43 +02:00
test_tool_imports.py feat: python tool/tagger.py sets up .venv too, not just tag.sh 2026-08-31 13:34:27 +02:00
test_transport_attachments.py feat: add a photo to the album from the chat, tagged, stripped and verified 2026-08-31 11:20:00 +02:00
test_transport_base.py feat: add a photo to the album from the chat, tagged, stripped and verified 2026-08-31 11:20:00 +02:00
test_transport_fake.py feat: rewrite the bot so it runs, and gate it like the other repos 2026-08-29 13:05:42 +02:00
test_transport_media.py release: 0.2.0 «Urbanus I» — a private photo album, and a tagger for it 2026-08-30 22:35:41 +02:00
test_transport_rest.py feat: add a photo to the album from the chat, tagged, stripped and verified 2026-08-31 11:20:00 +02:00
test_trust.py feat: the message event log, and the nightly report built on it 2026-08-30 19:14:43 +02:00
test_versioning.py release: 0.1.7 «Callistus I» — a version now says which pope, and when 2026-08-30 22:12:09 +02:00