package integration import ( "context" "testing" "time" "github.com/anthonyrawlins/bzzz/pkg/dht" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestMockDHTBasicOperations(t *testing.T) { mockDHT := dht.NewMockDHT() ctx := context.Background() // Test basic put/get operations key := "test-key" value := []byte("test-value") // Put value err := mockDHT.PutValue(ctx, key, value) assert.NoError(t, err) // Get value retrieved, err := mockDHT.GetValue(ctx, key) assert.NoError(t, err) assert.Equal(t, value, retrieved) // Test non-existent key _, err = mockDHT.GetValue(ctx, "non-existent") assert.Error(t, err) assert.Contains(t, err.Error(), "key not found") } func TestMockDHTProviderAnnouncement(t *testing.T) { mockDHT := dht.NewMockDHT() ctx := context.Background() key := "test-provider-key" // Initially no providers providers, err := mockDHT.FindProviders(ctx, key, 10) assert.NoError(t, err) assert.Empty(t, providers) // Announce as provider err = mockDHT.Provide(ctx, key) assert.NoError(t, err) // Should now find provider providers, err = mockDHT.FindProviders(ctx, key, 10) assert.NoError(t, err) assert.Len(t, providers, 1) assert.Equal(t, "mock-peer-local", providers[0]) } func TestMockDHTNetworkSimulation(t *testing.T) { mockDHT := dht.NewMockDHT() ctx := context.Background() // Test latency simulation mockDHT.SetLatency(50 * time.Millisecond) start := time.Now() err := mockDHT.PutValue(ctx, "latency-test", []byte("data")) elapsed := time.Since(start) assert.NoError(t, err) assert.GreaterOrEqual(t, elapsed, 50*time.Millisecond) // Test failure simulation mockDHT.SetFailureRate(1.0) // Always fail err = mockDHT.PutValue(ctx, "failure-test", []byte("data")) assert.Error(t, err) assert.Contains(t, err.Error(), "mock network failure") // Reset failure rate mockDHT.SetFailureRate(0.0) err = mockDHT.PutValue(ctx, "success-test", []byte("data")) assert.NoError(t, err) } func TestMockDHTContextCancellation(t *testing.T) { mockDHT := dht.NewMockDHT() mockDHT.SetLatency(100 * time.Millisecond) // Create context with short timeout ctx, cancel := context.WithTimeout(context.Background(), 10*time.Millisecond) defer cancel() // Operation should fail due to context cancellation err := mockDHT.PutValue(ctx, "timeout-test", []byte("data")) assert.Error(t, err) assert.Equal(t, context.DeadlineExceeded, err) } func TestMockDHTPeerManagement(t *testing.T) { mockDHT := dht.NewMockDHT() // Add peers mockDHT.AddPeer("peer-1", "192.168.1.1:8080") mockDHT.AddPeer("peer-2", "192.168.1.2:8080") peers := mockDHT.GetPeers() assert.Len(t, peers, 2) assert.Contains(t, peers, "peer-1") assert.Contains(t, peers, "peer-2") // Remove peer mockDHT.RemovePeer("peer-1") peers = mockDHT.GetPeers() assert.Len(t, peers, 1) assert.NotContains(t, peers, "peer-1") } func TestMockDHTStats(t *testing.T) { mockDHT := dht.NewMockDHT() ctx := context.Background() // Initial stats stats := mockDHT.GetStats() assert.Equal(t, 0, stats.TotalKeys) assert.Equal(t, 0, stats.TotalPeers) // Add some data err := mockDHT.PutValue(ctx, "key1", []byte("value1")) require.NoError(t, err) err = mockDHT.PutValue(ctx, "key2", []byte("value2")) require.NoError(t, err) mockDHT.AddPeer("peer1", "addr1") // Check updated stats stats = mockDHT.GetStats() assert.Equal(t, 2, stats.TotalKeys) assert.Equal(t, 1, stats.TotalPeers) } func TestMockDHTConsistencyWithRealInterface(t *testing.T) { // This test ensures MockDHT provides the same interface as real DHT // It should be updated when the real DHT interface changes mockDHT := dht.NewMockDHT() ctx := context.Background() // Test all interface methods exist and work testKey := "interface-test" testValue := []byte("interface-value") // PutValue err := mockDHT.PutValue(ctx, testKey, testValue) assert.NoError(t, err) // GetValue value, err := mockDHT.GetValue(ctx, testKey) assert.NoError(t, err) assert.Equal(t, testValue, value) // Provide err = mockDHT.Provide(ctx, testKey) assert.NoError(t, err) // FindProviders providers, err := mockDHT.FindProviders(ctx, testKey, 10) assert.NoError(t, err) assert.NotEmpty(t, providers) } func TestMockDHTConcurrentAccess(t *testing.T) { mockDHT := dht.NewMockDHT() ctx := context.Background() // Test concurrent access const numGoroutines = 10 const numOperations = 100 // Start concurrent goroutines doing put/get operations done := make(chan bool, numGoroutines) for i := 0; i < numGoroutines; i++ { go func(goroutineID int) { defer func() { done <- true }() for j := 0; j < numOperations; j++ { key := fmt.Sprintf("concurrent-key-%d-%d", goroutineID, j) value := []byte(fmt.Sprintf("concurrent-value-%d-%d", goroutineID, j)) // Put err := mockDHT.PutValue(ctx, key, value) assert.NoError(t, err) // Get retrieved, err := mockDHT.GetValue(ctx, key) assert.NoError(t, err) assert.Equal(t, value, retrieved) } }(i) } // Wait for all goroutines to complete for i := 0; i < numGoroutines; i++ { <-done } // Verify final state stats := mockDHT.GetStats() assert.Equal(t, numGoroutines*numOperations, stats.TotalKeys) } func TestMockDHTClear(t *testing.T) { mockDHT := dht.NewMockDHT() ctx := context.Background() // Add some data err := mockDHT.PutValue(ctx, "key1", []byte("value1")) require.NoError(t, err) mockDHT.AddPeer("peer1", "addr1") stats := mockDHT.GetStats() assert.Greater(t, stats.TotalKeys, 0) assert.Greater(t, stats.TotalPeers, 0) // Clear everything mockDHT.Clear() // Should be empty stats = mockDHT.GetStats() assert.Equal(t, 0, stats.TotalKeys) assert.Equal(t, 0, stats.TotalPeers) // Should not be able to get cleared data _, err = mockDHT.GetValue(ctx, "key1") assert.Error(t, err) }