From 0ab3b352ef2f942e3a44314c70b56b14b84401be Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 02:27:37 +0000 Subject: [PATCH 1/2] Initial analysis of coverage gaps Agent-Logs-Url: https://github.com/taskiq-python/natsrpy/sessions/0a995e8c-b582-460a-905b-8e41485c74e8 Co-authored-by: s3rius <18153319+s3rius@users.noreply.github.com> --- default_14516872994696397057_0_10174.profraw | Bin 0 -> 71632 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 default_14516872994696397057_0_10174.profraw diff --git a/default_14516872994696397057_0_10174.profraw b/default_14516872994696397057_0_10174.profraw new file mode 100644 index 0000000000000000000000000000000000000000..565c69c7f4bbc5716da0f3113a2f33356c346b03 GIT binary patch literal 71632 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 JolGQz`8UJ?gn|G7 literal 0 HcmV?d00001 From 51aebed51ace54588e13dc6d982d219e6f12dc9b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 27 Mar 2026 02:36:45 +0000 Subject: [PATCH 2/2] Add tests to improve coverage for uncovered Rust code paths Agent-Logs-Url: https://github.com/taskiq-python/natsrpy/sessions/0a995e8c-b582-460a-905b-8e41485c74e8 Co-authored-by: s3rius <18153319+s3rius@users.noreply.github.com> --- .gitignore | 1 + default_14516872994696397057_0_10174.profraw | Bin 71632 -> 0 bytes python/tests/test_callback_subscription.py | 48 ++++++ python/tests/test_consumer_properties.py | 76 +++++++++ python/tests/test_consumers_manager.py | 107 ++++++++++++ python/tests/test_js_message_acks.py | 168 +++++++++++++++++++ python/tests/test_kv_manager.py | 20 +++ python/tests/test_publication_and_extras.py | 63 +++++++ python/tests/test_stream_config_types.py | 97 +++++++++++ python/tests/test_stream_purge.py | 39 +++++ 10 files changed, 619 insertions(+) delete mode 100644 default_14516872994696397057_0_10174.profraw create mode 100644 python/tests/test_callback_subscription.py create mode 100644 python/tests/test_consumer_properties.py create mode 100644 python/tests/test_consumers_manager.py create mode 100644 python/tests/test_js_message_acks.py create mode 100644 python/tests/test_kv_manager.py create mode 100644 python/tests/test_publication_and_extras.py create mode 100644 python/tests/test_stream_config_types.py create mode 100644 python/tests/test_stream_purge.py diff --git a/.gitignore b/.gitignore index 4cb54d1..32ce7fc 100644 --- a/.gitignore +++ b/.gitignore @@ -72,3 +72,4 @@ docs/_build/ .python-version .venv/ target/ +*.profraw diff --git a/default_14516872994696397057_0_10174.profraw b/default_14516872994696397057_0_10174.profraw deleted file mode 100644 index 565c69c7f4bbc5716da0f3113a2f33356c346b03..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 71632 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 JolGQz`8UJ?gn|G7 diff --git a/python/tests/test_callback_subscription.py b/python/tests/test_callback_subscription.py new file mode 100644 index 0000000..5fdbada --- /dev/null +++ b/python/tests/test_callback_subscription.py @@ -0,0 +1,48 @@ +import asyncio +import uuid + +from natsrpy import CallbackSubscription, Nats + + +async def test_callback_unsubscribe(nats: Nats) -> None: + subj = uuid.uuid4().hex + + async def callback(msg: object) -> None: + pass + + sub = await nats.subscribe(subject=subj, callback=callback) + assert isinstance(sub, CallbackSubscription) + await sub.unsubscribe() + + +async def test_callback_unsubscribe_with_limit(nats: Nats) -> None: + subj = uuid.uuid4().hex + received: list[bytes] = [] + event = asyncio.Event() + + async def callback(msg: object) -> None: + received.append(msg.payload) # type: ignore[attr-defined] + if len(received) >= 2: + event.set() + + sub = await nats.subscribe(subject=subj, callback=callback) + assert isinstance(sub, CallbackSubscription) + await sub.unsubscribe(limit=2) + await nats.publish(subj, b"msg-1") + await nats.publish(subj, b"msg-2") + await asyncio.wait_for(event.wait(), timeout=5.0) + assert received == [b"msg-1", b"msg-2"] + + +async def test_callback_drain(nats_url: str) -> None: + client = Nats(addrs=[nats_url]) + await client.startup() + subj = uuid.uuid4().hex + + async def callback(msg: object) -> None: + pass + + sub = await client.subscribe(subject=subj, callback=callback) + assert isinstance(sub, CallbackSubscription) + await sub.drain() + await client.shutdown() diff --git a/python/tests/test_consumer_properties.py b/python/tests/test_consumer_properties.py new file mode 100644 index 0000000..7c92ea6 --- /dev/null +++ b/python/tests/test_consumer_properties.py @@ -0,0 +1,76 @@ +import uuid + +from natsrpy.js import ( + JetStream, + PullConsumer, + PullConsumerConfig, + PushConsumer, + PushConsumerConfig, + StreamConfig, +) + + +async def test_pull_consumer_name_and_stream(js: JetStream) -> None: + stream_name = f"test-pcns-{uuid.uuid4().hex[:8]}" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + consumer_name = f"consumer-{uuid.uuid4().hex[:8]}" + consumer = await stream.consumers.create( + PullConsumerConfig(name=consumer_name), + ) + assert isinstance(consumer, PullConsumer) + assert consumer.name == consumer_name + assert consumer.stream_name == stream_name + finally: + await js.streams.delete(stream_name) + + +async def test_push_consumer_name_and_stream(js: JetStream) -> None: + stream_name = f"test-pushns-{uuid.uuid4().hex[:8]}" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + deliver_subj = uuid.uuid4().hex + consumer_name = f"push-{uuid.uuid4().hex[:8]}" + consumer = await stream.consumers.create( + PushConsumerConfig(deliver_subject=deliver_subj, name=consumer_name), + ) + assert isinstance(consumer, PushConsumer) + assert consumer.name == consumer_name + assert consumer.stream_name == stream_name + finally: + await js.streams.delete(stream_name) + + +async def test_pull_consumer_messages_iterator(js: JetStream) -> None: + stream_name = f"test-pullmsgiter-{uuid.uuid4().hex[:8]}" + subj = f"{stream_name}.data" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + await js.publish(subj, b"iter-msg-1", wait=True) + await js.publish(subj, b"iter-msg-2", wait=True) + consumer = await stream.consumers.create( + PullConsumerConfig(name=f"consumer-{uuid.uuid4().hex[:8]}"), + ) + msgs = await consumer.fetch(max_messages=2, timeout=5.0) + assert len(msgs) == 2 + assert msgs[0].payload == b"iter-msg-1" + assert msgs[1].payload == b"iter-msg-2" + finally: + await js.streams.delete(stream_name) + + +async def test_pull_consumer_fetch_empty(js: JetStream) -> None: + stream_name = f"test-fetchempty-{uuid.uuid4().hex[:8]}" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + consumer = await stream.consumers.create( + PullConsumerConfig(name=f"consumer-{uuid.uuid4().hex[:8]}"), + ) + msgs = await consumer.fetch(max_messages=1, timeout=0.5) + assert len(msgs) == 0 + finally: + await js.streams.delete(stream_name) diff --git a/python/tests/test_consumers_manager.py b/python/tests/test_consumers_manager.py new file mode 100644 index 0000000..9b354ca --- /dev/null +++ b/python/tests/test_consumers_manager.py @@ -0,0 +1,107 @@ +import uuid +from datetime import timedelta + +from natsrpy.js import ( + JetStream, + PullConsumer, + PullConsumerConfig, + PushConsumer, + PushConsumerConfig, + StreamConfig, +) + + +async def test_consumers_manager_delete(js: JetStream) -> None: + stream_name = f"test-cmdel-{uuid.uuid4().hex[:8]}" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + consumer_name = f"consumer-{uuid.uuid4().hex[:8]}" + await stream.consumers.create(PullConsumerConfig(name=consumer_name)) + result = await stream.consumers.delete(consumer_name) + assert result is True + finally: + await js.streams.delete(stream_name) + + +async def test_consumers_manager_get_push(js: JetStream) -> None: + stream_name = f"test-cmgp-{uuid.uuid4().hex[:8]}" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + deliver_subj = uuid.uuid4().hex + consumer_name = f"push-{uuid.uuid4().hex[:8]}" + await stream.consumers.create( + PushConsumerConfig(deliver_subject=deliver_subj, name=consumer_name), + ) + consumer = await stream.consumers.get_push(consumer_name) + assert isinstance(consumer, PushConsumer) + assert consumer.name == consumer_name + finally: + await js.streams.delete(stream_name) + + +async def test_consumers_manager_update_pull(js: JetStream) -> None: + stream_name = f"test-cmup-{uuid.uuid4().hex[:8]}" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + consumer_name = f"consumer-{uuid.uuid4().hex[:8]}" + cfg = PullConsumerConfig(name=consumer_name, description="original") + await stream.consumers.create(cfg) + cfg.description = "updated" + updated = await stream.consumers.update(cfg) + assert isinstance(updated, PullConsumer) + finally: + await js.streams.delete(stream_name) + + +async def test_consumers_manager_update_push(js: JetStream) -> None: + stream_name = f"test-cmupp-{uuid.uuid4().hex[:8]}" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + deliver_subj = uuid.uuid4().hex + consumer_name = f"push-{uuid.uuid4().hex[:8]}" + cfg = PushConsumerConfig( + deliver_subject=deliver_subj, + name=consumer_name, + description="original", + ) + await stream.consumers.create(cfg) + cfg.description = "updated" + updated = await stream.consumers.update(cfg) + assert isinstance(updated, PushConsumer) + finally: + await js.streams.delete(stream_name) + + +async def test_consumers_manager_pause_and_resume(js: JetStream) -> None: + stream_name = f"test-cmpr-{uuid.uuid4().hex[:8]}" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + consumer_name = f"consumer-{uuid.uuid4().hex[:8]}" + await stream.consumers.create(PullConsumerConfig(name=consumer_name)) + paused = await stream.consumers.pause(consumer_name, delay=60.0) + assert isinstance(paused, bool) + resumed = await stream.consumers.resume(consumer_name) + assert isinstance(resumed, bool) + finally: + await js.streams.delete(stream_name) + + +async def test_consumers_manager_pause_timedelta(js: JetStream) -> None: + stream_name = f"test-cmpd-{uuid.uuid4().hex[:8]}" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + consumer_name = f"consumer-{uuid.uuid4().hex[:8]}" + await stream.consumers.create(PullConsumerConfig(name=consumer_name)) + paused = await stream.consumers.pause( + consumer_name, + delay=timedelta(seconds=60), + ) + assert isinstance(paused, bool) + finally: + await js.streams.delete(stream_name) diff --git a/python/tests/test_js_message_acks.py b/python/tests/test_js_message_acks.py new file mode 100644 index 0000000..bc4b3bc --- /dev/null +++ b/python/tests/test_js_message_acks.py @@ -0,0 +1,168 @@ +import uuid + +from natsrpy.js import ( + AckPolicy, + JetStream, + PullConsumerConfig, + StreamConfig, +) + + +async def test_message_ack_double(js: JetStream) -> None: + stream_name = f"test-ackd-{uuid.uuid4().hex[:8]}" + subj = f"{stream_name}.data" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + await js.publish(subj, b"ack-double", wait=True) + consumer = await stream.consumers.create( + PullConsumerConfig( + name=f"consumer-{uuid.uuid4().hex[:8]}", + ack_policy=AckPolicy.EXPLICIT, + ), + ) + messages = await consumer.fetch(max_messages=1, timeout=5.0) + assert len(messages) == 1 + await messages[0].ack(double=True) + finally: + await js.streams.delete(stream_name) + + +async def test_message_nack_with_delay(js: JetStream) -> None: + stream_name = f"test-nackd-{uuid.uuid4().hex[:8]}" + subj = f"{stream_name}.data" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + await js.publish(subj, b"nack-delay", wait=True) + consumer = await stream.consumers.create( + PullConsumerConfig( + name=f"consumer-{uuid.uuid4().hex[:8]}", + ack_policy=AckPolicy.EXPLICIT, + ), + ) + messages = await consumer.fetch(max_messages=1, timeout=5.0) + assert len(messages) == 1 + await messages[0].nack(delay=2.0) + finally: + await js.streams.delete(stream_name) + + +async def test_message_nack_double(js: JetStream) -> None: + stream_name = f"test-nackdb-{uuid.uuid4().hex[:8]}" + subj = f"{stream_name}.data" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + await js.publish(subj, b"nack-double", wait=True) + consumer = await stream.consumers.create( + PullConsumerConfig( + name=f"consumer-{uuid.uuid4().hex[:8]}", + ack_policy=AckPolicy.EXPLICIT, + ), + ) + messages = await consumer.fetch(max_messages=1, timeout=5.0) + assert len(messages) == 1 + await messages[0].nack(double=True) + finally: + await js.streams.delete(stream_name) + + +async def test_message_next_ack(js: JetStream) -> None: + stream_name = f"test-nextack-{uuid.uuid4().hex[:8]}" + subj = f"{stream_name}.data" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + await js.publish(subj, b"next-ack", wait=True) + consumer = await stream.consumers.create( + PullConsumerConfig( + name=f"consumer-{uuid.uuid4().hex[:8]}", + ack_policy=AckPolicy.EXPLICIT, + ), + ) + messages = await consumer.fetch(max_messages=1, timeout=5.0) + assert len(messages) == 1 + await messages[0].next() + finally: + await js.streams.delete(stream_name) + + +async def test_message_term_double(js: JetStream) -> None: + stream_name = f"test-termd-{uuid.uuid4().hex[:8]}" + subj = f"{stream_name}.data" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + await js.publish(subj, b"term-double", wait=True) + consumer = await stream.consumers.create( + PullConsumerConfig( + name=f"consumer-{uuid.uuid4().hex[:8]}", + ack_policy=AckPolicy.EXPLICIT, + ), + ) + messages = await consumer.fetch(max_messages=1, timeout=5.0) + assert len(messages) == 1 + await messages[0].term(double=True) + finally: + await js.streams.delete(stream_name) + + +async def test_message_progress_double(js: JetStream) -> None: + stream_name = f"test-progd-{uuid.uuid4().hex[:8]}" + subj = f"{stream_name}.data" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + await js.publish(subj, b"progress-double", wait=True) + consumer = await stream.consumers.create( + PullConsumerConfig( + name=f"consumer-{uuid.uuid4().hex[:8]}", + ack_policy=AckPolicy.EXPLICIT, + ), + ) + messages = await consumer.fetch(max_messages=1, timeout=5.0) + assert len(messages) == 1 + await messages[0].progress(double=True) + await messages[0].ack() + finally: + await js.streams.delete(stream_name) + + +async def test_message_domain_and_acc_hash(js: JetStream) -> None: + stream_name = f"test-domhash-{uuid.uuid4().hex[:8]}" + subj = f"{stream_name}.data" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + await js.publish(subj, b"domain-test", wait=True) + consumer = await stream.consumers.create( + PullConsumerConfig(name=f"consumer-{uuid.uuid4().hex[:8]}"), + ) + messages = await consumer.fetch(max_messages=1, timeout=5.0) + assert len(messages) == 1 + msg = messages[0] + # domain may be None for a non-domain jetstream + assert msg.domain is None or isinstance(msg.domain, str) + assert msg.acc_hash is None or isinstance(msg.acc_hash, str) + assert msg.token is None or isinstance(msg.token, str) + assert msg.reply is None or isinstance(msg.reply, str) + finally: + await js.streams.delete(stream_name) + + +async def test_message_headers_empty(js: JetStream) -> None: + stream_name = f"test-msghdr-{uuid.uuid4().hex[:8]}" + subj = f"{stream_name}.data" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + stream = await js.streams.create(config) + try: + await js.publish(subj, b"no-headers", wait=True) + consumer = await stream.consumers.create( + PullConsumerConfig(name=f"consumer-{uuid.uuid4().hex[:8]}"), + ) + messages = await consumer.fetch(max_messages=1, timeout=5.0) + assert len(messages) == 1 + assert isinstance(messages[0].headers, dict) + finally: + await js.streams.delete(stream_name) diff --git a/python/tests/test_kv_manager.py b/python/tests/test_kv_manager.py new file mode 100644 index 0000000..6e63311 --- /dev/null +++ b/python/tests/test_kv_manager.py @@ -0,0 +1,20 @@ +import uuid + +from natsrpy.js import ( + JetStream, + KeyValue, + KVConfig, +) + + +async def test_kv_manager_update(js: JetStream) -> None: + bucket = f"test-kv-mgrupd-{uuid.uuid4().hex[:8]}" + config = KVConfig(bucket=bucket, description="original") + await js.kv.create(config) + try: + config.description = "updated description" + updated_kv = await js.kv.update(config) + assert isinstance(updated_kv, KeyValue) + assert updated_kv.name == bucket + finally: + await js.kv.delete(bucket) diff --git a/python/tests/test_publication_and_extras.py b/python/tests/test_publication_and_extras.py new file mode 100644 index 0000000..e16e18a --- /dev/null +++ b/python/tests/test_publication_and_extras.py @@ -0,0 +1,63 @@ +import uuid + +from natsrpy import Nats +from natsrpy.js import JetStream, Publication, StreamConfig + + +async def test_publication_properties(js: JetStream) -> None: + stream_name = f"test-pub-{uuid.uuid4().hex[:8]}" + subj = f"{stream_name}.data" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + await js.streams.create(config) + try: + pub = await js.publish(subj, b"pub-test", wait=True) + assert isinstance(pub, Publication) + assert pub.stream == stream_name + assert pub.sequence >= 1 + assert isinstance(pub.domain, str) + assert isinstance(pub.duplicate, bool) + assert pub.duplicate is False + finally: + await js.streams.delete(stream_name) + + +async def test_publication_value_none(js: JetStream) -> None: + stream_name = f"test-pubval-{uuid.uuid4().hex[:8]}" + subj = f"{stream_name}.data" + config = StreamConfig(name=stream_name, subjects=[f"{stream_name}.>"]) + await js.streams.create(config) + try: + pub = await js.publish(subj, b"val-test", wait=True) + # value is only set for counters, None for regular streams + assert pub.value is None + finally: + await js.streams.delete(stream_name) + + +async def test_subscribe_with_queue_group(nats: Nats) -> None: + subj = uuid.uuid4().hex + queue = f"queue-{uuid.uuid4().hex[:8]}" + sub = await nats.subscribe(subject=subj, queue=queue) + await nats.publish(subj, b"queue-msg") + msg = await sub.next(timeout=5.0) + assert msg.payload == b"queue-msg" + await sub.unsubscribe() + + +async def test_nats_publish_bytearray(nats: Nats) -> None: + subj = uuid.uuid4().hex + payload = bytearray(b"bytearray-payload") + sub = await nats.subscribe(subject=subj) + await nats.publish(subj, payload) + msg = await anext(sub) + assert msg.payload == bytes(payload) + + +async def test_nats_publish_memoryview(nats: Nats) -> None: + subj = uuid.uuid4().hex + data = b"memoryview-payload" + payload = memoryview(data) + sub = await nats.subscribe(subject=subj) + await nats.publish(subj, payload) + msg = await anext(sub) + assert msg.payload == data diff --git a/python/tests/test_stream_config_types.py b/python/tests/test_stream_config_types.py new file mode 100644 index 0000000..7f001ae --- /dev/null +++ b/python/tests/test_stream_config_types.py @@ -0,0 +1,97 @@ +from datetime import timedelta + +from natsrpy.js import ( + ConsumerLimits, + External, + Placement, + Source, +) + + +async def test_external_construct() -> None: + ext = External(api_prefix="$JS.API") + assert ext.api_prefix == "$JS.API" + assert ext.delivery_prefix is None + + +async def test_external_with_delivery_prefix() -> None: + ext = External(api_prefix="$JS.API", delivery_prefix="$JS.DELIVER") + assert ext.api_prefix == "$JS.API" + assert ext.delivery_prefix == "$JS.DELIVER" + + +async def test_external_setters() -> None: + ext = External(api_prefix="$JS.API") + ext.api_prefix = "$JS.NEW" + assert ext.api_prefix == "$JS.NEW" + ext.delivery_prefix = "$JS.DEL" + assert ext.delivery_prefix == "$JS.DEL" + + +async def test_source_construct_minimal() -> None: + src = Source(name="src-stream") + assert src.name == "src-stream" + assert src.filter_subject is None + assert src.external is None + assert src.start_sequence is None + assert src.domain is None + + +async def test_source_with_filter() -> None: + src = Source(name="src-stream", filter_subject="test.>") + assert src.name == "src-stream" + assert src.filter_subject == "test.>" + + +async def test_source_with_external() -> None: + ext = External(api_prefix="$JS.API") + src = Source(name="src-stream", external=ext) + assert src.name == "src-stream" + assert src.external is not None + + +async def test_source_setters() -> None: + src = Source(name="src-stream") + src.name = "new-stream" + assert src.name == "new-stream" + src.filter_subject = "filter.>" + assert src.filter_subject == "filter.>" + + +async def test_placement_construct() -> None: + p = Placement(cluster="us-east", tags=["fast", "ssd"]) + assert p.cluster == "us-east" + assert p.tags == ["fast", "ssd"] + + +async def test_placement_defaults() -> None: + p = Placement() + assert p.cluster is None + # default is empty list + assert p.tags == [] + + +async def test_placement_setters() -> None: + p = Placement() + p.cluster = "eu-west" + assert p.cluster == "eu-west" + p.tags = ["backup"] + assert p.tags == ["backup"] + + +async def test_consumer_limits_construct() -> None: + cl = ConsumerLimits( + inactive_threshold=timedelta(seconds=30), + max_ack_pending=100, + ) + # ConsumerLimits doesn't expose its fields as Python getters, + # but the constructor should succeed without errors. + assert cl is not None + + +async def test_consumer_limits_construct_with_float() -> None: + cl = ConsumerLimits( + inactive_threshold=timedelta(seconds=30), + max_ack_pending=50, + ) + assert cl is not None diff --git a/python/tests/test_stream_purge.py b/python/tests/test_stream_purge.py new file mode 100644 index 0000000..ad9d231 --- /dev/null +++ b/python/tests/test_stream_purge.py @@ -0,0 +1,39 @@ +import uuid + +from natsrpy.js import JetStream, StreamConfig + + +async def test_stream_purge_with_sequence(js: JetStream) -> None: + name = f"test-purgeseq-{uuid.uuid4().hex[:8]}" + subj = f"{name}.data" + config = StreamConfig(name=name, subjects=[f"{name}.>"]) + stream = await js.streams.create(config) + try: + await js.publish(subj, b"msg-1", wait=True) + await js.publish(subj, b"msg-2", wait=True) + await js.publish(subj, b"msg-3", wait=True) + purged = await stream.purge(sequence=2) + assert purged >= 1 + info = await stream.get_info() + assert info.state.messages < 3 + finally: + await js.streams.delete(name) + + +async def test_stream_purge_with_keep(js: JetStream) -> None: + name = f"test-purgekeep-{uuid.uuid4().hex[:8]}" + subj = f"{name}.data" + config = StreamConfig(name=name, subjects=[f"{name}.>"]) + stream = await js.streams.create(config) + try: + await js.publish(subj, b"msg-1", wait=True) + await js.publish(subj, b"msg-2", wait=True) + await js.publish(subj, b"msg-3", wait=True) + await js.publish(subj, b"msg-4", wait=True) + await js.publish(subj, b"msg-5", wait=True) + purged = await stream.purge(keep=2) + assert purged == 3 + info = await stream.get_info() + assert info.state.messages == 2 + finally: + await js.streams.delete(name)