package routing_test import ( "context" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "github.com/veylant/ia-gateway/internal/routing" ) func makeRule(name, tenantID string, priority int) routing.RoutingRule { return routing.RoutingRule{ Name: name, TenantID: tenantID, Priority: priority, IsEnabled: true, Conditions: []routing.Condition{}, Action: routing.Action{Provider: "openai"}, } } func TestRuleCache_Get_LoadsFromStore(t *testing.T) { store := routing.NewMemStore() _, err := store.Create(context.Background(), makeRule("r1", "t1", 100)) require.NoError(t, err) cache := routing.NewRuleCache(store, 30*time.Second, zap.NewNop()) rules, err := cache.Get(context.Background(), "t1") require.NoError(t, err) assert.Len(t, rules, 1) assert.Equal(t, "r1", rules[0].Name) } func TestRuleCache_Get_ReturnsCachedOnSecondCall(t *testing.T) { store := routing.NewMemStore() _, err := store.Create(context.Background(), makeRule("r1", "t1", 100)) require.NoError(t, err) cache := routing.NewRuleCache(store, 30*time.Second, zap.NewNop()) // First call loads from store rules1, err := cache.Get(context.Background(), "t1") require.NoError(t, err) // Add another rule to the store — should NOT appear before cache expiry _, err = store.Create(context.Background(), makeRule("r2", "t1", 200)) require.NoError(t, err) rules2, err := cache.Get(context.Background(), "t1") require.NoError(t, err) assert.Equal(t, len(rules1), len(rules2), "cache should serve stale data within TTL") } func TestRuleCache_Invalidate_ForcesReload(t *testing.T) { store := routing.NewMemStore() _, err := store.Create(context.Background(), makeRule("r1", "t1", 100)) require.NoError(t, err) cache := routing.NewRuleCache(store, 30*time.Second, zap.NewNop()) _, err = cache.Get(context.Background(), "t1") require.NoError(t, err) // Add a new rule and invalidate _, err = store.Create(context.Background(), makeRule("r2", "t1", 200)) require.NoError(t, err) cache.Invalidate("t1") rules, err := cache.Get(context.Background(), "t1") require.NoError(t, err) assert.Len(t, rules, 2, "after invalidation, new rule should be visible") } func TestRuleCache_EmptyTenant_ReturnsEmpty(t *testing.T) { store := routing.NewMemStore() cache := routing.NewRuleCache(store, 30*time.Second, zap.NewNop()) rules, err := cache.Get(context.Background(), "unknown-tenant") require.NoError(t, err) assert.Empty(t, rules) } func TestRuleCache_StartStop_NoRace(t *testing.T) { store := routing.NewMemStore() cache := routing.NewRuleCache(store, 10*time.Millisecond, zap.NewNop()) cache.Start() // Perform some gets while refresh loop runs for i := 0; i < 5; i++ { _, _ = cache.Get(context.Background(), "t1") time.Sleep(5 * time.Millisecond) } cache.Stop() }