-
Notifications
You must be signed in to change notification settings - Fork 21
Chunk regular diffs to avoid HC entry size limit #655
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
4 commits
Select commit
Hold shift + click to select a range
bf97de8
Use chunking for regular diffs to prevent Holochain entry size limit …
lucksus 614bb87
Migrate p-diff-sync tryorama tests to sweettest and add test for larg…
lucksus 0ac01d7
fix build
lucksus 6dce6a9
Merge branch 'dev' into fix/hc-entry-size-limit
lucksus File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
8,512 changes: 7,672 additions & 840 deletions
8,512
bootstrap-languages/p-diff-sync/hc-dna/Cargo.lock
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,3 @@ | ||
| #!/bin/bash | ||
| CARGO_TARGET_DIR=target RUSTFLAGS='--cfg getrandom_backend="custom"' cargo build --release --target wasm32-unknown-unknown && hc dna pack workdir && hc app pack workdir | ||
| # Build only the zome packages for WASM (exclude sweettest which has native-only dependencies) | ||
| CARGO_TARGET_DIR=target RUSTFLAGS='--cfg getrandom_backend="custom"' cargo build --release --target wasm32-unknown-unknown -p perspective_diff_sync -p perspective_diff_sync_integrity && hc dna pack workdir && hc app pack workdir |
37 changes: 37 additions & 0 deletions
37
bootstrap-languages/p-diff-sync/hc-dna/tests/sweettest/Cargo.toml
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,37 @@ | ||
| [package] | ||
| name = "perspective_diff_sync_tests" | ||
| version = "0.1.0" | ||
| edition = "2021" | ||
| publish = false | ||
|
|
||
| [lib] | ||
| path = "src/lib.rs" | ||
|
|
||
| [[test]] | ||
| name = "integration" | ||
| path = "src/integration_tests.rs" | ||
|
|
||
| [dependencies] | ||
| # Holochain dependencies - matching the zome's versions | ||
| holochain = { git = "https://github.com/coasys/holochain.git", branch = "0.6.0-coasys", features = ["sweettest", "test_utils"] } | ||
| hdk = { version = "0.6.0", git = "https://github.com/coasys/holochain.git", branch = "0.6.0-coasys" } | ||
| hdi = { version = "0.7.0", git = "https://github.com/coasys/holochain.git", branch = "0.6.0-coasys" } | ||
| holochain_types = { git = "https://github.com/coasys/holochain.git", branch = "0.6.0-coasys" } | ||
| holochain_serialized_bytes = "=0.0.56" | ||
| holochain_zome_types = { git = "https://github.com/coasys/holochain.git", branch = "0.6.0-coasys" } | ||
|
|
||
| # Shared types from the integrity zome | ||
| perspective_diff_sync_integrity = { path = "../../zomes/perspective_diff_sync_integrity" } | ||
|
|
||
| # Standard dependencies | ||
| serde = { version = "1.0", features = ["derive"] } | ||
| serde_json = "1.0" | ||
| tokio = { version = "1", features = ["full", "time"] } | ||
| futures = "0.3" | ||
| chrono = { version = "0.4", features = ["serde"] } | ||
| uuid = { version = "1.0", features = ["v4"] } | ||
| rand = "0.8" | ||
|
|
||
| [dev-dependencies] | ||
| # Test utilities | ||
| pretty_assertions = "1.4" |
13 changes: 13 additions & 0 deletions
13
bootstrap-languages/p-diff-sync/hc-dna/tests/sweettest/src/integration_tests.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| //! Integration tests for perspective_diff_sync | ||
| //! | ||
| //! This file imports all test modules and runs them as integration tests. | ||
|
|
||
| // Import test modules | ||
| mod utils; | ||
| mod test_commit_pull; | ||
| mod test_render; | ||
| mod test_revisions; | ||
| mod test_telepresence; | ||
|
|
||
| // Re-export for external access if needed | ||
| pub use utils::*; |
10 changes: 10 additions & 0 deletions
10
bootstrap-languages/p-diff-sync/hc-dna/tests/sweettest/src/lib.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| //! Sweettest integration tests for perspective_diff_sync | ||
| //! | ||
| //! These tests replace the old Tryorama TypeScript tests with Rust-based | ||
| //! sweettest tests for testing the p-diff-sync Holochain DNA. | ||
|
|
||
| pub mod utils; | ||
| pub mod test_commit_pull; | ||
| pub mod test_render; | ||
| pub mod test_revisions; | ||
| pub mod test_telepresence; |
259 changes: 259 additions & 0 deletions
259
bootstrap-languages/p-diff-sync/hc-dna/tests/sweettest/src/test_commit_pull.rs
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,259 @@ | ||
| //! Tests for commit and pull functionality | ||
| //! | ||
| //! These tests verify the CRDT merge behavior when agents commit | ||
| //! independently and then synchronize. | ||
|
|
||
| use crate::utils::*; | ||
| use holochain_types::prelude::ActionHash; | ||
| use perspective_diff_sync_integrity::PerspectiveDiff; | ||
|
|
||
| /// Test basic commit and pull between two agents | ||
| /// | ||
| /// Scenario: | ||
| /// 1. Alice and Bob start disconnected | ||
| /// 2. Alice commits a link | ||
| /// 3. Bob commits a link (creating a fork) | ||
| /// 4. They connect and exchange peer info | ||
| /// 5. Alice pulls Bob's commit (should merge) | ||
| /// 6. Bob pulls and sees Alice's link | ||
| #[tokio::test(flavor = "multi_thread")] | ||
| async fn test_merge_fetch() { | ||
| // Setup two disconnected conductors | ||
| let (mut conductors, cells) = setup_conductors(2, false).await; | ||
| let alice_cell = &cells[0]; | ||
| let bob_cell = &cells[1]; | ||
|
|
||
| // Create DID links for both agents | ||
| create_did_link(&conductors[0], alice_cell, "did:test:alice").await; | ||
| create_did_link(&conductors[1], bob_cell, "did:test:bob").await; | ||
|
|
||
| // Alice commits while Bob is disconnected | ||
| let alice_link = generate_link_expression("alice"); | ||
| let alice_commit: ActionHash = call_zome( | ||
| &conductors[0], | ||
| alice_cell, | ||
| "commit", | ||
| perspective_diff_sync_integrity::CommitInput { | ||
| diff: PerspectiveDiff { | ||
| additions: vec![alice_link.clone()], | ||
| removals: vec![], | ||
| }, | ||
| my_did: "did:test:alice".to_string(), | ||
| }, | ||
| ).await; | ||
| println!("Alice committed: {:?}", alice_commit); | ||
|
|
||
| await_consistency(1000).await; | ||
|
|
||
| // Bob tries to pull Alice's commit (should fail - not connected) | ||
| let bob_pull_result = call_zome_fallible::<_, perspective_diff_sync_integrity::PullResult>( | ||
| &conductors[1], | ||
| bob_cell, | ||
| "pull", | ||
| serde_json::json!({ "hash": alice_commit, "is_scribe": false }), | ||
| ).await; | ||
| assert!(bob_pull_result.is_err(), "Bob's pull should fail when disconnected"); | ||
|
|
||
| // Bob commits his own link (creating a fork) | ||
| let bob_link = generate_link_expression("bob"); | ||
| let bob_commit: ActionHash = call_zome( | ||
| &conductors[1], | ||
| bob_cell, | ||
| "commit", | ||
| perspective_diff_sync_integrity::CommitInput { | ||
| diff: PerspectiveDiff { | ||
| additions: vec![bob_link.clone()], | ||
| removals: vec![], | ||
| }, | ||
| my_did: "did:test:bob".to_string(), | ||
| }, | ||
| ).await; | ||
| println!("Bob committed: {:?}", bob_commit); | ||
|
|
||
| // Connect the conductors | ||
| conductors.exchange_peer_info().await; | ||
| await_consistency(10000).await; | ||
|
|
||
| // Alice tries to merge by pulling Bob's commit | ||
| let alice_pull = retry_until_success( | ||
| &conductors[0], | ||
| alice_cell, | ||
| "pull", | ||
| serde_json::json!({ "hash": bob_commit, "is_scribe": true }), | ||
| 5, | ||
| 2000, | ||
| |result: &perspective_diff_sync_integrity::PullResult| { | ||
| result.diff.additions.len() == 1 | ||
| }, | ||
| ).await.expect("Alice's pull should succeed"); | ||
|
|
||
| assert_eq!(alice_pull.diff.additions.len(), 1, "Alice should see Bob's addition"); | ||
| assert_eq!( | ||
| alice_pull.diff.additions[0].data, | ||
| bob_link.data, | ||
| "Alice should see Bob's link data" | ||
| ); | ||
|
|
||
| // Get Alice's new merge commit hash | ||
| let alice_revision: Option<ActionHash> = call_zome( | ||
| &conductors[0], | ||
| alice_cell, | ||
| "current_revision", | ||
| (), | ||
| ).await; | ||
|
|
||
| await_consistency(2000).await; | ||
|
|
||
| // Bob pulls Alice's merge commit | ||
| let bob_pull = retry_until_success( | ||
| &conductors[1], | ||
| bob_cell, | ||
| "pull", | ||
| serde_json::json!({ "hash": alice_revision.unwrap(), "is_scribe": false }), | ||
| 5, | ||
| 2000, | ||
| |result: &perspective_diff_sync_integrity::PullResult| { | ||
| result.diff.additions.len() == 1 | ||
| }, | ||
| ).await.expect("Bob's pull should succeed"); | ||
|
|
||
| assert_eq!(bob_pull.diff.additions.len(), 1, "Bob should see Alice's addition"); | ||
| assert_eq!( | ||
| bob_pull.diff.additions[0].data, | ||
| alice_link.data, | ||
| "Bob should see Alice's link data" | ||
| ); | ||
|
|
||
| // Cleanup | ||
| for conductor in conductors.iter_mut() { | ||
| conductor.shutdown().await; | ||
| } | ||
| } | ||
|
|
||
| /// Test deep merge with multiple commits on both sides | ||
| /// | ||
| /// Scenario: | ||
| /// 1. Alice commits 7 links | ||
| /// 2. Bob commits 7 links (disconnected, creating fork) | ||
| /// 3. They connect | ||
| /// 4. Alice pulls Bob's revision and merges (should see 7 from Bob) | ||
| /// 5. Bob pulls Alice's merged revision and sees 7 from Alice | ||
| #[tokio::test(flavor = "multi_thread")] | ||
| async fn test_merge_fetch_deep() { | ||
| let (mut conductors, cells) = setup_conductors(2, false).await; | ||
| let alice_cell = &cells[0]; | ||
| let bob_cell = &cells[1]; | ||
|
|
||
| // Create DID links for both | ||
| create_did_link(&conductors[0], alice_cell, "did:test:alice").await; | ||
| create_did_link(&conductors[1], bob_cell, "did:test:bob").await; | ||
|
|
||
| // Alice commits 7 links while disconnected | ||
| let mut _alice_commits = Vec::new(); | ||
| for _ in 0..7 { | ||
| let hash = commit_link(&conductors[0], alice_cell, "alice").await; | ||
| _alice_commits.push(hash); | ||
| } | ||
|
|
||
| // Get Alice's current revision | ||
| let alice_rev: Option<ActionHash> = call_zome(&conductors[0], alice_cell, "current_revision", ()).await; | ||
| assert!(alice_rev.is_some(), "Alice should have a revision after commits"); | ||
|
|
||
| // Bob commits 7 links (creating fork) | ||
| for _ in 0..7 { | ||
| commit_link(&conductors[1], bob_cell, "bob").await; | ||
| } | ||
|
|
||
| // Get Bob's current revision | ||
| let bob_rev: Option<ActionHash> = call_zome(&conductors[1], bob_cell, "current_revision", ()).await; | ||
| assert!(bob_rev.is_some(), "Bob should have a revision after commits"); | ||
|
|
||
| // Connect conductors | ||
| conductors.exchange_peer_info().await; | ||
| await_consistency(5000).await; | ||
|
|
||
| // Alice pulls Bob's revision and merges | ||
| let alice_merge = retry_until_success( | ||
| &conductors[0], | ||
| alice_cell, | ||
| "pull", | ||
| serde_json::json!({ "hash": bob_rev.unwrap(), "is_scribe": true }), | ||
| 5, | ||
| 2000, | ||
| |result: &perspective_diff_sync_integrity::PullResult| { | ||
| result.diff.additions.len() == 7 | ||
| }, | ||
| ).await.expect("Alice's merge should succeed"); | ||
| assert_eq!(alice_merge.diff.additions.len(), 7, "Alice should see 7 from Bob"); | ||
|
|
||
| // Get Alice's new merged revision | ||
| let alice_merged_rev: Option<ActionHash> = call_zome(&conductors[0], alice_cell, "current_revision", ()).await; | ||
|
|
||
| await_consistency(2000).await; | ||
|
|
||
| // Bob pulls Alice's merged revision and sees her links | ||
| let bob_final = retry_until_success( | ||
| &conductors[1], | ||
| bob_cell, | ||
| "pull", | ||
| serde_json::json!({ "hash": alice_merged_rev.unwrap(), "is_scribe": false }), | ||
| 5, | ||
| 2000, | ||
| |result: &perspective_diff_sync_integrity::PullResult| { | ||
| result.diff.additions.len() == 7 | ||
| }, | ||
| ).await.expect("Bob's pull should succeed"); | ||
| assert_eq!(bob_final.diff.additions.len(), 7, "Bob should see 7 from Alice"); | ||
|
|
||
| // Cleanup | ||
| for conductor in conductors.iter_mut() { | ||
| conductor.shutdown().await; | ||
| } | ||
| } | ||
|
|
||
| /// Test that large diffs are chunked properly | ||
| /// | ||
| /// This tests the fix for the entry size limit issue | ||
| #[tokio::test(flavor = "multi_thread")] | ||
| async fn test_large_diff_chunking() { | ||
| let (mut conductor, cell) = setup_1_conductor().await; | ||
|
|
||
| // Create DID link | ||
| create_did_link(&conductor, &cell, "did:test:alice").await; | ||
|
|
||
| // Commit 600 links (above the CHUNKING_THRESHOLD of 500) | ||
| let large_input = create_commit_input_multi("alice", 600); | ||
|
|
||
| let commit_result: ActionHash = call_zome( | ||
| &conductor, | ||
| &cell, | ||
| "commit", | ||
| large_input, | ||
| ).await; | ||
|
|
||
| println!("Large commit succeeded: {:?}", commit_result); | ||
|
|
||
| // Verify we can read it back | ||
| let current: Option<ActionHash> = call_zome(&conductor, &cell, "current_revision", ()).await; | ||
| assert!(current.is_some(), "Should have a current revision"); | ||
| assert_eq!(current.unwrap(), commit_result, "Current revision should match commit"); | ||
|
|
||
| conductor.shutdown().await; | ||
| } | ||
|
|
||
| /// Test commit with empty diff | ||
| #[tokio::test(flavor = "multi_thread")] | ||
| async fn test_empty_commit() { | ||
| let (mut conductor, cell) = setup_1_conductor().await; | ||
|
|
||
| create_did_link(&conductor, &cell, "did:test:alice").await; | ||
|
|
||
| // First make a real commit | ||
| commit_link(&conductor, &cell, "alice").await; | ||
|
|
||
| // Get current revision | ||
| let rev1: Option<ActionHash> = call_zome(&conductor, &cell, "current_revision", ()).await; | ||
| assert!(rev1.is_some()); | ||
|
|
||
| conductor.shutdown().await; | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Test name
test_empty_commitis misleading — it never commits an empty diff.The function creates a regular link via
commit_link, then asserts a revision exists. It doesn't test empty-diff behavior. Either rename to reflect actual intent (e.g.,test_single_commit_has_revision) or commit with an empty diff to match the name.🤖 Prompt for AI Agents