|
53 | 53 | from faststream_outbox.publisher.fake import OutboxFakePublisher |
54 | 54 | from faststream_outbox.publisher.producer import OutboxProducer |
55 | 55 | from faststream_outbox.publisher.specification import OutboxPublisherSpecification |
56 | | -from faststream_outbox.registrator import _default_retry_strategy |
57 | 56 | from faststream_outbox.response import OutboxPublishCommand |
| 57 | +from faststream_outbox.retry import _default_retry_strategy |
58 | 58 | from faststream_outbox.router import OutboxRoute |
59 | 59 | from faststream_outbox.schema_validation import ( |
60 | 60 | _AUTOGEN_BLIND_HINT, |
@@ -3289,7 +3289,7 @@ def test_subscriber_warns_on_nack_with_no_retry() -> None: |
3289 | 3289 |
|
3290 | 3290 | def test_subscriber_warns_on_max_deliveries_with_no_retry() -> None: |
3291 | 3291 | broker = _make_broker() |
3292 | | - with pytest.warns(UserWarning, match="max_deliveries is set but no retry_strategy"): |
| 3292 | + with pytest.warns(UserWarning, match=r"max_deliveries is set but retry_strategy=NoRetry\(\)"): |
3293 | 3293 | _register_subscriber(broker, max_deliveries=5, retry_strategy=NoRetry()) |
3294 | 3294 |
|
3295 | 3295 |
|
@@ -3317,6 +3317,73 @@ def test_subscriber_reject_on_error_with_no_retry_is_silent() -> None: |
3317 | 3317 | _register_subscriber(broker, ack_policy=AckPolicy.REJECT_ON_ERROR, retry_strategy=NoRetry()) |
3318 | 3318 |
|
3319 | 3319 |
|
| 3320 | +@pytest.mark.parametrize("max_deliveries", [0, -1]) |
| 3321 | +def test_subscriber_rejects_max_deliveries_below_one(max_deliveries: int) -> None: |
| 3322 | + """INVARIANT: ``max_deliveries`` below 1 is refused at registration. |
| 3323 | +
|
| 3324 | + Every row has ``deliveries_count >= 1`` by the time it is checked, so a cap of 0 or less marks |
| 3325 | + every row over the limit and deletes the queue (or sends it to the DLQ) without the handler ever |
| 3326 | + running. Someone reading 0 as "unlimited" would lose the whole queue. |
| 3327 | + """ |
| 3328 | + with pytest.raises(ValueError, match="max_deliveries must be >= 1"): |
| 3329 | + _register_subscriber(_make_broker(), max_deliveries=max_deliveries) |
| 3330 | + |
| 3331 | + |
| 3332 | +@pytest.mark.parametrize("field", ["min_fetch_interval", "max_fetch_interval", "lease_ttl_seconds"]) |
| 3333 | +@pytest.mark.parametrize("value", [math.nan, math.inf]) |
| 3334 | +def test_subscriber_rejects_non_finite_intervals(field: str, value: float) -> None: |
| 3335 | + with pytest.raises(ValueError, match=f"{field} must be a finite number"): |
| 3336 | + _register_subscriber(_make_broker(), **{field: value}) |
| 3337 | + |
| 3338 | + |
| 3339 | +@pytest.mark.parametrize("field", ["max_workers", "fetch_batch_size", "terminal_flush_batch_size", "max_deliveries"]) |
| 3340 | +def test_subscriber_rejects_bool_for_integer_settings(field: str) -> None: |
| 3341 | + with pytest.raises(TypeError, match=f"{field} must be an int"): |
| 3342 | + _register_subscriber(_make_broker(), **{field: True}) |
| 3343 | + |
| 3344 | + |
| 3345 | +@pytest.mark.parametrize( |
| 3346 | + ("queues", "match"), |
| 3347 | + [ |
| 3348 | + ("", "queue must be a non-empty string"), |
| 3349 | + ("q" * 256, "queue must be at most 255 characters"), |
| 3350 | + (["orders", "orders"], "duplicate queue"), |
| 3351 | + ], |
| 3352 | +) |
| 3353 | +def test_subscriber_rejects_queues_that_can_never_match(queues: str | list[str], match: str) -> None: |
| 3354 | + with pytest.raises(ValueError, match=match): |
| 3355 | + _make_broker().subscriber(queues) |
| 3356 | + |
| 3357 | + |
| 3358 | +def test_subscriber_rejects_a_non_string_queue() -> None: |
| 3359 | + with pytest.raises(TypeError, match="queue must be a str"): |
| 3360 | + _make_broker().subscriber([1]) # ty: ignore[invalid-argument-type] |
| 3361 | + |
| 3362 | + |
| 3363 | +def test_include_router_rejects_a_prefix() -> None: |
| 3364 | + broker = _make_broker() |
| 3365 | + router = OutboxRouter() |
| 3366 | + |
| 3367 | + @router.subscriber("orders") |
| 3368 | + async def handle(body: dict) -> None: ... |
| 3369 | + |
| 3370 | + with pytest.raises(ValueError, match="prefix"): |
| 3371 | + broker.include_router(router, prefix="billing.") |
| 3372 | + |
| 3373 | + |
| 3374 | +def test_subscriber_reject_on_error_without_retry_strategy_is_silent() -> None: |
| 3375 | + with warnings.catch_warnings(): |
| 3376 | + warnings.simplefilter("error", UserWarning) |
| 3377 | + _register_subscriber(_make_broker(), ack_policy=AckPolicy.REJECT_ON_ERROR) |
| 3378 | + |
| 3379 | + |
| 3380 | +def test_subscriber_max_deliveries_with_the_default_retry_strategy_is_silent() -> None: |
| 3381 | + with warnings.catch_warnings(): |
| 3382 | + warnings.simplefilter("error", UserWarning) |
| 3383 | + sub = _make_broker().subscriber("orders", max_deliveries=5) |
| 3384 | + assert isinstance(sub._config.retry_strategy, ExponentialRetry) # noqa: SLF001 |
| 3385 | + |
| 3386 | + |
3320 | 3387 | # --- MetricsRecorder seam ------------------------------------------------------------ |
3321 | 3388 |
|
3322 | 3389 |
|
|
0 commit comments