mirror of
https://github.com/logos-messaging/nim-sds.git
synced 2026-01-04 15:13:08 +00:00
263 lines
8.1 KiB
Go
263 lines
8.1 KiB
Go
package main
|
|
import (
|
|
// "fmt"
|
|
// "sync"
|
|
"testing"
|
|
// "time"
|
|
)
|
|
|
|
// Test basic creation, cleanup, and reset
|
|
func TestLifecycle(t *testing.T) {
|
|
channelID := "test-lifecycle"
|
|
handle, err := NewReliabilityManager(channelID)
|
|
if err != nil {
|
|
t.Fatalf("NewReliabilityManager failed: %v", err)
|
|
}
|
|
if handle == nil {
|
|
t.Fatal("NewReliabilityManager returned a nil handle")
|
|
}
|
|
defer CleanupReliabilityManager(handle) // Ensure cleanup even on test failure
|
|
|
|
err = ResetReliabilityManager(handle)
|
|
if err != nil {
|
|
t.Errorf("ResetReliabilityManager failed: %v", err)
|
|
}
|
|
}
|
|
|
|
// Test wrapping and unwrapping a simple message
|
|
func TestWrapUnwrap(t *testing.T) {
|
|
channelID := "test-wrap-unwrap"
|
|
handle, err := NewReliabilityManager(channelID)
|
|
if err != nil {
|
|
t.Fatalf("NewReliabilityManager failed: %v", err)
|
|
}
|
|
defer CleanupReliabilityManager(handle)
|
|
|
|
originalPayload := []byte("hello reliability")
|
|
messageID := MessageID("msg-wrap-1")
|
|
|
|
wrappedMsg, err := WrapOutgoingMessage(handle, originalPayload, messageID)
|
|
if err != nil {
|
|
t.Fatalf("WrapOutgoingMessage failed: %v", err)
|
|
}
|
|
if len(wrappedMsg) == 0 {
|
|
t.Fatal("WrapOutgoingMessage returned empty bytes")
|
|
}
|
|
|
|
// Simulate receiving the wrapped message
|
|
unwrappedPayload, missingDeps, err := UnwrapReceivedMessage(handle, wrappedMsg)
|
|
if err != nil {
|
|
t.Fatalf("UnwrapReceivedMessage failed: %v", err)
|
|
}
|
|
|
|
if string(unwrappedPayload) != string(originalPayload) {
|
|
t.Errorf("Unwrapped payload mismatch: got %q, want %q", unwrappedPayload, originalPayload)
|
|
}
|
|
if len(missingDeps) != 0 {
|
|
t.Errorf("Expected 0 missing dependencies, got %d: %v", len(missingDeps), missingDeps)
|
|
}
|
|
}
|
|
|
|
// // Test dependency handling
|
|
// func TestDependencies(t *testing.T) {
|
|
// channelID := "test-deps"
|
|
// handle, err := NewReliabilityManager(channelID)
|
|
// if err != nil {
|
|
// t.Fatalf("NewReliabilityManager failed: %v", err)
|
|
// }
|
|
// defer CleanupReliabilityManager(handle)
|
|
|
|
// // 1. Send message 1 (will become a dependency)
|
|
// payload1 := []byte("message one")
|
|
// msgID1 := MessageID("msg-dep-1")
|
|
// wrappedMsg1, err := WrapOutgoingMessage(handle, payload1, msgID1)
|
|
// if err != nil {
|
|
// t.Fatalf("WrapOutgoingMessage (1) failed: %v", err)
|
|
// }
|
|
// // Simulate receiving msg1 to add it to history (implicitly acknowledges it)
|
|
// _, _, err = UnwrapReceivedMessage(handle, wrappedMsg1)
|
|
// if err != nil {
|
|
// t.Fatalf("UnwrapReceivedMessage (1) failed: %v", err)
|
|
// }
|
|
|
|
// // 2. Send message 2 (depends on message 1 implicitly via causal history)
|
|
// payload2 := []byte("message two")
|
|
// msgID2 := MessageID("msg-dep-2")
|
|
// wrappedMsg2, err := WrapOutgoingMessage(handle, payload2, msgID2)
|
|
// if err != nil {
|
|
// t.Fatalf("WrapOutgoingMessage (2) failed: %v", err)
|
|
// }
|
|
|
|
// // 3. Create a new manager to simulate a different peer receiving msg2 without msg1
|
|
// handle2, err := NewReliabilityManager(channelID) // Same channel ID
|
|
// if err != nil {
|
|
// t.Fatalf("NewReliabilityManager (2) failed: %v", err)
|
|
// }
|
|
// defer CleanupReliabilityManager(handle2)
|
|
|
|
// // 4. Unwrap message 2 on the second manager - should report msg1 as missing
|
|
// _, missingDeps, err := UnwrapReceivedMessage(handle2, wrappedMsg2)
|
|
// if err != nil {
|
|
// t.Fatalf("UnwrapReceivedMessage (2) on handle2 failed: %v", err)
|
|
// }
|
|
|
|
// if len(missingDeps) == 0 {
|
|
// t.Fatalf("Expected missing dependencies, got none")
|
|
// }
|
|
// foundDep1 := false
|
|
// for _, dep := range missingDeps {
|
|
// if dep == msgID1 {
|
|
// foundDep1 = true
|
|
// break
|
|
// }
|
|
// }
|
|
// if !foundDep1 {
|
|
// t.Errorf("Expected missing dependency %q, got %v", msgID1, missingDeps)
|
|
// }
|
|
|
|
// // 5. Mark the dependency as met
|
|
// err = MarkDependenciesMet(handle2, []MessageID{msgID1})
|
|
// if err != nil {
|
|
// t.Fatalf("MarkDependenciesMet failed: %v", err)
|
|
// }
|
|
|
|
// // Ideally, we'd check if the message is now moved from an internal buffer,
|
|
// // but the current API doesn't expose buffer state. We rely on callbacks for this.
|
|
// }
|
|
|
|
// // Test callbacks
|
|
// func TestCallbacks(t *testing.T) {
|
|
// channelID := "test-callbacks"
|
|
// handle, err := NewReliabilityManager(channelID)
|
|
// if err != nil {
|
|
// t.Fatalf("NewReliabilityManager failed: %v", err)
|
|
// }
|
|
// defer CleanupReliabilityManager(handle)
|
|
|
|
// var wg sync.WaitGroup
|
|
// receivedReady := make(map[MessageID]bool)
|
|
// receivedSent := make(map[MessageID]bool)
|
|
// receivedMissing := make(map[MessageID][]MessageID)
|
|
// syncRequested := false
|
|
// var cbMutex sync.Mutex // Protect access to callback tracking maps/vars
|
|
|
|
// callbacks := Callbacks{
|
|
// OnMessageReady: func(messageId MessageID) {
|
|
// fmt.Printf("Test: OnMessageReady received: %s\n", messageId)
|
|
// cbMutex.Lock()
|
|
// receivedReady[messageId] = true
|
|
// cbMutex.Unlock()
|
|
// wg.Done()
|
|
// },
|
|
// OnMessageSent: func(messageId MessageID) {
|
|
// fmt.Printf("Test: OnMessageSent received: %s\n", messageId)
|
|
// cbMutex.Lock()
|
|
// receivedSent[messageId] = true
|
|
// cbMutex.Unlock()
|
|
// wg.Done()
|
|
// },
|
|
// OnMissingDependencies: func(messageId MessageID, missingDeps []MessageID) {
|
|
// fmt.Printf("Test: OnMissingDependencies received for %s: %v\n", messageId, missingDeps)
|
|
// cbMutex.Lock()
|
|
// receivedMissing[messageId] = missingDeps
|
|
// cbMutex.Unlock()
|
|
// wg.Done()
|
|
// },
|
|
// OnPeriodicSync: func() {
|
|
// fmt.Println("Test: OnPeriodicSync received")
|
|
// cbMutex.Lock()
|
|
// syncRequested = true
|
|
// cbMutex.Unlock()
|
|
// // Don't wg.Done() here, it might be called multiple times
|
|
// },
|
|
// }
|
|
|
|
// err = RegisterCallbacks(handle, callbacks)
|
|
// if err != nil {
|
|
// t.Fatalf("RegisterCallbacks failed: %v", err)
|
|
// }
|
|
|
|
// // Start tasks AFTER registering callbacks
|
|
// err = StartPeriodicTasks(handle)
|
|
// if err != nil {
|
|
// t.Fatalf("StartPeriodicTasks failed: %v", err)
|
|
// }
|
|
|
|
// // --- Test Scenario ---
|
|
|
|
// // 1. Send msg1
|
|
// wg.Add(1) // Expect OnMessageSent for msg1 eventually
|
|
// payload1 := []byte("callback test 1")
|
|
// msgID1 := MessageID("cb-msg-1")
|
|
// wrappedMsg1, err := WrapOutgoingMessage(handle, payload1, msgID1)
|
|
// if err != nil {
|
|
// t.Fatalf("WrapOutgoingMessage (1) failed: %v", err)
|
|
// }
|
|
|
|
// // 2. Receive msg1 (triggers OnMessageReady for msg1, OnMessageSent for msg1 via causal history)
|
|
// wg.Add(1) // Expect OnMessageReady for msg1
|
|
// _, _, err = UnwrapReceivedMessage(handle, wrappedMsg1)
|
|
// if err != nil {
|
|
// t.Fatalf("UnwrapReceivedMessage (1) failed: %v", err)
|
|
// }
|
|
|
|
// // 3. Send msg2 (depends on msg1)
|
|
// wg.Add(1) // Expect OnMessageSent for msg2 eventually
|
|
// payload2 := []byte("callback test 2")
|
|
// msgID2 := MessageID("cb-msg-2")
|
|
// wrappedMsg2, err := WrapOutgoingMessage(handle, payload2, msgID2)
|
|
// if err != nil {
|
|
// t.Fatalf("WrapOutgoingMessage (2) failed: %v", err)
|
|
// }
|
|
|
|
// // 4. Receive msg2 (triggers OnMessageReady for msg2, OnMessageSent for msg2)
|
|
// wg.Add(1) // Expect OnMessageReady for msg2
|
|
// _, _, err = UnwrapReceivedMessage(handle, wrappedMsg2)
|
|
// if err != nil {
|
|
// t.Fatalf("UnwrapReceivedMessage (2) failed: %v", err)
|
|
// }
|
|
|
|
// // --- Verification ---
|
|
// // Wait for expected callbacks with a timeout
|
|
// waitTimeout(&wg, 5*time.Second, t)
|
|
|
|
// cbMutex.Lock()
|
|
// defer cbMutex.Unlock()
|
|
|
|
// if !receivedReady[msgID1] {
|
|
// t.Errorf("OnMessageReady not called for %s", msgID1)
|
|
// }
|
|
// if !receivedReady[msgID2] {
|
|
// t.Errorf("OnMessageReady not called for %s", msgID2)
|
|
// }
|
|
// if !receivedSent[msgID1] {
|
|
// t.Errorf("OnMessageSent not called for %s", msgID1)
|
|
// }
|
|
// if !receivedSent[msgID2] {
|
|
// t.Errorf("OnMessageSent not called for %s", msgID2)
|
|
// }
|
|
// // We didn't explicitly test missing deps in this path
|
|
// if len(receivedMissing) > 0 {
|
|
// t.Errorf("Unexpected OnMissingDependencies calls: %v", receivedMissing)
|
|
// }
|
|
// // Periodic sync is harder to guarantee in a short test, just check if it was ever true
|
|
// if !syncRequested {
|
|
// t.Logf("Warning: OnPeriodicSync might not have been called within the test timeout")
|
|
// }
|
|
// }
|
|
|
|
// // Helper function to wait for WaitGroup with a timeout
|
|
// func waitTimeout(wg *sync.WaitGroup, timeout time.Duration, t *testing.T) {
|
|
// c := make(chan struct{})
|
|
// go func() {
|
|
// defer close(c)
|
|
// wg.Wait()
|
|
// }()
|
|
// select {
|
|
// case <-c:
|
|
// // Completed normally
|
|
// case <-time.After(timeout):
|
|
// t.Fatalf("Timed out waiting for callbacks")
|
|
// }
|
|
// }
|