Skip to content

fix: topic reference drop if topic.gossip_sender/receiver() is called…#26

Merged
rustonbsd merged 4 commits into
mainfrom
manual-split-ref-bug-fix
Apr 21, 2026
Merged

fix: topic reference drop if topic.gossip_sender/receiver() is called…#26
rustonbsd merged 4 commits into
mainfrom
manual-split-ref-bug-fix

Conversation

@rustonbsd

@rustonbsd rustonbsd commented Apr 21, 2026

Copy link
Copy Markdown
Owner

Summary by CodeRabbit

  • New Features

    • Configurable initial delay and join-peer limit for merge behavior, giving finer control over merge timing and peer participation.
  • Documentation

    • Updated examples and README to show the new merge configuration options.
  • Tests

    • Added a script to launch many chat instances and two tests ensuring topics survive drop while senders/receivers remain held.
  • Chores

    • Bumped package version to 0.3.2.

… instead of split (everything stops) + initial_interval added to bubble and message_overlap workers + reduced custom max_join_peers for merge strategies (now individually configurable) + added bash script to run many instances at once in tmux
@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Added two merge configuration fields—initial_interval and max_join_peers—exposed via builders/getters, threaded into Bubble and MessageOverlap merge constructors to schedule an initial delayed tick and limit join peers; docs, examples, topic wiring, and tests were updated accordingly.

Changes

Cohort / File(s) Summary
Config types & builders
src/config.rs
Added initial_interval: Duration and max_join_peers: usize to BubbleMergeConfigInner and MessageOverlapMergeConfigInner; added public getters and builder setters; defaults initialized (30s, 2).
Merge implementations
src/gossip/merge/bubble.rs, src/gossip/merge/message_overlap.rs
Added initial_interval: Duration parameter to ::new signatures; initialize ticker with tokio::time::interval_at(Instant::now() + initial_interval, base_interval) to delay the first tick.
Topic wiring & actor types
src/gossip/topic/topic.rs, src/gossip/topic/bootstrap.rs, src/gossip/topic/publisher.rs
Threaded per-merge config.max_join_peers() and config.initial_interval() into merge startup; simplified type paths (imported types instead of fully-qualified paths); adjusted gossip sender/receiver keep-alive handling; added tests ensuring Topic survives drop while senders/receivers exist.
Examples & docs
README.md, examples/full_config.rs
Updated examples to include initial_interval(Duration::from_secs(30)) and max_join_peers(2) for both merge strategies.
Crypto / RecordPublisher refactors
src/crypto/record.rs
Switched several fully-qualified types to local imports (e.g., RotationHandle, Config, Dht) and updated signatures to use imported types.
Misc. small changes
src/dht.rs, Cargo.toml, test_many_chats.sh
Updated package version (0.3.1 → 0.3.2); added test_many_chats.sh script; minor import/type cleanup in dht.rs.

Sequence Diagram(s)

sequenceDiagram
    participant Topic
    participant ConfigBuilder
    participant Merge (Bubble/MessageOverlap)
    participant TokioTimer
    participant MergeWorker

    Note over Topic,ConfigBuilder: startup sequence
    Topic->>ConfigBuilder: build merge config (initial_interval, max_join_peers)
    ConfigBuilder-->>Topic: merge config
    Topic->>Merge: new(config, initial_interval, max_join_peers, base_interval,...)
    Merge->>TokioTimer: interval_at(now + initial_interval, base_interval)
    TokioTimer-->>MergeWorker: first tick (after initial_interval)
    MergeWorker->>MergeWorker: compute jitter → reset_after(base_interval + jitter)
    MergeWorker->>Merge: perform merge round (respect max_join_peers)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Soft paws tap the ticker’s start,

initial waits and peers take part—
jittered hops and measured bounds,
gossip gardens weave their rounds.
A rabbit cheers: merges bloom at heart!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title describes a real bug fix addressing the topic reference drop issue when using gossip_sender/receiver() methods, which directly relates to the main functional changes in the PR.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/gossip/topic/topic.rs (1)

125-132: 🧹 Nitpick | 🔵 Trivial

split() now double-sets _topic_keep_alive.

Since gossip_sender() (Line 135-145) and gossip_receiver() (Line 148-157) each now inject their own Arc<self.clone()> into _topic_keep_alive, the subsequent assignments in split() overwrite a value that was already set moments earlier. Functionally correct, but two Arc<Topic> allocations per split are discarded. Consider simplifying:

♻️ Proposed simplification
 pub async fn split(&self) -> Result<(GossipSender, crate::gossip::receiver::GossipReceiver)> {
-    let topic_ref = Arc::new(self.clone());
-    let mut sender = self.gossip_sender().await?;
-    let mut receiver = self.gossip_receiver().await?;
-    sender._topic_keep_alive = Some(topic_ref.clone());
-    receiver._topic_keep_alive = Some(topic_ref);
-    Ok((sender, receiver))
+    let sender = self.gossip_sender().await?;
+    let receiver = self.gossip_receiver().await?;
+    Ok((sender, receiver))
 }

Alternatively, if you prefer a single shared Arc<Topic> across sender+receiver from split() (slightly cheaper than two separate allocations), keep the current body but drop the now-redundant set inside gossip_sender/gossip_receiver — pick one place to own the keep-alive policy.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/gossip/topic/topic.rs` around lines 125 - 132, split() creates a single
Arc of the topic and then overwrites _topic_keep_alive which is redundantly also
set inside gossip_sender() and gossip_receiver(); to avoid double Arc
allocations, remove the _topic_keep_alive assignment (the Arc::new(self.clone())
lines) from gossip_sender() and gossip_receiver() so split() is the single place
that injects the shared Arc into sender._topic_keep_alive and
receiver._topic_keep_alive, leaving gossip_sender/gossip_receiver to only
construct and return their respective objects.
src/config.rs (1)

226-537: 🧹 Nitpick | 🔵 Trivial

Config additions look consistent; one minor API-surface nit.

The initial_interval / max_join_peers fields, defaults (30s / 2), getters, and builder setters mirror each other across BubbleMergeConfigInner and MessageOverlapMergeConfigInner cleanly, and both integrate with the merge constructors via topic.rs.

One inconsistency worth noting: initial_interval accepts any Duration (including ZERO) without validation, while the sibling base_interval setter no-ops on ZERO and documents the override semantics. Zero is actually a valid value here (fires immediately), so this is fine — but consider a one-line doc note on initial_interval clarifying that Duration::ZERO is accepted and means "start immediately", to preempt confusion with the base_interval no-op convention.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/config.rs` around lines 226 - 537, Add a one-line doc clarification to
the initial_interval API docs stating that Duration::ZERO is accepted and means
"start immediately"; update the doc comments for the initial_interval methods
and builders in BubbleMergeConfigInner (initial_interval method and
BubbleMergeConfigBuilder::initial_interval) and MessageOverlapMergeConfigInner
(initial_interval method and MessageOverlapMergeConfigBuilder::initial_interval)
to note this behavior so it’s explicit alongside the existing base_interval
zero-noop semantics.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/gossip/topic/topic.rs`:
- Around line 490-579: Unify the timeouts and extend both tests
(test_topic_survives_topic_drop_split and
test_topic_survives_topic_drop_manual_sender_receiver) to prove the cancel token
only fires after all user-facing handles are dropped: after obtaining (_sender,
_receiver) from topic.split() or from
topic.gossip_sender()/topic.gossip_receiver(), drop(topic) as you already do,
then assert cancel_token is NOT cancelled within a single unified timeout
(choose e.g. 2s), then explicitly drop(_sender); drop(_receiver) and assert the
cancel_token does get cancelled (use tokio::time::timeout to wait for
cancel_token.cancelled() and expect Ok) to close the lifetime semantics loop.

In `@test_many_chats.sh`:
- Around line 1-20: Validate and harden the script by enabling safe bash flags
(add set -euo pipefail and IFS=$'\n\t'), remove the redundant shift after
reading N, and validate that N is a positive integer before using it (check N
with a regex like [[ $N =~ ^[0-9]+$ ]] and ensure N -ge 1), exiting with a clear
error message on failure; also check that tmux new-session (using CMD/SESSION)
succeeds before entering the loop (exit if it fails) so you don't attempt tmux
split-window/attach on failure.

---

Outside diff comments:
In `@src/config.rs`:
- Around line 226-537: Add a one-line doc clarification to the initial_interval
API docs stating that Duration::ZERO is accepted and means "start immediately";
update the doc comments for the initial_interval methods and builders in
BubbleMergeConfigInner (initial_interval method and
BubbleMergeConfigBuilder::initial_interval) and MessageOverlapMergeConfigInner
(initial_interval method and MessageOverlapMergeConfigBuilder::initial_interval)
to note this behavior so it’s explicit alongside the existing base_interval
zero-noop semantics.

In `@src/gossip/topic/topic.rs`:
- Around line 125-132: split() creates a single Arc of the topic and then
overwrites _topic_keep_alive which is redundantly also set inside
gossip_sender() and gossip_receiver(); to avoid double Arc allocations, remove
the _topic_keep_alive assignment (the Arc::new(self.clone()) lines) from
gossip_sender() and gossip_receiver() so split() is the single place that
injects the shared Arc into sender._topic_keep_alive and
receiver._topic_keep_alive, leaving gossip_sender/gossip_receiver to only
construct and return their respective objects.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f3bd7ec6-481c-4bd9-98d3-9734ac4dd1ff

📥 Commits

Reviewing files that changed from the base of the PR and between 28a98ef and 258ca49.

📒 Files selected for processing (7)
  • README.md
  • examples/full_config.rs
  • src/config.rs
  • src/gossip/merge/bubble.rs
  • src/gossip/merge/message_overlap.rs
  • src/gossip/topic/topic.rs
  • test_many_chats.sh

Comment thread test_many_chats.sh
@rustonbsd

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@rustonbsd

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Apr 21, 2026

Copy link
Copy Markdown
✅ Actions performed

Full review triggered.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
test_many_chats.sh (1)

1-15: ⚠️ Potential issue | 🟡 Minor

Harden argument and failure handling before creating/splitting tmux panes.

This still has the previously flagged robustness gaps: no strict bash mode, redundant shift (Line 9), no positive-integer validation for N (Lines 8/15), and no guard for tmux new-session failure (Line 13), which can cause cascading command failures.

🛠️ Suggested patch
 #!/bin/bash
+set -euo pipefail
+IFS=$'\n\t'
 
 if [ $# -lt 1 ]; then
   echo "Usage: $0 <n>"
   exit 1
 fi
 
 N=$1
-shift
+if ! [[ "$N" =~ ^[0-9]+$ ]] || [ "$N" -lt 1 ]; then
+  echo "Error: <n> must be a positive integer, got '$N'"
+  exit 1
+fi
 CMD="cargo run --example chat"
 SESSION="multi-$$"
 
-tmux new-session -d -s "$SESSION" "$CMD"
+tmux new-session -d -s "$SESSION" "$CMD" || {
+  echo "Error: failed to create tmux session '$SESSION'"
+  exit 1
+}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test_many_chats.sh` around lines 1 - 15, Enable strict bash modes (set -euo
pipefail and IFS=$'\n\t') at the top, remove the redundant shift after assigning
N, validate that N is a positive integer (check N =~ ^[1-9][0-9]*$) and error
out with usage when invalid, and check the exit status of tmux new-session -d -s
"$SESSION" "$CMD" (abort with a clear error if it fails) before attempting to
split/create additional panes in the for loop; update references in the script
to variables N, CMD, SESSION and the tmux new-session invocation accordingly so
failures are caught early and handled cleanly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@Cargo.toml`:
- Line 10: Update the crate version in Cargo.toml from "0.3.2" to "0.4.0" to
reflect the breaking API change introduced by adding the required positional
parameter `initial_interval` to the public constructors `BubbleMerge::new()` and
`MessageOverlapMerge::new()` (exported in lib.rs); locate Cargo.toml and bump
the version string to "0.4.0" so semantic versioning correctly signals the
incompatible change to users.

In `@src/gossip/topic/topic.rs`:
- Around line 485-596: Extract the duplicated async test setup into a helper
async fn (e.g., make_topic) inside the test module that accepts a name: &str and
returns (crate::Topic, tokio_util::sync::CancellationToken) by performing the
secret_key/SigningKey generation, endpoint bind, iroh_gossip::net::Gossip spawn,
TopicId::new, RecordPublisher::new and crate::Topic::new calls currently
repeated in test_topic_full_shutdown_on_drop,
test_topic_survives_topic_drop_split, and
test_topic_survives_topic_drop_manual_sender_receiver; then update each test to
call make_topic(...) to obtain the Topic and its cancel_token and keep the
existing lifecycle assertions unchanged (references: Topic::new,
RecordPublisher::new, TopicId::new, topic.cancel_token(), topic.split(),
topic.gossip_sender(), topic.gossip_receiver()).

---

Duplicate comments:
In `@test_many_chats.sh`:
- Around line 1-15: Enable strict bash modes (set -euo pipefail and IFS=$'\n\t')
at the top, remove the redundant shift after assigning N, validate that N is a
positive integer (check N =~ ^[1-9][0-9]*$) and error out with usage when
invalid, and check the exit status of tmux new-session -d -s "$SESSION" "$CMD"
(abort with a clear error if it fails) before attempting to split/create
additional panes in the for loop; update references in the script to variables
N, CMD, SESSION and the tmux new-session invocation accordingly so failures are
caught early and handled cleanly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 14080ab8-22a5-4b0c-bf08-ae3b134aa170

📥 Commits

Reviewing files that changed from the base of the PR and between 28a98ef and ec2b0db.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (12)
  • Cargo.toml
  • README.md
  • examples/full_config.rs
  • src/config.rs
  • src/crypto/record.rs
  • src/dht.rs
  • src/gossip/merge/bubble.rs
  • src/gossip/merge/message_overlap.rs
  • src/gossip/topic/bootstrap.rs
  • src/gossip/topic/publisher.rs
  • src/gossip/topic/topic.rs
  • test_many_chats.sh

Comment thread Cargo.toml
keywords = ["networking"]
categories = ["network-programming"]
version = "0.3.1"
version = "0.3.2"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Description: Check whether the changed merge constructors are exported through the crate's public module tree.
set -euo pipefail

fd -e rs '^(lib|mod)$|gossip|merge' src -x sh -c '
  echo "--- $1"
  rg -nP -C3 "pub\s+(mod|use)\s+.*(gossip|merge|BubbleMerge|MessageOverlapMerge)|\b(BubbleMerge|MessageOverlapMerge)\b" "$1" || true
' sh {}

Repository: rustonbsd/distributed-topic-tracker

Length of output: 61


🏁 Script executed:

# First, find all Rust files and understand directory structure
fd -e rs --type f src | head -20

Repository: rustonbsd/distributed-topic-tracker

Length of output: 61


🏁 Script executed:

# Search for BubbleMerge and MessageOverlapMerge definitions
rg "struct\s+(BubbleMerge|MessageOverlapMerge)" -t rust

Repository: rustonbsd/distributed-topic-tracker

Length of output: 542


🏁 Script executed:

# Check the public exports in lib.rs
cat src/lib.rs 2>/dev/null || echo "lib.rs not found"

Repository: rustonbsd/distributed-topic-tracker

Length of output: 1579


🏁 Script executed:

# Look for the merge module structure
fd -path "*/merge*" -t f src

Repository: rustonbsd/distributed-topic-tracker

Length of output: 247


🏁 Script executed:

# Find the new() constructor implementations for BubbleMerge
rg -A 5 "impl\s+BubbleMerge" src/gossip/merge/bubble.rs -t rust

Repository: rustonbsd/distributed-topic-tracker

Length of output: 771


🏁 Script executed:

# Find the new() constructor implementations for MessageOverlapMerge
rg -A 5 "impl\s+MessageOverlapMerge" src/gossip/merge/message_overlap.rs -t rust

Repository: rustonbsd/distributed-topic-tracker

Length of output: 848


🏁 Script executed:

# Look at the actual constructor signature for BubbleMerge
sed -n '/impl.*BubbleMerge/,/^}/p' src/gossip/merge/bubble.rs | head -30

Repository: rustonbsd/distributed-topic-tracker

Length of output: 1220


🏁 Script executed:

# Look at the actual constructor signature for MessageOverlapMerge
sed -n '/impl.*MessageOverlapMerge/,/^}/p' src/gossip/merge/message_overlap.rs | head -30

Repository: rustonbsd/distributed-topic-tracker

Length of output: 1214


Change version bump to 0.4.0 to reflect the breaking API change.

Adding initial_interval as a required positional argument to BubbleMerge::new() and MessageOverlapMerge::new() breaks backward compatibility. These constructors are publicly exported from lib.rs, so existing ^0.3 users will experience compilation failures. A patch bump (0.3.2) is incompatible with breaking changes; use a minor pre-1.0 bump such as 0.4.0.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Cargo.toml` at line 10, Update the crate version in Cargo.toml from "0.3.2"
to "0.4.0" to reflect the breaking API change introduced by adding the required
positional parameter `initial_interval` to the public constructors
`BubbleMerge::new()` and `MessageOverlapMerge::new()` (exported in lib.rs);
locate Cargo.toml and bump the version string to "0.4.0" so semantic versioning
correctly signals the incompatible change to users.

Comment thread src/gossip/topic/topic.rs
Comment on lines +485 to +596
#[tokio::test]
async fn test_topic_survives_topic_drop_split() {
let secret_key = iroh::SecretKey::generate();
let signing_key = mainline::SigningKey::from_bytes(&secret_key.to_bytes());
let endpoint = iroh::Endpoint::builder(iroh::endpoint::presets::N0)
.secret_key(secret_key.clone())
.bind()
.await
.expect("failed to bind endpoint");
let gossip = iroh_gossip::net::Gossip::builder().spawn(endpoint.clone());

let topic_id = crate::TopicId::new("my-iroh-gossip-topic-survives-drop-split".to_string());
let initial_secret = b"my-initial-secret".to_vec();

let record_publisher = RecordPublisher::new(
topic_id.clone(),
signing_key.clone(),
None,
initial_secret,
crate::config::Config::default(),
);

let topic = crate::Topic::new(record_publisher, gossip.clone(), true)
.await
.expect("failed to create Topic");

let cancel_token = topic.cancel_token();
let (_sender, _receiver) = topic.split().await.expect("failed to split topic");

drop(topic);

assert!(
tokio::time::timeout(std::time::Duration::from_secs(2), cancel_token.cancelled())
.await
.is_err()
);

assert!(!cancel_token.is_cancelled());

drop(_sender);
drop(_receiver);

assert!(
tokio::time::timeout(std::time::Duration::from_secs(2), cancel_token.cancelled())
.await
.is_ok()
);

assert!(cancel_token.is_cancelled());
}

#[tokio::test]
async fn test_topic_survives_topic_drop_manual_sender_receiver() {
let secret_key = iroh::SecretKey::generate();
let signing_key = mainline::SigningKey::from_bytes(&secret_key.to_bytes());
let endpoint = iroh::Endpoint::builder(iroh::endpoint::presets::N0)
.secret_key(secret_key.clone())
.bind()
.await
.expect("failed to bind endpoint");
let gossip = iroh_gossip::net::Gossip::builder().spawn(endpoint.clone());

let topic_id = crate::TopicId::new(
"my-iroh-gossip-topic-survives-drop-manual-sender-receiver".to_string(),
);
let initial_secret = b"my-initial-secret".to_vec();

let record_publisher = RecordPublisher::new(
topic_id.clone(),
signing_key.clone(),
None,
initial_secret,
crate::config::Config::default(),
);

let topic = crate::Topic::new(record_publisher, gossip.clone(), true)
.await
.expect("failed to create Topic");

let cancel_token = topic.cancel_token();
let (_sender, _receiver) = (
topic
.gossip_sender()
.await
.expect("failed to get gossip sender"),
topic
.gossip_receiver()
.await
.expect("failed to get gossip receiver"),
);

drop(topic);

assert!(
tokio::time::timeout(std::time::Duration::from_secs(2), cancel_token.cancelled())
.await
.is_err()
);

assert!(!cancel_token.is_cancelled());

drop(_sender);
drop(_receiver);

assert!(
tokio::time::timeout(std::time::Duration::from_secs(2), cancel_token.cancelled())
.await
.is_ok()
);

assert!(cancel_token.is_cancelled());
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick | 🔵 Trivial

Optional: de-duplicate the three survival/shutdown tests.

test_topic_full_shutdown_on_drop, test_topic_survives_topic_drop_split, and test_topic_survives_topic_drop_manual_sender_receiver share ~15 lines of identical setup (secret key → endpoint → gossip → RecordPublisherTopic::new). A small async fn make_topic(name: &str) -> (Topic, CancellationToken) helper inside the test module would make each test a handful of lines focused on the actual lifecycle assertions.

No functional concern — prior feedback on unifying timeouts and asserting shutdown after dropping both handles is already incorporated (2s unified; drop(_sender); drop(_receiver); … .is_ok()).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/gossip/topic/topic.rs` around lines 485 - 596, Extract the duplicated
async test setup into a helper async fn (e.g., make_topic) inside the test
module that accepts a name: &str and returns (crate::Topic,
tokio_util::sync::CancellationToken) by performing the secret_key/SigningKey
generation, endpoint bind, iroh_gossip::net::Gossip spawn, TopicId::new,
RecordPublisher::new and crate::Topic::new calls currently repeated in
test_topic_full_shutdown_on_drop, test_topic_survives_topic_drop_split, and
test_topic_survives_topic_drop_manual_sender_receiver; then update each test to
call make_topic(...) to obtain the Topic and its cancel_token and keep the
existing lifecycle assertions unchanged (references: Topic::new,
RecordPublisher::new, TopicId::new, topic.cancel_token(), topic.split(),
topic.gossip_sender(), topic.gossip_receiver()).

@rustonbsd rustonbsd merged commit ccf2f1e into main Apr 21, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant