From 9ea19a3532ba7f99ce51ceb8f3e5cb8a5b2698ed Mon Sep 17 00:00:00 2001 From: Kalzu Rekku Date: Thu, 11 Sep 2025 07:46:57 +0300 Subject: [PATCH] Updated integration tests script for the merkel tree implementation. --- integration_test.sh | 119 ++++++++++++++++++++++++++++++-------------- 1 file changed, 83 insertions(+), 36 deletions(-) diff --git a/integration_test.sh b/integration_test.sh index 464087f..22305cf 100755 --- a/integration_test.sh +++ b/integration_test.sh @@ -1,7 +1,7 @@ #!/bin/bash -# KVS Integration Test Suite - Working Version -# Tests all critical features of the distributed key-value store +# KVS Integration Test Suite - Adapted for Merkle Tree Sync +# Tests all critical features of the distributed key-value store with Merkle Tree replication # Colors for output RED='\033[0;31m' @@ -43,7 +43,7 @@ test_start() { # Cleanup function cleanup() { log_info "Cleaning up test environment..." - pkill -f "./kvs" 2>/dev/null || true + pkill -f "$BINARY" 2>/dev/null || true rm -rf "$TEST_DIR" 2>/dev/null || true sleep 2 # Allow processes to fully terminate } @@ -75,6 +75,7 @@ test_build() { log_error "Binary build failed" return 1 fi + # Ensure we are back in TEST_DIR for subsequent tests cd "$TEST_DIR" } @@ -103,12 +104,12 @@ EOF -d '{"message":"hello world"}') local get_result=$(curl -s http://localhost:8090/kv/test/basic) - local message=$(echo "$get_result" | jq -r '.message' 2>/dev/null) + local message=$(echo "$get_result" | jq -r '.data.message' 2>/dev/null) # Adjusted jq path if [ "$message" = "hello world" ]; then log_success "Basic CRUD operations work" else - log_error "Basic CRUD failed: $get_result" + log_error "Basic CRUD failed: Expected 'hello world', got '$message' from $get_result" fi else log_error "Basic test node failed to start" @@ -120,7 +121,7 @@ EOF # Test 3: Cluster formation test_cluster_formation() { - test_start "2-node cluster formation" + test_start "2-node cluster formation and Merkle Tree replication" # Node 1 config cat > cluster1.yaml </dev/null 2>&1 & local pid2=$! @@ -168,28 +169,46 @@ EOF return 1 fi - # Wait for cluster formation - sleep 8 + # Wait for cluster formation and initial Merkle sync + sleep 15 # Check if nodes see each other local node1_members=$(curl -s http://localhost:8101/members/ | jq length 2>/dev/null || echo 0) local node2_members=$(curl -s http://localhost:8102/members/ | jq length 2>/dev/null || echo 0) if [ "$node1_members" -ge 1 ] && [ "$node2_members" -ge 1 ]; then - log_success "2-node cluster formed successfully" + log_success "2-node cluster formed successfully (N1 members: $node1_members, N2 members: $node2_members)" # Test data replication + log_info "Putting data on Node 1, waiting for Merkle sync..." curl -s -X PUT http://localhost:8101/kv/cluster/test \ -H "Content-Type: application/json" \ - -d '{"source":"node1"}' >/dev/null + -d '{"source":"node1", "value": 1}' >/dev/null - sleep 12 # Wait for sync cycle + # Wait for Merkle sync cycle to complete + sleep 12 - local node2_data=$(curl -s http://localhost:8102/kv/cluster/test | jq -r '.source' 2>/dev/null) - if [ "$node2_data" = "node1" ]; then - log_success "Data replication works correctly" + local node2_data_full=$(curl -s http://localhost:8102/kv/cluster/test) + local node2_data_source=$(echo "$node2_data_full" | jq -r '.data.source' 2>/dev/null) + local node2_data_value=$(echo "$node2_data_full" | jq -r '.data.value' 2>/dev/null) + local node1_data_full=$(curl -s http://localhost:8101/kv/cluster/test) + + if [ "$node2_data_source" = "node1" ] && [ "$node2_data_value" = "1" ]; then + log_success "Data replication works correctly (Node 2 has data from Node 1)" + + # Verify UUIDs and Timestamps are identical (crucial for Merkle sync correctness) + local node1_uuid=$(echo "$node1_data_full" | jq -r '.uuid' 2>/dev/null) + local node1_timestamp=$(echo "$node1_data_full" | jq -r '.timestamp' 2>/dev/null) + local node2_uuid=$(echo "$node2_data_full" | jq -r '.uuid' 2>/dev/null) + local node2_timestamp=$(echo "$node2_data_full" | jq -r '.timestamp' 2>/dev/null) + + if [ "$node1_uuid" = "$node2_uuid" ] && [ "$node1_timestamp" = "$node2_timestamp" ]; then + log_success "Replicated data retains original UUID and Timestamp" + else + log_error "Replicated data changed UUID/Timestamp: N1_UUID=$node1_uuid, N1_TS=$node1_timestamp, N2_UUID=$node2_uuid, N2_TS=$node2_timestamp" + fi else - log_error "Data replication failed: $node2_data" + log_error "Data replication failed: Node 2 data: $node2_data_full" fi else log_error "Cluster formation failed (N1 members: $node1_members, N2 members: $node2_members)" @@ -199,9 +218,12 @@ EOF sleep 2 } -# Test 4: Conflict resolution (simplified) +# Test 4: Conflict resolution (Merkle Tree based) +# This test assumes 'test_conflict.go' creates two BadgerDBs with a key +# that has the same path and timestamp but different UUIDs, or different timestamps +# but same path. The Merkle tree sync should then trigger conflict resolution. test_conflict_resolution() { - test_start "Conflict resolution test" + test_start "Conflict resolution test (Merkle Tree based)" # Create conflicting data using our utility rm -rf conflict1_data conflict2_data 2>/dev/null || true @@ -233,7 +255,8 @@ sync_interval: 8 EOF # Start nodes - $BINARY conflict1.yaml >conflict1.log 2>&1 & + # Node 1 started first, making it "older" for tie-breaker if timestamps are equal + "$BINARY" conflict1.yaml >conflict1.log 2>&1 & local pid1=$! if wait_for_service 8111; then @@ -242,26 +265,50 @@ EOF local pid2=$! if wait_for_service 8112; then - # Get initial data - local node1_initial=$(curl -s http://localhost:8111/kv/test/conflict/data | jq -r '.message' 2>/dev/null) - local node2_initial=$(curl -s http://localhost:8112/kv/test/conflict/data | jq -r '.message' 2>/dev/null) + # Get initial data (full StoredValue) + local node1_initial_full=$(curl -s http://localhost:8111/kv/test/conflict/data) + local node2_initial_full=$(curl -s http://localhost:8112/kv/test/conflict/data) - # Wait for conflict resolution - sleep 12 + local node1_initial_msg=$(echo "$node1_initial_full" | jq -r '.data.message' 2>/dev/null) + local node2_initial_msg=$(echo "$node2_initial_full" | jq -r '.data.message' 2>/dev/null) - # Get final data - local node1_final=$(curl -s http://localhost:8111/kv/test/conflict/data | jq -r '.message' 2>/dev/null) - local node2_final=$(curl -s http://localhost:8112/kv/test/conflict/data | jq -r '.message' 2>/dev/null) + log_info "Initial conflict state: Node1='$node1_initial_msg', Node2='$node2_initial_msg'" + + # Wait for conflict resolution (multiple sync cycles might be needed) + sleep 20 + + # Get final data (full StoredValue) + local node1_final_full=$(curl -s http://localhost:8111/kv/test/conflict/data) + local node2_final_full=$(curl -s http://localhost:8112/kv/test/conflict/data) + + local node1_final_msg=$(echo "$node1_final_full" | jq -r '.data.message' 2>/dev/null) + local node2_final_msg=$(echo "$node2_final_full" | jq -r '.data.message' 2>/dev/null) # Check if they converged - if [ "$node1_final" = "$node2_final" ] && [ -n "$node1_final" ]; then - if grep -q "conflict resolution" conflict1.log conflict2.log 2>/dev/null; then - log_success "Conflict resolution detected and resolved ($node1_initial vs $node2_initial → $node1_final)" + if [ "$node1_final_msg" = "$node2_final_msg" ] && [ -n "$node1_final_msg" ]; then + log_success "Conflict resolution converged to: '$node1_final_msg'" + + # Verify UUIDs and Timestamps are identical after resolution + local node1_final_uuid=$(echo "$node1_final_full" | jq -r '.uuid' 2>/dev/null) + local node1_final_timestamp=$(echo "$node1_final_full" | jq -r '.timestamp' 2>/dev/null) + local node2_final_uuid=$(echo "$node2_final_full" | jq -r '.uuid' 2>/dev/null) + local node2_final_timestamp=$(echo "$node2_final_full" | jq -r '.timestamp' 2>/dev/null) + + if [ "$node1_final_uuid" = "$node2_final_uuid" ] && [ "$node1_final_timestamp" = "$node2_final_timestamp" ]; then + log_success "Resolved data retains consistent UUID and Timestamp across nodes" else - log_success "Nodes converged without conflicts ($node1_final)" + log_error "Resolved data has inconsistent UUID/Timestamp: N1_UUID=$node1_final_uuid, N1_TS=$node1_final_timestamp, N2_UUID=$node2_final_uuid, N2_TS=$node2_final_timestamp" fi + + # Optionally, check logs for conflict resolution messages + if grep -q "Conflict resolved" conflict1.log conflict2.log 2>/dev/null; then + log_success "Conflict resolution messages found in logs" + else + log_error "No 'Conflict resolved' messages found in logs, but data converged." + fi + else - log_error "Conflict resolution failed: N1='$node1_final', N2='$node2_final'" + log_error "Conflict resolution failed: N1_final='$node1_final_msg', N2_final='$node2_final_msg'" fi else log_error "Conflict node 2 failed to start" @@ -276,14 +323,14 @@ EOF sleep 2 else cd "$TEST_DIR" - log_error "Failed to create conflict test data" + log_error "Failed to create conflict test data. Ensure test_conflict.go is correct." fi } # Main test execution main() { echo "==================================================" - echo " KVS Integration Test Suite" + echo " KVS Integration Test Suite (Merkle Tree)" echo "==================================================" # Setup @@ -308,7 +355,7 @@ main() { echo "==================================================" if [ $TESTS_FAILED -eq 0 ]; then - echo -e "${GREEN}🎉 All tests passed! KVS is working correctly.${NC}" + echo -e "${GREEN}🎉 All tests passed! KVS with Merkle Tree sync is working correctly.${NC}" cleanup exit 0 else @@ -322,4 +369,4 @@ main() { trap cleanup INT TERM # Run tests -main "$@" \ No newline at end of file +main "$@"