Skip to main content
Glama
orneryd

M.I.M.I.R - Multi-agent Intelligent Memory & Insight Repository

by orneryd
cooldown_test.go9 kB
package inference import ( "sync" "testing" "time" "github.com/orneryd/nornicdb/pkg/config" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestNewCooldownTable(t *testing.T) { ct := NewCooldownTable() require.NotNil(t, ct) assert.Empty(t, ct.entries) assert.NotEmpty(t, ct.defaults) assert.Equal(t, 5*time.Minute, ct.defaults["relates_to"]) } func TestCooldownTable_CanMaterialize_FeatureDisabled(t *testing.T) { // Ensure feature is disabled cleanup := config.WithCooldownDisabled() defer cleanup() ct := NewCooldownTable() // Record a materialization ct.RecordMaterialization("a", "b", "relates_to") // Should still allow even immediately after assert.True(t, ct.CanMaterialize("a", "b", "relates_to")) } func TestCooldownTable_CanMaterialize_FeatureEnabled(t *testing.T) { // Enable cooldown feature cleanup := config.WithCooldownEnabled() defer cleanup() ct := NewCooldownTable() // First materialization should always be allowed assert.True(t, ct.CanMaterialize("a", "b", "relates_to")) // Record the materialization ct.RecordMaterialization("a", "b", "relates_to") // Immediately after should be blocked assert.False(t, ct.CanMaterialize("a", "b", "relates_to")) // Different pair should be allowed assert.True(t, ct.CanMaterialize("a", "c", "relates_to")) assert.True(t, ct.CanMaterialize("x", "y", "relates_to")) } func TestCooldownTable_CanMaterialize_AfterCooldown(t *testing.T) { cleanup := config.WithCooldownEnabled() defer cleanup() ct := NewCooldownTable() // Set a very short cooldown for testing ct.SetLabelCooldown("test_label", 10*time.Millisecond) // Record materialization ct.RecordMaterialization("a", "b", "test_label") // Should be blocked immediately assert.False(t, ct.CanMaterialize("a", "b", "test_label")) // Wait for cooldown to expire time.Sleep(15 * time.Millisecond) // Now should be allowed assert.True(t, ct.CanMaterialize("a", "b", "test_label")) } func TestCooldownTable_CanMaterializeWithReason(t *testing.T) { cleanup := config.WithCooldownEnabled() defer cleanup() ct := NewCooldownTable() ct.SetLabelCooldown("test_label", 1*time.Second) // First check - should be allowed allowed, reason := ct.CanMaterializeWithReason("a", "b", "test_label") assert.True(t, allowed) assert.Contains(t, reason, "first materialization") // Record and check again ct.RecordMaterialization("a", "b", "test_label") allowed, reason = ct.CanMaterializeWithReason("a", "b", "test_label") assert.False(t, allowed) assert.Contains(t, reason, "cooldown active") } func TestCooldownTable_RecordMaterializationAt(t *testing.T) { cleanup := config.WithCooldownEnabled() defer cleanup() ct := NewCooldownTable() ct.SetLabelCooldown("test_label", 1*time.Hour) // Record at a time in the past (2 hours ago) pastTime := time.Now().Add(-2 * time.Hour) ct.RecordMaterializationAt("a", "b", "test_label", pastTime) // Should be allowed because cooldown has expired assert.True(t, ct.CanMaterialize("a", "b", "test_label")) // Record at current time ct.RecordMaterialization("a", "b", "test_label") // Now should be blocked assert.False(t, ct.CanMaterialize("a", "b", "test_label")) } func TestCooldownTable_TimeUntilAllowed(t *testing.T) { cleanup := config.WithCooldownEnabled() defer cleanup() ct := NewCooldownTable() ct.SetLabelCooldown("test_label", 1*time.Second) // No entry - should return 0 assert.Equal(t, time.Duration(0), ct.TimeUntilAllowed("a", "b", "test_label")) // Record and check ct.RecordMaterialization("a", "b", "test_label") remaining := ct.TimeUntilAllowed("a", "b", "test_label") assert.True(t, remaining > 0 && remaining <= 1*time.Second) } func TestCooldownTable_GetEntry(t *testing.T) { ct := NewCooldownTable() // No entry assert.Nil(t, ct.GetEntry("a", "b", "test")) // After recording ct.RecordMaterialization("a", "b", "test") entry := ct.GetEntry("a", "b", "test") require.NotNil(t, entry) assert.Equal(t, int64(1), entry.Count) assert.False(t, entry.LastMaterialized.IsZero()) // Record again ct.RecordMaterialization("a", "b", "test") entry = ct.GetEntry("a", "b", "test") assert.Equal(t, int64(2), entry.Count) } func TestCooldownTable_Clear(t *testing.T) { ct := NewCooldownTable() ct.RecordMaterialization("a", "b", "test") ct.RecordMaterialization("c", "d", "test") assert.Equal(t, 2, ct.Size()) ct.Clear() assert.Equal(t, 0, ct.Size()) } func TestCooldownTable_Cleanup(t *testing.T) { ct := NewCooldownTable() ct.SetLabelCooldown("test_label", 10*time.Millisecond) // Record some entries ct.RecordMaterializationAt("a", "b", "test_label", time.Now().Add(-1*time.Hour)) ct.RecordMaterializationAt("c", "d", "test_label", time.Now().Add(-1*time.Hour)) ct.RecordMaterialization("e", "f", "test_label") // Recent, should not be cleaned assert.Equal(t, 3, ct.Size()) // Cleanup should remove expired entries removed := ct.Cleanup() assert.Equal(t, 2, removed) assert.Equal(t, 1, ct.Size()) } func TestCooldownTable_Stats(t *testing.T) { cleanup := config.WithCooldownEnabled() defer cleanup() ct := NewCooldownTable() ct.SetLabelCooldown("test", 100*time.Millisecond) // Initial stats stats := ct.Stats() assert.Equal(t, int64(0), stats.TotalEntries) assert.Equal(t, int64(0), stats.TotalChecks) // Make some checks ct.CanMaterialize("a", "b", "test") // Allowed (first) ct.RecordMaterialization("a", "b", "test") ct.CanMaterialize("a", "b", "test") // Blocked stats = ct.Stats() assert.Equal(t, int64(1), stats.TotalEntries) assert.Equal(t, int64(2), stats.TotalChecks) assert.Equal(t, int64(1), stats.TotalAllowed) assert.Equal(t, int64(1), stats.TotalBlocked) assert.Equal(t, 0.5, stats.BlockRate) } func TestCooldownTable_DifferentLabels(t *testing.T) { cleanup := config.WithCooldownEnabled() defer cleanup() ct := NewCooldownTable() ct.SetLabelCooldown("fast", 10*time.Millisecond) ct.SetLabelCooldown("slow", 1*time.Hour) // Record same pair with different labels ct.RecordMaterialization("a", "b", "fast") ct.RecordMaterialization("a", "b", "slow") // Wait for fast cooldown to expire time.Sleep(15 * time.Millisecond) // Fast label should be allowed, slow should not assert.True(t, ct.CanMaterialize("a", "b", "fast")) assert.False(t, ct.CanMaterialize("a", "b", "slow")) } func TestCooldownTable_Concurrent(t *testing.T) { cleanup := config.WithCooldownEnabled() defer cleanup() ct := NewCooldownTable() ct.SetLabelCooldown("test", 100*time.Millisecond) var wg sync.WaitGroup iterations := 100 // Concurrent reads for i := 0; i < iterations; i++ { wg.Add(1) go func() { defer wg.Done() ct.CanMaterialize("a", "b", "test") }() } // Concurrent writes for i := 0; i < iterations; i++ { wg.Add(1) go func() { defer wg.Done() ct.RecordMaterialization("a", "b", "test") }() } wg.Wait() // Should not panic and entry should exist entry := ct.GetEntry("a", "b", "test") require.NotNil(t, entry) assert.True(t, entry.Count > 0) } func TestCooldownTableWithOptions(t *testing.T) { ct := NewCooldownTableWithOptions( WithLabelCooldown("custom", 30*time.Second), WithLabelCooldown("another", 1*time.Minute), ) assert.Equal(t, 30*time.Second, ct.GetLabelCooldown("custom")) assert.Equal(t, 1*time.Minute, ct.GetLabelCooldown("another")) // Default should still work assert.Equal(t, 5*time.Minute, ct.GetLabelCooldown("relates_to")) } func TestGlobalCooldownTable(t *testing.T) { ResetGlobalCooldownTable() ct1 := GlobalCooldownTable() ct2 := GlobalCooldownTable() // Should be same instance assert.Same(t, ct1, ct2) // Reset and get new instance ResetGlobalCooldownTable() ct3 := GlobalCooldownTable() assert.NotSame(t, ct1, ct3) } func TestCooldownTable_DefaultCooldowns(t *testing.T) { ct := NewCooldownTable() // Verify all default cooldowns are set assert.Equal(t, 5*time.Minute, ct.GetLabelCooldown("relates_to")) assert.Equal(t, 10*time.Minute, ct.GetLabelCooldown("similar_to")) assert.Equal(t, 1*time.Minute, ct.GetLabelCooldown("coaccess")) assert.Equal(t, 15*time.Minute, ct.GetLabelCooldown("topology")) assert.Equal(t, 30*time.Minute, ct.GetLabelCooldown("depends_on")) // Unknown label should get default assert.Equal(t, DefaultCooldown, ct.GetLabelCooldown("unknown_label")) } func TestCooldownTable_FeatureDisabledTimeUntilAllowed(t *testing.T) { cleanup := config.WithCooldownDisabled() defer cleanup() ct := NewCooldownTable() ct.RecordMaterialization("a", "b", "test") // Should return 0 when feature is disabled assert.Equal(t, time.Duration(0), ct.TimeUntilAllowed("a", "b", "test")) } func TestCooldownTable_FeatureDisabledReason(t *testing.T) { cleanup := config.WithCooldownDisabled() defer cleanup() ct := NewCooldownTable() ct.RecordMaterialization("a", "b", "test") allowed, reason := ct.CanMaterializeWithReason("a", "b", "test") assert.True(t, allowed) assert.Contains(t, reason, "feature disabled") }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/orneryd/Mimir'

If you have feedback or need assistance with the MCP directory API, please join our Discord server