package modes import ( "context" "os" "path/filepath" "testing" "time" "github.com/bnema/zut/packages/tui" ) func writeWatchedTheme(t *testing.T, home, body string) string { t.Helper() path := filepath.Join(home, "themes", "live.json") if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(path, []byte(body), 0o644); err != nil { t.Fatal(err) } return path } func TestWatchThemeSourceTicksPublishesOnlyValidRevisions(t *testing.T) { home := t.TempDir() path := writeWatchedTheme(t, home, `{"colors":{"accent":20}}`) accepted, err := tui.LoadThemeSource(home, "live") if err != nil { t.Fatal(err) } ctx, cancel := context.WithCancel(context.Background()) defer cancel() ticks := make(chan time.Time) events := make(chan themeWatchEvent, 3) go watchThemeSourceTicks(ctx, home, "live", accepted, ticks, events) if err := os.WriteFile(path, []byte(`{"colors":{"accent":21}}`), 0o644); err != nil { t.Fatal(err) } ticks <- time.Unix(1, 0) select { case event := <-events: if event.source == nil || event.source.File.Colors.Base.Accent == nil || event.source.File.Colors.Base.Accent.Index != 21 { t.Fatalf("valid revision event = %#v", event) } case <-time.After(time.Second): t.Fatal("watcher did not publish valid revision") } if err := os.WriteFile(path, []byte(`{"colors":{"accent":999}}`), 0o644); err != nil { t.Fatal(err) } ticks <- time.Unix(2, 0) select { case event := <-events: if event.err == nil || event.source != nil { t.Fatalf("invalid revision event = %#v", event) } case <-time.After(time.Second): t.Fatal("watcher did not report invalid revision") } } func TestWatchThemeSourceTicksRestartsDeletionGraceAfterInvalidReappearance(t *testing.T) { home := t.TempDir() path := writeWatchedTheme(t, home, `{}`) accepted, err := tui.LoadThemeSource(home, "live") if err != nil { t.Fatal(err) } ctx, cancel := context.WithCancel(context.Background()) defer cancel() ticks := make(chan time.Time) events := make(chan themeWatchEvent, 1) go watchThemeSourceTicks(ctx, home, "live", accepted, ticks, events) if err := os.Remove(path); err != nil { t.Fatal(err) } ticks <- time.Unix(1, 0) if err := os.WriteFile(path, []byte(`{"colors":{"accent":999}}`), 0o644); err != nil { t.Fatal(err) } ticks <- time.Unix(2, 0) select { case event := <-events: if event.err == nil { t.Fatalf("invalid reappearance event = %#v", event) } case <-time.After(time.Second): t.Fatal("watcher did not report invalid reappearance") } if err := os.Remove(path); err != nil { t.Fatal(err) } ticks <- time.Unix(3, 0) ticks <- time.Unix(4, 0) select { case event := <-events: t.Fatalf("deletion happened before restarted grace period: %#v", event) default: } ticks <- time.Unix(6, 0) select { case event := <-events: if !event.deleted { t.Fatalf("persistent deletion event = %#v", event) } case <-time.After(time.Second): t.Fatal("watcher did not publish persistent deletion") } }