# Copyright (c) 2026 Krzysztof Rudnicki. MIT licence. """Shared scaffolding for the gate tests, split across two files by the 250-line cap.""" from __future__ import annotations from typing import TYPE_CHECKING import pytest from signal_bot.models import parse_envelope from signal_bot.registry import CommandContext, CommandRefusedError, Handler from tests.conftest import WATCHED_GROUP, envelope if TYPE_CHECKING: from signal_bot.config import Config from signal_bot.store import Store from signal_bot.transport_fake import FakeTransport DAY = "2026-08-29" def gate_context( store: Store, transport: FakeTransport, config: Config, *, uuid: str = "u1", now: float = 100.0, group: str = WATCHED_GROUP, ) -> CommandContext: """A context for a harmless "!ping", which every gate test wraps.""" message = parse_envelope(envelope(text="!ping", sender=(uuid, "Alice"), group_id=group)) assert message is not None return CommandContext( message=message, group_id=group, transport=transport, store=store, config=config, command_name="ping", now=now, day=DAY, ) async def refused(gate: Handler, ctx: CommandContext) -> None: """Await a gated handler that is expected to decline. A gate signals a refusal by raising, which is what keeps the dispatcher from recording a use for a run that never happened. """ with pytest.raises(CommandRefusedError): await gate(ctx)