Files
tsunagi/crates/tsunagi-core/tests/mainline.rs
T
Aleksandr BogomiakovandClaude 9966b50407 Rename the CLI package to tsunagi and its executable to tsng
The command line package is now `tsunagi` and builds the `tsng` executable,
which the packages, the unit, the installers and the workflows install and
run. The core library package becomes `tsunagi-core`, keeping the library name
`tsunagi`, so no code changes `use tsunagi::...`. Directories follow
(crates/tsunagi-core, crates/tsunagi). Every command shown to a person is now
`tsng ...`.

Co-Authored-By: Claude <noreply@anthropic.com>
2026-10-03 02:14:43 +01:00

93 lines
3.7 KiB
Rust

//! Local Mainline Testnet, real iroh authentication, and durable agent state.
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use std::time::Duration;
use tsunagi::testing::{local_config, network, wait_for_peers};
use tsunagi::{Agent, config::DiscoveryPolicy, discovery::MainlineDiscovery};
#[tokio::test]
#[ignore = "uses public Mainline DHT and iroh relay services"]
async fn public_dht_finds_and_authenticates_two_agents() {
tsunagi::testing::init_tracing();
let a_dir = tempfile::tempdir().unwrap();
let b_dir = tempfile::tempdir().unwrap();
let config = |path: &std::path::Path| {
tsunagi::config::AgentConfig::new(tsunagi::config::StoragePaths::under(path))
.with_transport(tsunagi::config::TransportPolicy::N0Defaults)
.with_dht(MainlineDiscovery::default())
};
let a = Agent::spawn(config(a_dir.path())).await.unwrap();
let b = Agent::spawn(config(b_dir.path())).await.unwrap();
let (name, secret) = network("mainline-public-smoke");
let id = a.join_network(&name, &secret).await.unwrap();
b.join_network(&name, &secret).await.unwrap();
let found = tokio::time::timeout(Duration::from_secs(180), async {
loop {
if !a.network_status(id).await.unwrap().peers.is_empty()
&& !b.network_status(id).await.unwrap().peers.is_empty()
{
break;
}
tokio::time::sleep(Duration::from_millis(250)).await;
}
})
.await;
a.shutdown().await;
b.shutdown().await;
assert!(
found.is_ok(),
"public DHT/relay discovery did not connect within three minutes"
);
}
#[tokio::test]
async fn two_agents_restore_after_all_dht_records_and_addresses_are_gone() {
let first_dir = tempfile::tempdir().unwrap();
let second_dir = tempfile::tempdir().unwrap();
let (name, secret) = network("dht-restart");
let mut identities = None;
for _ in 0..2 {
if identities.is_some() {
for path in [first_dir.path(), second_dir.path()] {
let cache = tsunagi::config::StoragePaths::under(path).cache_dir;
if cache.exists() {
std::fs::remove_dir_all(cache).unwrap();
}
}
}
// A fresh Testnet means no old rendezvous record survives. The second
// pass restores the networks from SQLite without another join command.
let net = mainline::Testnet::builder(5).build().unwrap();
let config = |path: &std::path::Path| {
local_config(path)
.with_dht(MainlineDiscovery::local_testnet(&net.bootstrap).unwrap())
.with_discovery_policy(DiscoveryPolicy {
lookup_interval: Duration::from_millis(100),
max_lookup_interval: Duration::from_millis(300),
..Default::default()
})
};
let (a, b) = tokio::join!(
Agent::spawn(config(first_dir.path())),
Agent::spawn(config(second_dir.path()))
);
let a = a.unwrap();
let b = b.unwrap();
let id = tsunagi::identity::NetworkKeys::derive(&name, &secret).network_id();
if let Some(old) = identities {
assert_eq!((a.endpoint_id(), b.endpoint_id()), old);
} else {
identities = Some((a.endpoint_id(), b.endpoint_id()));
let (ra, rb) = tokio::join!(
a.join_network(&name, &secret),
b.join_network(&name, &secret)
);
assert_eq!(ra.unwrap(), id);
assert_eq!(rb.unwrap(), id);
}
wait_for_peers(&a, id, 1).await;
wait_for_peers(&b, id, 1).await;
a.shutdown().await;
b.shutdown().await;
}
}