#![cfg(feature = "test_utils")]
use holochain::sweettest::SweetAgents;
use holochain::sweettest::SweetConductor;
use holochain::sweettest::SweetDnaFile;
use holochain::test_utils::consistency_10s;
use holochain_serialized_bytes::prelude::*;
use holochain_types::prelude::*;
use holochain_wasm_test_utils::TestWasm;
use holochain_zome_types::inline_zome::BoxApi;
#[derive(serde::Serialize, serde::Deserialize, Debug, SerializedBytes, derive_more::From)]
struct BaseTarget(EntryHash, EntryHash);
fn links_zome() -> InlineZome {
InlineZome::new_unique(vec![])
.callback("create_link", move |api, base_target: BaseTarget| {
let hash = api.create_link(CreateLinkInput::new(
base_target.0,
base_target.1,
().into(),
ChainTopOrdering::default(),
))?;
Ok(hash)
})
.callback(
"get_links",
move |api: BoxApi, base: EntryHash| -> InlineZomeResult<Vec<Vec<Link>>> {
Ok(api.get_links(vec![GetLinksInput::new(base, None)])?)
},
)
}
#[tokio::test(flavor = "multi_thread")]
#[cfg(feature = "slow_tests")]
async fn many_agents_can_reach_consistency_agent_links() {
observability::test_run().ok();
const NUM_AGENTS: usize = 20;
let (dna_file, _) = SweetDnaFile::unique_from_inline_zome("links", links_zome())
.await
.unwrap();
let mut conductor = SweetConductor::from_config(Default::default()).await;
let agents = SweetAgents::get(conductor.keystore(), NUM_AGENTS).await;
let apps = conductor
.setup_app_for_agents("app", &agents, &[dna_file])
.await
.unwrap();
let cells = apps.cells_flattened();
let alice = cells[0].zome("links");
consistency_10s(&cells[..]).await;
let base: EntryHash = cells[0].agent_pubkey().clone().into();
let target: EntryHash = cells[1].agent_pubkey().clone().into();
let _: HeaderHash = conductor
.call(
&alice,
"create_link",
BaseTarget(base.clone(), target.clone()),
)
.await;
consistency_10s(&cells[..]).await;
let mut seen = [0usize; NUM_AGENTS];
for (i, cell) in cells.iter().enumerate() {
let links: Vec<Vec<Link>> = conductor
.call(&cell.zome("links"), "get_links", base.clone())
.await;
seen[i] = links.into_iter().next().unwrap().len();
}
assert_eq!(seen.to_vec(), [1; NUM_AGENTS].to_vec());
}
#[tokio::test(flavor = "multi_thread")]
#[cfg(feature = "slow_tests")]
async fn many_agents_can_reach_consistency_normal_links() {
observability::test_run().ok();
const NUM_AGENTS: usize = 30;
let (dna_file, _) = SweetDnaFile::unique_from_test_wasms(vec![TestWasm::Link])
.await
.unwrap();
let mut conductor = SweetConductor::from_config(Default::default()).await;
let agents = SweetAgents::get(conductor.keystore(), NUM_AGENTS).await;
let apps = conductor
.setup_app_for_agents("app", &agents, &[dna_file])
.await
.unwrap();
let cells = apps.cells_flattened();
let alice = cells[0].zome(TestWasm::Link);
let _: HeaderHash = conductor.call(&alice, "create_link", ()).await;
consistency_10s(&cells[..]).await;
let mut num_seen = 0;
for cell in &cells {
let links: Vec<Link> = conductor
.call(&cell.zome(TestWasm::Link), "get_links", ())
.await;
num_seen += links.len();
}
assert_eq!(num_seen, NUM_AGENTS);
}
#[tokio::test(flavor = "multi_thread")]
#[cfg(feature = "test_utils")]
#[ignore = "Slow test for CI that is only useful for timing"]
async fn stuck_conductor_wasm_calls() -> anyhow::Result<()> {
observability::test_run().ok();
let (dna_file, _) = SweetDnaFile::unique_from_test_wasms(vec![TestWasm::MultipleCalls]).await?;
let mut conductor = SweetConductor::from_standard_config().await;
let alice = conductor
.setup_app("app", &[dna_file])
.await
.unwrap()
.into_cells()
.into_iter()
.next()
.unwrap();
let alice = alice.zome(TestWasm::MultipleCalls);
#[derive(Clone, Debug, PartialEq, Serialize, Deserialize, SerializedBytes)]
pub struct TwoInt(pub u32, pub u32);
let _: () = conductor.call(&alice, "slow_fn", TwoInt(0, 0)).await;
let all_now = std::time::Instant::now();
tracing::debug!("starting slow fn");
let conductor_arc = std::sync::Arc::new(conductor);
let mut handles = Vec::new();
for i in 0..1000 {
let h = tokio::task::spawn({
let alice = alice.clone();
let conductor = conductor_arc.clone();
async move {
let now = std::time::Instant::now();
tracing::debug!("starting slow fn {}", i);
let _: () = conductor.call(&alice, "slow_fn", TwoInt(i, 5)).await;
tracing::debug!("finished slow fn {} in {}", i, now.elapsed().as_secs());
}
});
handles.push(h);
}
for h in handles {
h.await.unwrap();
}
tracing::debug!("finished all slow fn in {}", all_now.elapsed().as_secs());
Ok(())
}