package apperr import ( "context" "testing" "time" "github.com/carlosframework/platform/internal/release" ) func TestReadWindowMergesAndFilters(t *testing.T) { ctx := context.Background() s := release.NewDirStore(t.TempDir()) now := time.Date(2026, 9, 7, 10, 30, 0, 0, time.UTC) put := func(box string, recs []Record) { body, err := EncodeChunk(recs) if err != nil { t.Fatal(err) } if _, err := s.Put(ctx, must(t)(ChunkKey("bab", "web", box, HourOf(recs[0].TS), recs[0].Seq, len(body))), body, ""); err != nil { t.Fatal(err) } } put("box1", []Record{ {TS: now, Box: "box1", Host: "h1", Source: SourceEdge, Name: "a", FP: "fa", Count: 3, First: now.Add(-4 * time.Minute), Last: now, Seq: 1}, {TS: now, Box: "box1", Host: "h1", Source: SourceServer, Name: "b", FP: "fb", Count: 1, First: now, Last: now, Seq: 2}, }) put("box2", []Record{ {TS: now.Add(-3 * time.Hour), Box: "box2", Host: "h2", Source: SourceEdge, Name: "old", FP: "fo", Count: 1, First: now.Add(-3 * time.Hour), Last: now.Add(-3 * time.Hour), Seq: 1}, }) recs, trimmed, err := ReadWindow(ctx, s, "bab", "web", Filter{Since: now.Add(-time.Hour)}, 100) if err != nil || trimmed { t.Fatalf("read: %v %v", err, trimmed) } if len(recs) != 2 || recs[0].FP != "fa" || recs[1].FP != "fb" { t.Fatalf("window = %+v", recs) } // A record whose [First, Last] overlaps the window is in, whole. recs, _, _ = ReadWindow(ctx, s, "bab", "web", Filter{Since: now.Add(-2 * time.Minute), Until: now.Add(-time.Minute)}, 100) if len(recs) != 1 || recs[0].FP != "fa" || recs[0].Count != 3 { t.Fatalf("overlap = %+v", recs) } recs, _, _ = ReadWindow(ctx, s, "bab", "web", Filter{Since: now.Add(-time.Hour), Source: SourceServer}, 100) if len(recs) != 1 || recs[0].FP != "fb" { t.Fatalf("source filter = %+v", recs) } recs, _, _ = ReadWindow(ctx, s, "bab", "web", Filter{Since: now.Add(-4 * time.Hour), Host: "h2"}, 100) if len(recs) != 1 || recs[0].FP != "fo" { t.Fatalf("host filter = %+v", recs) } } // TestReadWindowListsHourBeforeSince: a record's chunk sits under the hour // of its FIRST occurrence, which can be well before the window if the group // is still open. Since 11:00 must still find a chunk filed under hour 10 — // the listing is widened by one hour behind Since — while a record in that // same chunk whose Last never reaches the window stays excluded by Match. func TestReadWindowListsHourBeforeSince(t *testing.T) { ctx := context.Background() s := release.NewDirStore(t.TempDir()) base := time.Date(2026, 9, 7, 10, 50, 0, 0, time.UTC) // hour dir "…-10" recs := []Record{ // Opened at 10:50, still being added to at 11:05: inside the // [Since,Until] window by overlap, but filed under hour 10. {TS: base, Box: "box1", Host: "h1", Source: SourceEdge, Name: "long", FP: "flong", Count: 5, First: base, Last: base.Add(15 * time.Minute), Seq: 1}, // Opened and closed at 10:50-10:55, entirely before the window. {TS: base, Box: "box1", Host: "h1", Source: SourceEdge, Name: "gone", FP: "fgone", Count: 1, First: base, Last: base.Add(5 * time.Minute), Seq: 2}, } body, err := EncodeChunk(recs) if err != nil { t.Fatal(err) } if _, err := s.Put(ctx, must(t)(ChunkKey("bab", "web", "box1", HourOf(recs[0].TS), recs[0].Seq, len(body))), body, ""); err != nil { t.Fatal(err) } since := time.Date(2026, 9, 7, 11, 0, 0, 0, time.UTC) until := time.Date(2026, 9, 7, 11, 30, 0, 0, time.UTC) got, trimmed, err := ReadWindow(ctx, s, "bab", "web", Filter{Since: since, Until: until}, 100) if err != nil || trimmed { t.Fatalf("read: %v %v", err, trimmed) } if len(got) != 1 || got[0].FP != "flong" { t.Fatalf("window = %+v, want only flong", got) } } // must unwraps a key builder's (key, error) for fixtures whose names are // known good: must(t)(ChunkKey(...)). func must(t testing.TB) func(string, error) string { return func(k string, err error) string { t.Helper() if err != nil { t.Fatal(err) } return k } }