package queue import ( "testing" "time" "github.com/jfraeys/fetch_ml/internal/queue" ) func TestCommitDedup_IsDuplicate(t *testing.T) { d := queue.NewCommitDedup(5 * time.Minute) // First check should not be duplicate if d.IsDuplicate("job1", "abc123") { t.Error("Expected not duplicate on first check") } // Mark as queued d.MarkQueued("job1", "abc123") // Second check should be duplicate if !d.IsDuplicate("job1", "abc123") { t.Error("Expected duplicate after marking queued") } // Different job with same commit should not be duplicate if d.IsDuplicate("job2", "abc123") { t.Error("Different job with same commit should not be duplicate") } // Same job with different commit should not be duplicate if d.IsDuplicate("job1", "def456") { t.Error("Same job with different commit should not be duplicate") } } func TestCommitDedup_TTL(t *testing.T) { // Use very short TTL for testing d := queue.NewCommitDedup(100 * time.Millisecond) // Mark as queued d.MarkQueued("job1", "abc123") // Should be duplicate immediately if !d.IsDuplicate("job1", "abc123") { t.Error("Expected duplicate immediately after queueing") } // Wait for TTL to expire time.Sleep(150 * time.Millisecond) // Should no longer be duplicate after TTL if d.IsDuplicate("job1", "abc123") { t.Error("Expected not duplicate after TTL expired") } } func TestCommitDedup_Cleanup(t *testing.T) { d := queue.NewCommitDedup(100 * time.Millisecond) // Add several entries d.MarkQueued("job1", "abc123") d.MarkQueued("job2", "def456") d.MarkQueued("job3", "ghi789") // Wait for some to expire time.Sleep(150 * time.Millisecond) // Add one more after expiration d.MarkQueued("job4", "jkl012") // Before cleanup, size should be 4 if d.Size() != 4 { t.Errorf("Expected size 4 before cleanup, got %d", d.Size()) } // Run cleanup d.Cleanup() // After cleanup, size should be 1 (only job4 remains) if d.Size() != 1 { t.Errorf("Expected size 1 after cleanup, got %d", d.Size()) } // job4 should still be tracked if !d.IsDuplicate("job4", "jkl012") { t.Error("job4 should still be tracked after cleanup") } // expired entries should be gone if d.IsDuplicate("job1", "abc123") { t.Error("expired job1 should not be tracked after cleanup") } } func TestCommitDedup_DefaultTTL(t *testing.T) { // Create with zero/negative TTL - should use default (1 hour) d := queue.NewCommitDedup(0) // Mark as queued d.MarkQueued("job1", "abc123") // Should be duplicate immediately if !d.IsDuplicate("job1", "abc123") { t.Error("Expected duplicate with default TTL") } // Size should be 1 if d.Size() != 1 { t.Errorf("Expected size 1, got %d", d.Size()) } } func TestCommitDedup_ConcurrentAccess(t *testing.T) { d := queue.NewCommitDedup(5 * time.Minute) // Concurrent writes for i := 0; i < 100; i++ { go func(n int) { d.MarkQueued("job", string(rune(n))) }(i) } // Concurrent reads for i := 0; i < 100; i++ { go func(n int) { d.IsDuplicate("job", string(rune(n))) }(i) } // Small delay to let goroutines complete time.Sleep(100 * time.Millisecond) // Should not panic and should have some entries if d.Size() == 0 { t.Error("Expected some entries after concurrent access") } } func TestCommitDedup_DifferentJobSameCommit(t *testing.T) { d := queue.NewCommitDedup(5 * time.Minute) // Two different jobs can have the same commit d.MarkQueued("train-model", "abc123") // Different job with same commit should be allowed if d.IsDuplicate("evaluate-model", "abc123") { t.Error("Different jobs should not share commit dedup") } // But same job with same commit should be duplicate if !d.IsDuplicate("train-model", "abc123") { t.Error("Same job+commit should be duplicate") } } func BenchmarkCommitDedup_IsDuplicate(b *testing.B) { d := queue.NewCommitDedup(5 * time.Minute) d.MarkQueued("job", "commit") b.ResetTimer() for i := 0; i < b.N; i++ { d.IsDuplicate("job", "commit") } } func BenchmarkCommitDedup_MarkQueued(b *testing.B) { d := queue.NewCommitDedup(5 * time.Minute) b.ResetTimer() for i := 0; i < b.N; i++ { d.MarkQueued("job", string(rune(i%256))) } }