package data import ( "os" "path/filepath" "sync" "testing" "time" "kra/internal/config" ) func TestConfigWatcherStopWaitsForDebouncedReload(t *testing.T) { root := t.TempDir() configPath := filepath.Join(root, "config.yaml") config := []byte("data:\n database:\n driver: sqlite\n path: " + filepath.ToSlash(root) + "\nadmin: {}\n") if err := os.WriteFile(configPath, config, 0o600); err != nil { t.Fatal(err) } runtime := config.NewStore(&config.Config{Data: &config.Data{}, Admin: &config.Admin{ConfigPath: configPath}}) started := make(chan struct{}) release := make(chan struct{}) var startOnce sync.Once stopListener := runtime.Subscribe(func(*config.Data, *config.Admin) { startOnce.Do(func() { close(started) }) <-release }) defer stopListener() data := &Data{runtime: runtime} stop := data.watchConfig() // A write event schedules the 100ms debounce reload. if err := os.WriteFile(configPath, config, 0o600); err != nil { t.Fatal(err) } select { case <-started: case <-time.After(3 * time.Second): stop() t.Fatal("debounced reload did not start") } stopped := make(chan struct{}) go func() { stop() close(stopped) }() select { case <-stopped: t.Fatal("watcher stop returned before reload callback finished") case <-time.After(100 * time.Millisecond): } close(release) select { case <-stopped: case <-time.After(time.Second): t.Fatal("watcher stop did not wait for callback completion") } }