//go:build e2e package e2e import ( "fmt" "sync" "testing" "time" ) // TestPubSub_SubscribePublish tests basic pub/sub functionality via WebSocket func TestPubSub_SubscribePublish(t *testing.T) { SkipIfMissingGateway(t) topic := GenerateTopic() message := "test-message-from-publisher" // Create subscriber first subscriber, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create subscriber: %v", err) } defer subscriber.Close() // Give subscriber time to register Delay(200) // Create publisher publisher, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create publisher: %v", err) } defer publisher.Close() // Give connections time to stabilize Delay(200) // Publish message if err := publisher.Publish([]byte(message)); err != nil { t.Fatalf("publish failed: %v", err) } // Receive message on subscriber msg, err := subscriber.ReceiveWithTimeout(10 * time.Second) if err != nil { t.Fatalf("receive failed: %v", err) } if string(msg) != message { t.Fatalf("expected message %q, got %q", message, string(msg)) } } // TestPubSub_MultipleSubscribers tests that multiple subscribers receive the same message func TestPubSub_MultipleSubscribers(t *testing.T) { SkipIfMissingGateway(t) topic := GenerateTopic() message1 := "message-1" message2 := "message-2" // Create two subscribers sub1, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create subscriber1: %v", err) } defer sub1.Close() sub2, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create subscriber2: %v", err) } defer sub2.Close() // Give subscribers time to register Delay(200) // Create publisher publisher, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create publisher: %v", err) } defer publisher.Close() // Give connections time to stabilize Delay(200) // Publish first message if err := publisher.Publish([]byte(message1)); err != nil { t.Fatalf("publish1 failed: %v", err) } // Both subscribers should receive first message msg1a, err := sub1.ReceiveWithTimeout(10 * time.Second) if err != nil { t.Fatalf("sub1 receive1 failed: %v", err) } if string(msg1a) != message1 { t.Fatalf("sub1: expected %q, got %q", message1, string(msg1a)) } msg1b, err := sub2.ReceiveWithTimeout(10 * time.Second) if err != nil { t.Fatalf("sub2 receive1 failed: %v", err) } if string(msg1b) != message1 { t.Fatalf("sub2: expected %q, got %q", message1, string(msg1b)) } // Publish second message if err := publisher.Publish([]byte(message2)); err != nil { t.Fatalf("publish2 failed: %v", err) } // Both subscribers should receive second message msg2a, err := sub1.ReceiveWithTimeout(10 * time.Second) if err != nil { t.Fatalf("sub1 receive2 failed: %v", err) } if string(msg2a) != message2 { t.Fatalf("sub1: expected %q, got %q", message2, string(msg2a)) } msg2b, err := sub2.ReceiveWithTimeout(10 * time.Second) if err != nil { t.Fatalf("sub2 receive2 failed: %v", err) } if string(msg2b) != message2 { t.Fatalf("sub2: expected %q, got %q", message2, string(msg2b)) } } // TestPubSub_Deduplication tests that multiple identical messages are all received func TestPubSub_Deduplication(t *testing.T) { SkipIfMissingGateway(t) topic := GenerateTopic() message := "duplicate-test-message" // Create subscriber subscriber, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create subscriber: %v", err) } defer subscriber.Close() // Give subscriber time to register Delay(200) // Create publisher publisher, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create publisher: %v", err) } defer publisher.Close() // Give connections time to stabilize Delay(200) // Publish the same message multiple times for i := 0; i < 3; i++ { if err := publisher.Publish([]byte(message)); err != nil { t.Fatalf("publish %d failed: %v", i, err) } // Small delay between publishes Delay(50) } // Receive messages - should get all (no dedup filter) receivedCount := 0 for receivedCount < 3 { _, err := subscriber.ReceiveWithTimeout(5 * time.Second) if err != nil { break } receivedCount++ } if receivedCount < 1 { t.Fatalf("expected to receive at least 1 message, got %d", receivedCount) } t.Logf("received %d messages", receivedCount) } // TestPubSub_ConcurrentPublish tests concurrent message publishing func TestPubSub_ConcurrentPublish(t *testing.T) { SkipIfMissingGateway(t) topic := GenerateTopic() numMessages := 10 // Create subscriber subscriber, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create subscriber: %v", err) } defer subscriber.Close() // Give subscriber time to register Delay(200) // Create publisher publisher, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create publisher: %v", err) } defer publisher.Close() // Give connections time to stabilize Delay(200) // Publish multiple messages concurrently var wg sync.WaitGroup for i := 0; i < numMessages; i++ { wg.Add(1) go func(idx int) { defer wg.Done() msg := fmt.Sprintf("concurrent-msg-%d", idx) if err := publisher.Publish([]byte(msg)); err != nil { t.Logf("publish %d failed: %v", idx, err) } }(i) } wg.Wait() // Receive messages receivedCount := 0 for receivedCount < numMessages { _, err := subscriber.ReceiveWithTimeout(10 * time.Second) if err != nil { break } receivedCount++ } if receivedCount < numMessages { t.Logf("expected %d messages, got %d (some may have been dropped)", numMessages, receivedCount) } } // TestPubSub_TopicIsolation tests that messages are isolated to their topics func TestPubSub_TopicIsolation(t *testing.T) { SkipIfMissingGateway(t) topic1 := GenerateTopic() topic2 := GenerateTopic() msg1 := "message-on-topic1" msg2 := "message-on-topic2" // Create subscriber for topic1 sub1, err := NewWSPubSubClient(t, topic1) if err != nil { t.Fatalf("failed to create subscriber1: %v", err) } defer sub1.Close() // Create subscriber for topic2 sub2, err := NewWSPubSubClient(t, topic2) if err != nil { t.Fatalf("failed to create subscriber2: %v", err) } defer sub2.Close() // Give subscribers time to register Delay(200) // Create publishers pub1, err := NewWSPubSubClient(t, topic1) if err != nil { t.Fatalf("failed to create publisher1: %v", err) } defer pub1.Close() pub2, err := NewWSPubSubClient(t, topic2) if err != nil { t.Fatalf("failed to create publisher2: %v", err) } defer pub2.Close() // Give connections time to stabilize Delay(200) // Publish to topic2 first if err := pub2.Publish([]byte(msg2)); err != nil { t.Fatalf("publish2 failed: %v", err) } // Publish to topic1 if err := pub1.Publish([]byte(msg1)); err != nil { t.Fatalf("publish1 failed: %v", err) } // Sub1 should receive msg1 only received1, err := sub1.ReceiveWithTimeout(10 * time.Second) if err != nil { t.Fatalf("sub1 receive failed: %v", err) } if string(received1) != msg1 { t.Fatalf("sub1: expected %q, got %q", msg1, string(received1)) } // Sub2 should receive msg2 only received2, err := sub2.ReceiveWithTimeout(10 * time.Second) if err != nil { t.Fatalf("sub2 receive failed: %v", err) } if string(received2) != msg2 { t.Fatalf("sub2: expected %q, got %q", msg2, string(received2)) } } // TestPubSub_EmptyMessage tests sending and receiving empty messages func TestPubSub_EmptyMessage(t *testing.T) { SkipIfMissingGateway(t) topic := GenerateTopic() // Create subscriber subscriber, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create subscriber: %v", err) } defer subscriber.Close() // Give subscriber time to register Delay(200) // Create publisher publisher, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create publisher: %v", err) } defer publisher.Close() // Give connections time to stabilize Delay(200) // Publish empty message if err := publisher.Publish([]byte("")); err != nil { t.Fatalf("publish empty failed: %v", err) } // Receive on subscriber - should get empty message msg, err := subscriber.ReceiveWithTimeout(10 * time.Second) if err != nil { t.Fatalf("receive failed: %v", err) } if len(msg) != 0 { t.Fatalf("expected empty message, got %q", string(msg)) } } // TestPubSub_LargeMessage tests sending and receiving large messages func TestPubSub_LargeMessage(t *testing.T) { SkipIfMissingGateway(t) topic := GenerateTopic() // Create a large message (100KB) largeMessage := make([]byte, 100*1024) for i := range largeMessage { largeMessage[i] = byte(i % 256) } // Create subscriber subscriber, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create subscriber: %v", err) } defer subscriber.Close() // Give subscriber time to register Delay(200) // Create publisher publisher, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create publisher: %v", err) } defer publisher.Close() // Give connections time to stabilize Delay(200) // Publish large message if err := publisher.Publish(largeMessage); err != nil { t.Fatalf("publish large message failed: %v", err) } // Receive on subscriber msg, err := subscriber.ReceiveWithTimeout(30 * time.Second) if err != nil { t.Fatalf("receive failed: %v", err) } if len(msg) != len(largeMessage) { t.Fatalf("expected message of length %d, got %d", len(largeMessage), len(msg)) } // Verify content for i := range msg { if msg[i] != largeMessage[i] { t.Fatalf("message content mismatch at byte %d", i) } } } // TestPubSub_RapidPublish tests rapid message publishing func TestPubSub_RapidPublish(t *testing.T) { SkipIfMissingGateway(t) topic := GenerateTopic() numMessages := 50 // Create subscriber subscriber, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create subscriber: %v", err) } defer subscriber.Close() // Give subscriber time to register Delay(200) // Create publisher publisher, err := NewWSPubSubClient(t, topic) if err != nil { t.Fatalf("failed to create publisher: %v", err) } defer publisher.Close() // Give connections time to stabilize Delay(200) // Publish messages rapidly for i := 0; i < numMessages; i++ { msg := fmt.Sprintf("rapid-msg-%d", i) if err := publisher.Publish([]byte(msg)); err != nil { t.Fatalf("publish %d failed: %v", i, err) } } // Receive messages receivedCount := 0 for receivedCount < numMessages { _, err := subscriber.ReceiveWithTimeout(10 * time.Second) if err != nil { break } receivedCount++ } // Allow some message loss due to buffering minExpected := numMessages * 80 / 100 // 80% minimum if receivedCount < minExpected { t.Fatalf("expected at least %d messages, got %d", minExpected, receivedCount) } t.Logf("received %d/%d messages (%.1f%%)", receivedCount, numMessages, float64(receivedCount)*100/float64(numMessages)) }