Skip to content

Commit 3b51f46

Browse files
committed
test(kitsune): add integration test
1 parent 0c6da54 commit 3b51f46

File tree

1 file changed

+212
-1
lines changed

1 file changed

+212
-1
lines changed

crates/kitsune2/src/lib.rs

+212-1
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ use kitsune2_transport_tx5::Tx5TransportFactory;
1818
/// - `op_store` - The default op store is [MemOpStoreFactory].
1919
/// Note: you will likely want to implement your own op store.
2020
/// - `peer_meta_store` - The default peer meta store is [factories::MemPeerMetaStoreFactory].
21-
/// Note: you will likely want to implement your own peer meta store.
2221
/// - `gossip` - The default gossip module is [K2GossipFactory].
2322
/// - `local_agent_store` - The default local agent store is [factories::CoreLocalAgentStoreFactory].
2423
pub fn default_builder() -> Builder {
@@ -37,3 +36,215 @@ pub fn default_builder() -> Builder {
3736
local_agent_store: factories::CoreLocalAgentStoreFactory::create(),
3837
}
3938
}
39+
40+
#[cfg(test)]
41+
mod test {
42+
use crate::default_builder;
43+
use bytes::Bytes;
44+
use kitsune2_api::{
45+
BoxFut, DhtArc, DynSpace, DynSpaceHandler, K2Result, KitsuneHandler,
46+
LocalAgent, OpId, SpaceHandler, SpaceId, Timestamp,
47+
};
48+
use kitsune2_core::{
49+
factories::{
50+
config::{CoreBootstrapConfig, CoreBootstrapModConfig},
51+
MemOpStoreFactory, MemoryOp,
52+
},
53+
Ed25519LocalAgent,
54+
};
55+
use kitsune2_gossip::{K2GossipConfig, K2GossipModConfig};
56+
use kitsune2_test_utils::{
57+
bootstrap::TestBootstrapSrv, enable_tracing, iter_check, random_bytes,
58+
space::TEST_SPACE_ID,
59+
};
60+
use kitsune2_transport_tx5::config::{
61+
Tx5TransportConfig, Tx5TransportModConfig,
62+
};
63+
use sbd_server::SbdServer;
64+
use std::sync::Arc;
65+
66+
fn create_op_list(num_ops: u16) -> (Vec<Bytes>, Vec<OpId>) {
67+
let mut ops = Vec::new();
68+
let mut op_ids = Vec::new();
69+
for _ in 0..num_ops {
70+
let op =
71+
MemoryOp::new(Timestamp::from_micros(0), random_bytes(256));
72+
let op_id = op.compute_op_id();
73+
ops.push(op.into());
74+
op_ids.push(op_id);
75+
}
76+
(ops, op_ids)
77+
}
78+
79+
#[derive(Debug)]
80+
struct HolochainKitsuneHandler;
81+
impl KitsuneHandler for HolochainKitsuneHandler {
82+
fn create_space(
83+
&self,
84+
_space_id: SpaceId,
85+
) -> BoxFut<'_, K2Result<DynSpaceHandler>> {
86+
Box::pin(async {
87+
let space_handler: DynSpaceHandler =
88+
Arc::new(HolochainSpaceHandler);
89+
Ok(space_handler)
90+
})
91+
}
92+
}
93+
94+
#[derive(Debug)]
95+
struct HolochainSpaceHandler;
96+
impl SpaceHandler for HolochainSpaceHandler {}
97+
98+
async fn make_kitsune_space(
99+
signal_server_url: &str,
100+
bootstrap_server_url: &str,
101+
) -> DynSpace {
102+
let mut kitsune_builder =
103+
default_builder().with_default_config().unwrap();
104+
kitsune_builder
105+
.config
106+
.set_module_config(&CoreBootstrapModConfig {
107+
core_bootstrap: CoreBootstrapConfig {
108+
server_url: bootstrap_server_url.to_owned(),
109+
backoff_max_ms: 1000,
110+
..Default::default()
111+
},
112+
})
113+
.unwrap();
114+
kitsune_builder.op_store = MemOpStoreFactory::create();
115+
kitsune_builder
116+
.config
117+
.set_module_config(&Tx5TransportModConfig {
118+
tx5_transport: Tx5TransportConfig {
119+
server_url: signal_server_url.to_owned(),
120+
signal_allow_plain_text: true,
121+
},
122+
})
123+
.unwrap();
124+
kitsune_builder
125+
.config
126+
.set_module_config(&K2GossipModConfig {
127+
k2_gossip: K2GossipConfig {
128+
initiate_interval_ms: 100,
129+
min_initiate_interval_ms: 75,
130+
round_timeout_ms: 10000,
131+
..Default::default()
132+
},
133+
})
134+
.unwrap();
135+
136+
let kitsune_handler = Arc::new(HolochainKitsuneHandler);
137+
let kitsune = kitsune_builder
138+
.build(kitsune_handler.clone())
139+
.await
140+
.unwrap();
141+
kitsune.space(TEST_SPACE_ID).await.unwrap()
142+
}
143+
144+
#[tokio::test]
145+
async fn two_node_gossip() {
146+
enable_tracing();
147+
let signal_server = SbdServer::new(Arc::new(sbd_server::Config {
148+
bind: vec!["127.0.0.1:0".to_string()],
149+
..Default::default()
150+
}))
151+
.await
152+
.unwrap();
153+
let signal_server_url =
154+
format!("ws://{}", signal_server.bind_addrs()[0]);
155+
156+
let bootstrap_server = TestBootstrapSrv::new(false).await;
157+
let bootstrap_server_url = bootstrap_server.addr().to_string();
158+
159+
// Create 2 Kitsune instances and 1 space each.
160+
let space_1 =
161+
make_kitsune_space(&signal_server_url, &bootstrap_server_url).await;
162+
let space_2 =
163+
make_kitsune_space(&signal_server_url, &bootstrap_server_url).await;
164+
165+
// Insert ops into both spaces' op stores.
166+
let (ops_1, op_ids_1) = create_op_list(100);
167+
space_1
168+
.op_store()
169+
.process_incoming_ops(ops_1.clone())
170+
.await
171+
.unwrap();
172+
let (ops_2, op_ids_2) = create_op_list(100);
173+
space_2
174+
.op_store()
175+
.process_incoming_ops(ops_2.clone())
176+
.await
177+
.unwrap();
178+
179+
// Create 2 agents.
180+
let local_agent_1 = Arc::new(Ed25519LocalAgent::default());
181+
let local_agent_2 = Arc::new(Ed25519LocalAgent::default());
182+
local_agent_1.set_tgt_storage_arc_hint(DhtArc::FULL);
183+
local_agent_2.set_tgt_storage_arc_hint(DhtArc::FULL);
184+
185+
// Join agent 1 to local space.
186+
space_1
187+
.local_agent_join(local_agent_1.clone())
188+
.await
189+
.unwrap();
190+
191+
// Wait for agent_1 to publish their info to the bootstrap & peer store.
192+
iter_check!(5000, 100, {
193+
let space_1_agents = space_1.peer_store().get_all().await.unwrap();
194+
if space_1_agents
195+
.iter()
196+
.any(|agent| agent.agent == *local_agent_1.agent())
197+
{
198+
break;
199+
}
200+
});
201+
202+
// Join agent 2 to local space.
203+
space_2
204+
.local_agent_join(local_agent_2.clone())
205+
.await
206+
.unwrap();
207+
208+
// Wait for agent 2 to publish their info and agent 1 to be polled from
209+
// bootstrap.
210+
iter_check!(5000, 100, {
211+
let space_2_agents = space_2.peer_store().get_all().await.unwrap();
212+
if space_2_agents.len() == 2 {
213+
break;
214+
}
215+
});
216+
217+
// Wait for peer connection setup to complete.
218+
tokio::time::sleep(std::time::Duration::from_secs(1)).await;
219+
220+
// Wait for gossip to exchange all ops.
221+
iter_check!(5000, 500, {
222+
let actual_ops_1 = space_1
223+
.op_store()
224+
.retrieve_ops(op_ids_2.clone())
225+
.await
226+
.unwrap();
227+
let actual_ops_2 = space_2
228+
.op_store()
229+
.retrieve_ops(op_ids_1.clone())
230+
.await
231+
.unwrap();
232+
if actual_ops_1.len() == ops_2.len()
233+
&& actual_ops_2.len() == ops_1.len()
234+
{
235+
break;
236+
} else {
237+
println!(
238+
"space 1 actual ops {}/expected {}",
239+
actual_ops_1.len(),
240+
ops_2.len()
241+
);
242+
println!(
243+
"space 2 actual ops {}/expected {}",
244+
actual_ops_2.len(),
245+
ops_1.len()
246+
);
247+
}
248+
});
249+
}
250+
}

0 commit comments

Comments
 (0)