package wecom import ( "sync" "testing" ) func TestMessageDeduplicator_DuplicateDetection(t *testing.T) { d := NewMessageDeduplicator(wecomMaxProcessedMessages) if ok := d.MarkMessageProcessed("msg-1"); !ok { t.Fatalf("first message should be accepted") } if ok := d.MarkMessageProcessed("msg-1"); ok { t.Fatalf("duplicate message should be rejected") } } func TestMessageDeduplicator_ConcurrentSameMessage(t *testing.T) { d := NewMessageDeduplicator(wecomMaxProcessedMessages) const goroutines = 64 var wg sync.WaitGroup wg.Add(goroutines) results := make(chan bool, goroutines) for i := 0; i < goroutines; i++ { go func() { defer wg.Done() results <- d.MarkMessageProcessed("msg-concurrent") }() } wg.Wait() close(results) successes := 0 for ok := range results { if ok { successes++ } } if successes != 1 { t.Fatalf("expected exactly 1 successful mark, got %d", successes) } } func TestMessageDeduplicator_CircularQueueEviction(t *testing.T) { // Create a deduplicator with a very small capacity to test eviction easily. capacity := 3 d := NewMessageDeduplicator(capacity) // Fill the queue. d.MarkMessageProcessed("msg-1") d.MarkMessageProcessed("msg-2") d.MarkMessageProcessed("msg-3") // At this point, the queue is full. msg-1 is the oldest. if len(d.msgs) != 3 { t.Fatalf("expected map size to be 3, got %d", len(d.msgs)) } // This should evict msg-1 and add msg-4. if ok := d.MarkMessageProcessed("msg-4"); !ok { t.Fatalf("msg-4 should be accepted") } if len(d.msgs) != 3 { t.Fatalf("expected map size to remain at max capacity (3), got %d", len(d.msgs)) } // msg-1 should now be forgotten (evicted). if ok := d.MarkMessageProcessed("msg-1"); !ok { t.Fatalf("msg-1 should be accepted again because it was evicted") } // msg-2 should have been evicted when we added msg-1 back. if ok := d.MarkMessageProcessed("msg-2"); !ok { t.Fatalf("msg-2 should be accepted again because it was evicted") } }