96 lines
3.7 KiB
Rust
96 lines
3.7 KiB
Rust
//! Live integration against a REAL local Freenet node — the house pattern
|
|
//! (`#[ignore]`d corpus tests inside the crate, run on demand):
|
|
//!
|
|
//! 1. start a node: freenet local local --config-dir <dir> --data-dir <dir>
|
|
//! 2. set SOULSTREAM_F0_WASM to a contract wasm (the F0 spike's
|
|
//! research/freenet-f0/f0-contract build works verbatim)
|
|
//! 3. cargo test -p freenet-adapter --release -- --ignored
|
|
//!
|
|
//! Exercises the full adapter loop: detect → enable → put → get →
|
|
//! subscribe → update (with the first-write priming path) → notification
|
|
//! event → kill switch mid-life.
|
|
|
|
use freenet_adapter::{AdapterEvent, AdapterStatus, FreenetAdapter};
|
|
use std::time::Duration;
|
|
|
|
fn wasm() -> Option<Vec<u8>> {
|
|
let path = std::env::var("SOULSTREAM_F0_WASM").ok()?;
|
|
std::fs::read(path).ok()
|
|
}
|
|
|
|
#[tokio::test]
|
|
#[ignore = "needs a local freenet node on 7509 and SOULSTREAM_F0_WASM"]
|
|
async fn full_loop_against_live_node() {
|
|
let Some(code) = wasm() else {
|
|
panic!("set SOULSTREAM_F0_WASM to a contract wasm path");
|
|
};
|
|
let a = FreenetAdapter::default();
|
|
|
|
// Detection first — a wrong environment should fail here, loudly.
|
|
let detected = a.detect().await;
|
|
assert_eq!(detected, AdapterStatus::Connected, "node not reachable: {detected:?}");
|
|
|
|
a.enable();
|
|
// Wait for the supervisor to connect.
|
|
let mut tries = 0;
|
|
while a.status() != AdapterStatus::Connected {
|
|
tokio::time::sleep(Duration::from_millis(200)).await;
|
|
tries += 1;
|
|
assert!(tries < 50, "supervisor never connected: {:?}", a.status());
|
|
}
|
|
|
|
// A fresh instance per run: params carry a nonce.
|
|
let params = format!("adapter-live-{}", std::process::id()).into_bytes();
|
|
let ids = FreenetAdapter::contract_ids(code.clone(), ¶ms);
|
|
|
|
// PUT initial state.
|
|
let put_ids = a
|
|
.contract_put(code.clone(), ¶ms, br#"["alpha"]"#.to_vec(), false)
|
|
.await
|
|
.expect("put");
|
|
assert_eq!(put_ids.instance, ids.instance);
|
|
|
|
// GET it back.
|
|
let state = a.contract_get(&ids.instance, false).await.expect("get");
|
|
assert_eq!(state, br#"["alpha"]"#);
|
|
|
|
// SUBSCRIBE, then watch for the update notification through the
|
|
// adapter's bounded event bus.
|
|
let mut events = a.subscribe();
|
|
assert!(a.contract_subscribe(&ids.instance).await.expect("subscribe"));
|
|
|
|
// UPDATE via the primed path (exercises get_then_update's shape even
|
|
// though this node already holds the contract).
|
|
let summary = a
|
|
.get_then_update(code.clone(), ¶ms, br#"["beta"]"#.to_vec())
|
|
.await
|
|
.expect("update");
|
|
assert!(!summary.is_empty(), "update summary should carry bytes");
|
|
|
|
// The merged state must be the union.
|
|
let state = a.contract_get(&ids.instance, false).await.expect("get2");
|
|
assert_eq!(state, br#"["alpha","beta"]"#);
|
|
|
|
// The notification should have crossed the event bus (ids only).
|
|
let deadline = tokio::time::Instant::now() + Duration::from_secs(10);
|
|
let mut saw_update = false;
|
|
while tokio::time::Instant::now() < deadline {
|
|
match tokio::time::timeout(Duration::from_secs(1), events.recv()).await {
|
|
Ok(Ok(AdapterEvent::ContractUpdate { instance, .. })) if instance == ids.instance => {
|
|
saw_update = true;
|
|
break;
|
|
}
|
|
Ok(Ok(_)) => continue,
|
|
Ok(Err(_)) => break,
|
|
Err(_) => continue,
|
|
}
|
|
}
|
|
assert!(saw_update, "no ContractUpdate event reached the bus");
|
|
|
|
// Kill switch mid-life: immediate, and ops refuse afterwards.
|
|
a.disable();
|
|
assert_eq!(a.status(), AdapterStatus::Disabled);
|
|
let err = a.contract_get(&ids.instance, false).await;
|
|
assert!(matches!(err, Err(freenet_adapter::AdapterError::Disabled)));
|
|
}
|