diff --git a/src/iroh.rs b/src/iroh.rs index c3f6a1a..36b2385 100644 --- a/src/iroh.rs +++ b/src/iroh.rs @@ -347,10 +347,11 @@ impl NodeHost { if node.prefix() != prefix { anyhow::bail!("peer returned a Merkle node for the wrong prefix"); } - let local_node = local_merkle_index - .node(&prefix)? - .ok_or_else(|| anyhow::anyhow!("local Merkle node is missing"))?; - if local_node.hash() == node.hash() { + let local_node = local_merkle_index.node(&prefix)?; + if local_node + .as_ref() + .is_some_and(|local| local.hash() == node.hash()) + { continue; } match node { diff --git a/src/iroh/tests.rs b/src/iroh/tests.rs index 9ff168f..b2b577b 100644 --- a/src/iroh/tests.rs +++ b/src/iroh/tests.rs @@ -109,6 +109,71 @@ async fn announces_folder_changes_to_an_authorized_peer() -> anyhow::Result<()> Ok(()) } +#[tokio::test] +async fn requests_remote_merkle_children_missing_from_a_small_local_manifest() -> anyhow::Result<()> +{ + let source_directory = TempDir::new()?; + let target_directory = TempDir::new()?; + let source = + NodeHost::load_with_lan_discovery(source_directory.path(), SecretKey::generate(), false) + .await?; + let target = + NodeHost::load_with_lan_discovery(target_directory.path(), SecretKey::generate(), false) + .await?; + let folder_id = Uuid::new_v4(); + let capability = "folder-capability".to_owned(); + let remote_manifest = large_manifest(folder_id); + source + .register_folder( + FolderSession { + folder_id, + capability: capability.clone(), + active_member_ids: BTreeSet::from([target.endpoint_address().id.to_string()]), + roster: test_roster(folder_id, [target.endpoint_address().id.to_string()]), + peer_endpoints: vec![target.endpoint_address()], + }, + remote_manifest.clone(), + ) + .await?; + source.wait_until_online().await?; + target.wait_until_online().await?; + + let received = target + .request_manifest_delta( + source.endpoint_address(), + folder_id, + capability, + None, + &crate::domain::Manifest::empty(folder_id), + ) + .await?; + + assert_eq!(received, remote_manifest); + source.shutdown().await?; + target.shutdown().await?; + Ok(()) +} + +fn large_manifest(folder_id: Uuid) -> crate::domain::Manifest { + let mut manifest = crate::domain::Manifest::empty(folder_id); + for number in 0..65 { + let path = format!("file-{number}.txt"); + manifest.entries.insert( + path.clone(), + crate::domain::Entry { + path, + kind: crate::domain::EntryKind::File, + blob_hash: Some(format!("blob-{number}")), + size_bytes: Some(1), + modified_at: time::OffsetDateTime::UNIX_EPOCH, + clock: Default::default(), + author_device_id: "source".to_owned(), + }, + ); + } + manifest +} + #[tokio::test] async fn serves_multiple_isolated_folders_from_one_endpoint() -> anyhow::Result<()> { let source_directory = TempDir::new()?;