carlos / platform Public

Clone
git clone https://amadan.net/carlos/platform

Plain git — no account needed to clone.

Download

Download this file

1package apperr
2
3import (
4 "context"
5 "testing"
6 "time"
7
8 "github.com/carlosframework/platform/internal/release"
9)
10
11func TestReadWindowMergesAndFilters(t *testing.T) {
12 ctx := context.Background()
13 s := release.NewDirStore(t.TempDir())
14 now := time.Date(2026, 9, 7, 10, 30, 0, 0, time.UTC)
15 put := func(box string, recs []Record) {
16 body, err := EncodeChunk(recs)
17 if err != nil {
18 t.Fatal(err)
19 }
20 if _, err := s.Put(ctx, must(t)(ChunkKey("bab", "web", box, HourOf(recs[0].TS), recs[0].Seq, len(body))), body, ""); err != nil {
21 t.Fatal(err)
22 }
23 }
24 put("box1", []Record{
25 {TS: now, Box: "box1", Host: "h1", Source: SourceEdge, Name: "a", FP: "fa", Count: 3, First: now.Add(-4 * time.Minute), Last: now, Seq: 1},
26 {TS: now, Box: "box1", Host: "h1", Source: SourceServer, Name: "b", FP: "fb", Count: 1, First: now, Last: now, Seq: 2},
27 })
28 put("box2", []Record{
29 {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},
30 })
31 recs, trimmed, err := ReadWindow(ctx, s, "bab", "web", Filter{Since: now.Add(-time.Hour)}, 100)
32 if err != nil || trimmed {
33 t.Fatalf("read: %v %v", err, trimmed)
34 }
35 if len(recs) != 2 || recs[0].FP != "fa" || recs[1].FP != "fb" {
36 t.Fatalf("window = %+v", recs)
37 }
38 // A record whose [First, Last] overlaps the window is in, whole.
39 recs, _, _ = ReadWindow(ctx, s, "bab", "web", Filter{Since: now.Add(-2 * time.Minute), Until: now.Add(-time.Minute)}, 100)
40 if len(recs) != 1 || recs[0].FP != "fa" || recs[0].Count != 3 {
41 t.Fatalf("overlap = %+v", recs)
42 }
43 recs, _, _ = ReadWindow(ctx, s, "bab", "web", Filter{Since: now.Add(-time.Hour), Source: SourceServer}, 100)
44 if len(recs) != 1 || recs[0].FP != "fb" {
45 t.Fatalf("source filter = %+v", recs)
46 }
47 recs, _, _ = ReadWindow(ctx, s, "bab", "web", Filter{Since: now.Add(-4 * time.Hour), Host: "h2"}, 100)
48 if len(recs) != 1 || recs[0].FP != "fo" {
49 t.Fatalf("host filter = %+v", recs)
50 }
51}
52
53// TestReadWindowListsHourBeforeSince: a record's chunk sits under the hour
54// of its FIRST occurrence, which can be well before the window if the group
55// is still open. Since 11:00 must still find a chunk filed under hour 10 —
56// the listing is widened by one hour behind Since — while a record in that
57// same chunk whose Last never reaches the window stays excluded by Match.
58func TestReadWindowListsHourBeforeSince(t *testing.T) {
59 ctx := context.Background()
60 s := release.NewDirStore(t.TempDir())
61 base := time.Date(2026, 9, 7, 10, 50, 0, 0, time.UTC) // hour dir "…-10"
62 recs := []Record{
63 // Opened at 10:50, still being added to at 11:05: inside the
64 // [Since,Until] window by overlap, but filed under hour 10.
65 {TS: base, Box: "box1", Host: "h1", Source: SourceEdge, Name: "long", FP: "flong",
66 Count: 5, First: base, Last: base.Add(15 * time.Minute), Seq: 1},
67 // Opened and closed at 10:50-10:55, entirely before the window.
68 {TS: base, Box: "box1", Host: "h1", Source: SourceEdge, Name: "gone", FP: "fgone",
69 Count: 1, First: base, Last: base.Add(5 * time.Minute), Seq: 2},
70 }
71 body, err := EncodeChunk(recs)
72 if err != nil {
73 t.Fatal(err)
74 }
75 if _, err := s.Put(ctx, must(t)(ChunkKey("bab", "web", "box1", HourOf(recs[0].TS), recs[0].Seq, len(body))), body, ""); err != nil {
76 t.Fatal(err)
77 }
78 since := time.Date(2026, 9, 7, 11, 0, 0, 0, time.UTC)
79 until := time.Date(2026, 9, 7, 11, 30, 0, 0, time.UTC)
80 got, trimmed, err := ReadWindow(ctx, s, "bab", "web", Filter{Since: since, Until: until}, 100)
81 if err != nil || trimmed {
82 t.Fatalf("read: %v %v", err, trimmed)
83 }
84 if len(got) != 1 || got[0].FP != "flong" {
85 t.Fatalf("window = %+v, want only flong", got)
86 }
87}
88
89// must unwraps a key builder's (key, error) for fixtures whose names are
90// known good: must(t)(ChunkKey(...)).
91func must(t testing.TB) func(string, error) string {
92 return func(k string, err error) string {
93 t.Helper()
94 if err != nil {
95 t.Fatal(err)
96 }
97 return k
98 }
99}
100