diff --git a/faststream_redis_timers/broker.py b/faststream_redis_timers/broker.py index e6cc224..fa60d96 100644 --- a/faststream_redis_timers/broker.py +++ b/faststream_redis_timers/broker.py @@ -45,6 +45,9 @@ class TimersParamsStorage(DefaultLoggerStorage): __max_msg_id_ln = -1 _max_channel_name = 7 + def register_subscriber(self, params: dict[str, typing.Any]) -> None: + self._max_channel_name = max(self._max_channel_name, len(params.get("channel", ""))) + def get_logger(self, *, context: "ContextRepo") -> LoggerProto: if logger := self._get_logger_ref(): return logger diff --git a/faststream_redis_timers/subscriber/usecase.py b/faststream_redis_timers/subscriber/usecase.py index e8a76bf..936ca55 100644 --- a/faststream_redis_timers/subscriber/usecase.py +++ b/faststream_redis_timers/subscriber/usecase.py @@ -182,6 +182,11 @@ async def get_one(self, *, timeout: float = 5.0) -> typing.NoReturn: msg = "TimersBroker does not support get_one()" raise NotImplementedError(msg) + @override + def __aiter__(self) -> typing.NoReturn: + msg = "TimersBroker does not support iteration" + raise NotImplementedError(msg) + def _make_response_publisher( self, message: "StreamMessage[TimerMessage]", # noqa: ARG002 diff --git a/tests/test_adr_citations.py b/tests/test_adr_citations.py index 760f6f1..7f7706c 100644 --- a/tests/test_adr_citations.py +++ b/tests/test_adr_citations.py @@ -7,7 +7,7 @@ _REPO_ROOT: typing.Final = pathlib.Path(__file__).resolve().parent.parent _ADR_DIR: typing.Final = "docs/adr/" -_CITATION: typing.Final = re.compile(r"docs/adr/\d{4}-[a-z0-9-]+\.md") +_CITATION: typing.Final = re.compile(r"docs/adr/\d{4}(?:-[a-z0-9-]+\.md)?") _UNWALKED_DIR: typing.Final = "node_modules" @@ -45,6 +45,8 @@ def test_every_adr_path_cited_from_python_resolves() -> None: offline link gate reads Markdown only, so a path in a docstring, a comment or a guard message is otherwise checked by nothing, and an `INVARIANT:` docstring that names its ADR silently loses the rationale the test depends on. A user who trips a guard is handed a link to follow. + A bare `docs/adr/NNNN` is reported as well: it names no file, so it would survive the same + rename or drop unnoticed and point at whatever record holds that number next. """ unresolved = unresolved_citations(_REPO_ROOT) @@ -64,6 +66,18 @@ def test_a_citation_of_a_missing_adr_is_reported_with_its_citing_file(tmp_path: assert unresolved_citations(tmp_path) == [("pkg/mod.py", f"{_ADR_DIR}9999-missing.md")] +def test_a_short_form_citation_is_reported_even_when_the_adr_exists(tmp_path: pathlib.Path) -> None: + """`docs/adr/NNNN` with no slug names nothing on disk, so a rename or a drop never breaks it.""" + (tmp_path / _ADR_DIR).mkdir(parents=True) + (tmp_path / _ADR_DIR / "0002-kept.md").write_text("# kept\n", encoding="utf-8") + (tmp_path / "pkg").mkdir() + (tmp_path / "pkg" / "mod.py").write_text( + f'"""Argued in {_ADR_DIR}0002 and {_ADR_DIR}0002-kept.md."""\n', encoding="utf-8" + ) + + assert unresolved_citations(tmp_path) == [("pkg/mod.py", f"{_ADR_DIR}0002")] + + def test_a_citation_split_across_adjacent_string_literals_is_found(tmp_path: pathlib.Path) -> None: """Python joins adjacent literals at parse time, which is what the `nack` guard message relies on.""" (tmp_path / "guard.py").write_text( diff --git a/tests/test_unit.py b/tests/test_unit.py index 1e4822f..a784a9e 100644 --- a/tests/test_unit.py +++ b/tests/test_unit.py @@ -136,6 +136,16 @@ def test_params_storage_get_logger_is_cached() -> None: assert logger1 is logger2 +def test_params_storage_register_subscriber_widens_the_channel_column() -> None: + logging.getLogger("faststream.access.timers").handlers.clear() + storage = TimersParamsStorage() + storage.register_subscriber({"channel": "billing-reminders"}) + storage.get_logger(context=MagicMock()) + formatter = logging.getLogger("faststream.access.timers").handlers[0].formatter + assert formatter is not None + assert "%(channel)-17s" in (formatter._fmt or "") # noqa: SLF001 + + # --- Subscriber.get_one raises --- @@ -146,6 +156,14 @@ async def test_subscriber_get_one_raises() -> None: await sub.get_one() +async def test_subscriber_iteration_raises() -> None: + broker = TimersBroker() + sub = broker.subscriber("topic") + with pytest.raises(NotImplementedError): + async for _ in sub: # pragma: no cover - never yields + pass + + # --- Publisher.request raises ---