package service import ( "sync" "testing" ) func TestProgressCacheFlushAndSnapshot(t *testing.T) { c := NewProgressCache() c.Start(1) c.AppendLog(1, "line1") c.AppendLog(1, "line2") if got := c.Snapshot(1); got != "line1\nline2\n" { t.Errorf("Snapshot = %q, want the full buffer", got) } // First flush returns everything appended so far. if first := c.FlushLogs(1); first != "line1\nline2\n" { t.Errorf("first flush = %q, want full buffer", first) } // With nothing new, a flush returns empty (no DB rewrite of the whole log). if again := c.FlushLogs(1); again != "" { t.Errorf("flush with no new logs = %q, want empty", again) } // A later flush returns only the newly appended tail. c.AppendLog(1, "line3") if tail := c.FlushLogs(1); tail != "line3\n" { t.Errorf("incremental flush = %q, want only the new tail", tail) } if got := c.Snapshot(999); got != "" { t.Errorf("Snapshot of unknown id = %q, want empty", got) } } // TestProgressCacheConcurrentSnapshot exercises the data-race fix: a reader // (Snapshot) and the writer (AppendLog) touch the same strings.Builder, so they // must be serialized by the cache lock. Run with -race to catch a regression. func TestProgressCacheConcurrentSnapshot(t *testing.T) { c := NewProgressCache() c.Start(1) var wg sync.WaitGroup wg.Add(2) go func() { defer wg.Done() for i := 0; i < 2000; i++ { c.AppendLog(1, "log line") } }() go func() { defer wg.Done() for i := 0; i < 2000; i++ { _ = c.Snapshot(1) } }() wg.Wait() }