Skip to content

Commit 382cbca

Browse files
committed
test(sync): add L4+L5 bootstrap E2E tests (10 tests) + fix bootstrap path
L4 bootstrap tests (l4_bootstrap.rs, 6 tests): - LB01: Node starts with --seed-list flag - LB02: Node exits on missing seed list file - LB03: Node exits on invalid JSON seed list - LB04: --peer takes precedence over --seed-list - LB05: --seed-authority dao flag accepted - LB06: --seed-list full path exits on NoResponses (stub) L5 bootstrap tests (l5_bootstrap.rs, 4 tests): - LB07: Container starts with --seed-list flag - LB08: Container exits on missing seed list file - LB09: Container with --seed-authority dao - LB10: Container with --seed-list + --peer precedence Bug fix: moved bootstrap code out of the if-!args.adapters.is_empty() block so it runs whenever --seed-list is provided, regardless of whether --adapter is specified. Creates a minimal NodeTransport when no adapters are loaded. Also fixed: node_id format (64 hex chars, not 65).
1 parent 98c5c9f commit 382cbca

3 files changed

Lines changed: 967 additions & 51 deletions

File tree

sync-e2e-tests/stoolap-node/src/main.rs

Lines changed: 61 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -215,57 +215,6 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
215215
"built GDP advertisement"
216216
);
217217

218-
// --- Bootstrap: run BootstrapOrchestrator if --seed-list provided and no --peer ---
219-
if let Some(seed_list_path) = &args.seed_list {
220-
if args.peers.is_empty() {
221-
use octo_transport::bootstrap::{BootstrapConfig, BootstrapOrchestrator};
222-
use octo_network::mon::bootstrap::SeedListAuthority;
223-
use octo_network::gdp::discovery::{BootstrapMethod, DiscoveryState};
224-
225-
let authority = match args.seed_authority.as_str() {
226-
"dao" => SeedListAuthority::Dao,
227-
_ => SeedListAuthority::Foundation,
228-
};
229-
230-
let seed_json = match std::fs::read_to_string(seed_list_path) {
231-
Ok(json) => json,
232-
Err(e) => {
233-
tracing::error!(path = %seed_list_path, error = %e, "failed to read seed list");
234-
std::process::exit(1);
235-
}
236-
};
237-
let seed_envelope: octo_network::mon::bootstrap::SeedListEnvelope =
238-
match serde_json::from_str(&seed_json) {
239-
Ok(env) => env,
240-
Err(e) => {
241-
tracing::error!(error = %e, "failed to parse seed list");
242-
std::process::exit(1);
243-
}
244-
};
245-
246-
let bootstrap_config = BootstrapConfig {
247-
authority,
248-
current_epoch: now,
249-
node_id,
250-
node_pubkey: node_id, // Same as node_id for now
251-
..BootstrapConfig::default()
252-
};
253-
254-
let mut orch = BootstrapOrchestrator::new(seed_envelope, bootstrap_config);
255-
let mut disc_state = DiscoveryState::new(BootstrapMethod::Static);
256-
257-
match orch.run(&transport, &discovery.lock().unwrap(), &mut disc_state).await {
258-
Ok(count) => {
259-
tracing::info!(peers = count, "bootstrap complete");
260-
}
261-
Err(e) => {
262-
tracing::error!(error = %e, "bootstrap failed");
263-
std::process::exit(1);
264-
}
265-
}
266-
}
267-
}
268-
269218
// --- Outbound: subscribe transport peer, spawn drain task ---
270219
session.subscribe_peer(transport_peer).unwrap();
271220
let session_clone = session.clone();
@@ -432,6 +381,67 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
432381
None
433382
};
434383

384+
// --- Bootstrap: run BootstrapOrchestrator if --seed-list provided and no --peer ---
385+
if let Some(seed_list_path) = &args.seed_list {
386+
if args.peers.is_empty() {
387+
use octo_network::mon::bootstrap::SeedListAuthority;
388+
use octo_transport::bootstrap::{BootstrapConfig, BootstrapOrchestrator};
389+
use octo_network::gdp::discovery::{BootstrapMethod, DiscoveryState};
390+
391+
// Use existing transport if adapters loaded, otherwise create minimal one
392+
let bootstrap_transport = transport_opt.clone().unwrap_or_else(|| {
393+
Arc::new(octo_transport::NodeTransport::new(vec![]))
394+
});
395+
396+
let authority = match args.seed_authority.as_str() {
397+
"dao" => SeedListAuthority::Dao,
398+
_ => SeedListAuthority::Foundation,
399+
};
400+
401+
let now = SystemTime::now()
402+
.duration_since(UNIX_EPOCH)
403+
.unwrap_or_default()
404+
.as_secs();
405+
406+
let seed_json = match std::fs::read_to_string(seed_list_path) {
407+
Ok(json) => json,
408+
Err(e) => {
409+
tracing::error!(path = %seed_list_path, error = %e, "failed to read seed list");
410+
std::process::exit(1);
411+
}
412+
};
413+
let seed_envelope: octo_network::mon::bootstrap::SeedListEnvelope =
414+
match serde_json::from_str(&seed_json) {
415+
Ok(env) => env,
416+
Err(e) => {
417+
tracing::error!(error = %e, "failed to parse seed list");
418+
std::process::exit(1);
419+
}
420+
};
421+
422+
let bootstrap_config = BootstrapConfig {
423+
authority,
424+
current_epoch: now,
425+
node_id,
426+
node_pubkey: node_id,
427+
..BootstrapConfig::default()
428+
};
429+
430+
let mut orch = BootstrapOrchestrator::new(seed_envelope, bootstrap_config);
431+
let mut disc_state = DiscoveryState::new(BootstrapMethod::Static);
432+
433+
match orch.run(&bootstrap_transport, &discovery.lock().unwrap(), &mut disc_state).await {
434+
Ok(count) => {
435+
tracing::info!(peers = count, "bootstrap complete");
436+
}
437+
Err(e) => {
438+
tracing::error!(error = %e, "bootstrap failed");
439+
std::process::exit(1);
440+
}
441+
}
442+
}
443+
}
444+
435445
// TCP sync path (default, backward-compatible)
436446
let listener = TcpListener::bind(format!("0.0.0.0:{}", args.listen)).await?;
437447
let adapter_for_accept = adapter_arc.clone();

0 commit comments

Comments
 (0)