package cfgservice import ( "context" "testing" "time" ) // Akzeptanzkriterium 1 + Pruefung 1: Tenant-Override hat Vorrang vor // Global-Default, automatisiert getestet. func TestService_TenantOverrideTakesPrecedenceOverGlobal(t *testing.T) { store, cleanup := setupStoreTest(t) defer cleanup() ctx := context.Background() if _, err := store.Set(ctx, "test_precedence_key", GlobalScope, "global-wert"); err != nil { t.Fatalf("set global: %v", err) } if _, err := store.Set(ctx, "test_precedence_key", "test_acme", "tenant-wert"); err != nil { t.Fatalf("set tenant: %v", err) } svc := NewService(store, time.Hour) got, err := svc.Resolve(ctx, "test_acme", "test_precedence_key") if err != nil { t.Fatalf("resolve mit override: %v", err) } if got.Value != "tenant-wert" { t.Fatalf("erwartet tenant-override, habe %q", got.Value) } gotOther, err := svc.Resolve(ctx, "test_anderer_tenant", "test_precedence_key") if err != nil { t.Fatalf("resolve ohne override: %v", err) } if gotOther.Value != "global-wert" { t.Fatalf("erwartet global-default fuer tenant ohne override, habe %q", gotOther.Value) } } // Akzeptanzkriterium 2 + Pruefung 2: Cache-Invalidierung nach // Konfigurationsaenderung innerhalb dokumentierter Zeit gemessen. func TestService_CacheInvalidationTiming(t *testing.T) { store, cleanup := setupStoreTest(t) defer cleanup() ctx := context.Background() const ttl = 150 * time.Millisecond if _, err := store.Set(ctx, "test_ttl_key", GlobalScope, "alt"); err != nil { t.Fatalf("set: %v", err) } svc := NewService(store, ttl) v, err := svc.Resolve(ctx, "", "test_ttl_key") if err != nil { t.Fatalf("resolve: %v", err) } if v.Value != "alt" { t.Fatalf("erwartet 'alt', habe %q", v.Value) } changedAt := time.Now() if _, err := store.Set(ctx, "test_ttl_key", GlobalScope, "neu"); err != nil { t.Fatalf("set: %v", err) } v, err = svc.Resolve(ctx, "", "test_ttl_key") if err != nil { t.Fatalf("resolve direkt nach aenderung: %v", err) } if v.Value != "alt" { t.Fatalf("cache haette den alten wert liefern sollen, habe %q", v.Value) } deadline := changedAt.Add(ttl + 100*time.Millisecond) for time.Now().Before(deadline) { v, err := svc.Resolve(ctx, "", "test_ttl_key") if err != nil { t.Fatalf("resolve: %v", err) } if v.Value == "neu" { t.Logf("aenderung wurde nach %s wirksam (ziel: innerhalb %s + toleranz)", time.Since(changedAt), ttl) return } time.Sleep(10 * time.Millisecond) } t.Fatalf("aenderung wurde nicht innerhalb von %s wirksam", deadline.Sub(changedAt)) } func TestService_InvalidateForcesImmediateRefresh(t *testing.T) { store, cleanup := setupStoreTest(t) defer cleanup() ctx := context.Background() if _, err := store.Set(ctx, "test_invalidate_key", GlobalScope, "alt"); err != nil { t.Fatalf("set: %v", err) } svc := NewService(store, time.Hour) _, _ = svc.Resolve(ctx, "", "test_invalidate_key") if _, err := store.Set(ctx, "test_invalidate_key", GlobalScope, "neu"); err != nil { t.Fatalf("set: %v", err) } svc.Invalidate("test_invalidate_key", "") v, err := svc.Resolve(ctx, "", "test_invalidate_key") if err != nil { t.Fatalf("resolve: %v", err) } if v.Value != "neu" { t.Fatalf("erwartet sofort sichtbaren neuen wert nach Invalidate, habe %q", v.Value) } }