radicle/node/config: Use `IndexSet`

With `connect: std::collections::HashSet`, we suffer unpredictable reordering
of values in our test cases.

Use `connect: indexmap::IndexSet` instead, which preserves insertion
order for iteration.
This commit is contained in:
Lorenz Leutgeb 2026-04-27 14:51:41 +02:00
parent 099722dd08
commit 3bc8abdc29
No known key found for this signature in database
6 changed files with 17 additions and 10 deletions

1
Cargo.lock generated
View File

@ -3217,6 +3217,7 @@ dependencies = [
"cyphernet", "cyphernet",
"fastrand", "fastrand",
"gix-packetline", "gix-packetline",
"indexmap",
"lexopt", "lexopt",
"log", "log",
"mio", "mio",

View File

@ -34,6 +34,7 @@ gix-hash = { version = "0.22.1", default-features = false, features = ["sha1"] }
gix-packetline = { version = "0.21.1", default-features = false } gix-packetline = { version = "0.21.1", default-features = false }
human-panic = "2.0.6" human-panic = "2.0.6"
humantime = "2.3" humantime = "2.3"
indexmap = { version = "2", default-features = false }
itertools = "0.14" itertools = "0.14"
lexopt = "0.3.0" lexopt = "0.3.0"
libc = "0.2.137" libc = "0.2.137"

View File

@ -23,6 +23,7 @@ crossbeam-channel = { workspace = true }
cyphernet = { workspace = true, features = ["dns", "ed25519", "p2p-ed25519", "noise-framework", "noise_sha2"] } cyphernet = { workspace = true, features = ["dns", "ed25519", "p2p-ed25519", "noise-framework", "noise_sha2"] }
fastrand = { workspace = true } fastrand = { workspace = true }
gix-packetline = { workspace = true, features = ["blocking-io"] } gix-packetline = { workspace = true, features = ["blocking-io"] }
indexmap = { workspace = true }
lexopt = { workspace = true } lexopt = { workspace = true }
log = { workspace = true, features = ["kv", "std"] } log = { workspace = true, features = ["kv", "std"] }
mio = { version = "1", features = ["net", "os-poll"] } mio = { version = "1", features = ["net", "os-poll"] }

View File

@ -233,11 +233,11 @@ fn test_inbound_connection() {
#[test] #[test]
fn test_persistent_peer_connect() { fn test_persistent_peer_connect() {
use std::collections::HashSet; use indexmap::IndexSet;
let bob = Peer::new("bob", [8, 8, 8, 8]); let bob = Peer::new("bob", [8, 8, 8, 8]);
let eve = Peer::new("eve", [9, 9, 9, 9]); let eve = Peer::new("eve", [9, 9, 9, 9]);
let connect = HashSet::<ConnectAddress>::from_iter([ let connect = IndexSet::<ConnectAddress>::from_iter([
(bob.id(), bob.address()).into(), (bob.id(), bob.address()).into(),
(eve.id(), eve.address()).into(), (eve.id(), eve.address()).into(),
]); ]);
@ -1186,7 +1186,7 @@ fn test_inventory_relay() {
#[test] #[test]
fn test_persistent_peer_reconnect_attempt() { fn test_persistent_peer_reconnect_attempt() {
use std::collections::HashSet; use indexmap::IndexSet;
let mut bob = Peer::new("bob", [8, 8, 8, 8]); let mut bob = Peer::new("bob", [8, 8, 8, 8]);
let mut eve = Peer::new("eve", [9, 9, 9, 9]); let mut eve = Peer::new("eve", [9, 9, 9, 9]);
@ -1196,7 +1196,7 @@ fn test_persistent_peer_reconnect_attempt() {
MockStorage::empty(), MockStorage::empty(),
peer::Config { peer::Config {
config: Config { config: Config {
connect: HashSet::from_iter([ connect: IndexSet::from_iter([
(bob.id(), bob.address()).into(), (bob.id(), bob.address()).into(),
(eve.id(), eve.address()).into(), (eve.id(), eve.address()).into(),
]), ]),
@ -1246,7 +1246,7 @@ fn test_persistent_peer_reconnect_attempt() {
#[test] #[test]
fn test_persistent_peer_reconnect_success() { fn test_persistent_peer_reconnect_success() {
use std::collections::HashSet; use indexmap::IndexSet;
let bob = Peer::with_storage("bob", [9, 9, 9, 9], MockStorage::empty()); let bob = Peer::with_storage("bob", [9, 9, 9, 9], MockStorage::empty());
let mut alice = Peer::config( let mut alice = Peer::config(
@ -1255,7 +1255,7 @@ fn test_persistent_peer_reconnect_success() {
MockStorage::empty(), MockStorage::empty(),
peer::Config { peer::Config {
config: Config { config: Config {
connect: HashSet::from_iter([(bob.id, bob.addr()).into()]), connect: IndexSet::from_iter([(bob.id, bob.addr()).into()]),
..Config::new(node::Alias::new("alice")) ..Config::new(node::Alias::new("alice"))
}, },
..peer::Config::default() ..peer::Config::default()

View File

@ -42,7 +42,7 @@ dunce = { workspace = true }
fast-glob = { version = "0.3.2" } fast-glob = { version = "0.3.2" }
fastrand = { workspace = true, features = ["std"] } fastrand = { workspace = true, features = ["std"] }
git2 = { workspace = true, features = ["vendored-libgit2"] } git2 = { workspace = true, features = ["vendored-libgit2"] }
indexmap = { version = "2", features = ["serde"] } indexmap = { workspace = true, features = ["serde"] }
log = { workspace = true, features = ["std"] } log = { workspace = true, features = ["std"] }
nonempty = { workspace = true, features = ["serialize"] } nonempty = { workspace = true, features = ["serialize"] }
qcheck = { workspace = true, optional = true } qcheck = { workspace = true, optional = true }

View File

@ -1,9 +1,9 @@
use std::collections::HashSet;
use std::ops::Deref; use std::ops::Deref;
use std::str::FromStr; use std::str::FromStr;
use std::{fmt, net}; use std::{fmt, net};
use cyphernet::addr::PeerAddr; use cyphernet::addr::PeerAddr;
use indexmap::IndexSet;
use localtime::LocalDuration; use localtime::LocalDuration;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use serde_json as json; use serde_json as json;
@ -547,7 +547,11 @@ pub struct Config {
/// Peers to connect to on startup. /// Peers to connect to on startup.
/// Connections to these peers will be maintained. /// Connections to these peers will be maintained.
#[serde(default)] #[serde(default)]
pub connect: HashSet<ConnectAddress>, #[cfg_attr(
feature = "schemars",
schemars(with = "std::collections::HashSet<ConnectAddress>")
)]
pub connect: IndexSet<ConnectAddress>,
/// Specify the node's public addresses /// Specify the node's public addresses
#[serde(default)] #[serde(default)]
pub external_addresses: Vec<Address>, pub external_addresses: Vec<Address>,
@ -617,7 +621,7 @@ impl Config {
user_agent: Some(UserAgent::default()), user_agent: Some(UserAgent::default()),
peers: PeerConfig::default(), peers: PeerConfig::default(),
listen: vec![], listen: vec![],
connect: HashSet::default(), connect: IndexSet::default(),
external_addresses: vec![], external_addresses: vec![],
network: Network::default(), network: Network::default(),
proxy: None, proxy: None,