package chunker_test import ( "compress/gzip" "io" "os" "path/filepath" "strings" "testing" "workshop.perforce.com/p4lf/internal/chunker" "workshop.perforce.com/p4lf/internal/config" "workshop.perforce.com/p4lf/internal/tailer" ) type nopLogger struct{} func (nopLogger) Infof(string, ...interface{}) {} func (nopLogger) Warnf(string, ...interface{}) {} func (nopLogger) Debugf(string, ...interface{}) {} func (nopLogger) Errorf(string, ...interface{}) {} func testConfig(t *testing.T) config.Config { t.Helper() dir := t.TempDir() cfg := config.Defaults() cfg.LogChunksDir = dir cfg.P4LogFile = "/dev/null" cfg.StateFile = filepath.Join(dir, "p4lf.state") return cfg } func TestWrite_CreatesGzipFile(t *testing.T) { cfg := testConfig(t) c, err := chunker.New(cfg, nopLogger{}) if err != nil { t.Fatalf("New: %v", err) } chunk := &tailer.Chunk{ Data: []byte("line1\nline2\nline3\n"), StartOffset: 0, EndOffset: 18, } ok, err := c.Write(chunk) if err != nil { t.Fatalf("Write: %v", err) } if !ok { t.Fatal("Write returned false (skipped), expected true") } expected := filepath.Join(cfg.LogChunksDir, "log.0.18.gz") if _, err := os.Stat(expected); os.IsNotExist(err) { t.Fatalf("expected chunk file %q not found", expected) } // Verify the gzip content. f, err := os.Open(expected) if err != nil { t.Fatalf("opening chunk: %v", err) } defer f.Close() gz, err := gzip.NewReader(f) if err != nil { t.Fatalf("gzip.NewReader: %v", err) } content, err := io.ReadAll(gz) if err != nil { t.Fatalf("reading gzip content: %v", err) } if string(content) != string(chunk.Data) { t.Errorf("chunk content: got %q, want %q", content, chunk.Data) } } func TestWrite_MaxLogChunks(t *testing.T) { cfg := testConfig(t) cfg.MaxLogChunks = 1 c, err := chunker.New(cfg, nopLogger{}) if err != nil { t.Fatalf("New: %v", err) } chunk1 := &tailer.Chunk{Data: []byte("data"), StartOffset: 0, EndOffset: 4} ok, err := c.Write(chunk1) if err != nil || !ok { t.Fatalf("first Write: err=%v ok=%v", err, ok) } // Second write should be skipped (1 file already present, MaxLogChunks=1). chunk2 := &tailer.Chunk{Data: []byte("more"), StartOffset: 4, EndOffset: 8} ok, err = c.Write(chunk2) if err != nil { t.Fatalf("second Write error: %v", err) } if ok { t.Error("second Write should have been skipped due to MaxLogChunks=1") } } func TestWrite_NoPanicOnEmptyDir(t *testing.T) { cfg := testConfig(t) cfg.MaxLogChunks = 5 c, err := chunker.New(cfg, nopLogger{}) if err != nil { t.Fatalf("New: %v", err) } chunk := &tailer.Chunk{Data: []byte("x"), StartOffset: 100, EndOffset: 101} ok, err := c.Write(chunk) if err != nil { t.Fatalf("Write: %v", err) } if !ok { t.Error("expected write to succeed") } } func TestWrite_ContentIsCorrect(t *testing.T) { cfg := testConfig(t) c, err := chunker.New(cfg, nopLogger{}) if err != nil { t.Fatalf("New: %v", err) } payload := strings.Repeat("Perforce log line\n", 100) chunk := &tailer.Chunk{ Data: []byte(payload), StartOffset: 1000, EndOffset: 1000 + int64(len(payload)), } if _, err := c.Write(chunk); err != nil { t.Fatalf("Write: %v", err) } matches, err := filepath.Glob(filepath.Join(cfg.LogChunksDir, "log.*.gz")) if err != nil || len(matches) == 0 { t.Fatalf("no chunk files found in %s", cfg.LogChunksDir) } f, _ := os.Open(matches[0]) defer f.Close() gz, _ := gzip.NewReader(f) got, _ := io.ReadAll(gz) if string(got) != payload { t.Errorf("content mismatch: got %d bytes, want %d bytes", len(got), len(payload)) } }