carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package host
2
3import (
4 "context"
5 "encoding/json"
6 "testing"
7 "time"
8
9 "github.com/carlosframework/platform/internal/analytics"
10 "github.com/carlosframework/platform/internal/release"
11 "github.com/carlosframework/platform/internal/usage"
12)
13
14func TestUsageFlushWritesAndReflushesMerge(t *testing.T) {
15 s := release.NewDirStore(t.TempDir())
16 ctx := context.Background()
17 at := time.Date(2026, 8, 11, 9, 0, 0, 0, time.UTC)
18 rows := map[string]usage.EdgeRow{
19 "a.example.com": {Account: "bab", App: "hello", Requests: 3, BytesOut: 300},
20 }
21 if err := usageFlush(ctx, s, s, "box1", func() map[string]usage.EdgeRow { return rows }, nil, at); err != nil {
22 t.Fatal(err)
23 }
24 if err := usageFlush(ctx, s, s, "box1", func() map[string]usage.EdgeRow { return rows }, nil, at.Add(5*time.Minute)); err != nil {
25 t.Fatal(err)
26 }
27 raw, _, err := s.Get(ctx, usage.EdgeDayKey("box1", "2026-08-11"))
28 if err != nil {
29 t.Fatal(err)
30 }
31 var day usage.EdgeDay
32 if err := json.Unmarshal(raw, &day); err != nil {
33 t.Fatal(err)
34 }
35 if got := day.Hours["09"]["a.example.com"].Requests; got != 6 {
36 t.Fatalf("requests = %d, want 6", got)
37 }
38}
39
40func TestUsageFlushEmptyDrainWritesNothing(t *testing.T) {
41 s := release.NewDirStore(t.TempDir())
42 err := usageFlush(context.Background(), s, s, "box1", func() map[string]usage.EdgeRow { return nil }, nil, time.Now().UTC())
43 if err != nil {
44 t.Fatal(err)
45 }
46 if keys, _ := s.List(context.Background(), "usage/"); len(keys) != 0 {
47 t.Fatalf("wrote %v", keys)
48 }
49}
50
51// TestUsageFlushAlsoFlushesAnalytics: the same tick that flushes edge usage
52// also drains the analytics meter into its app-day objects (spec §3.3). A
53// batch that fails to write is re-queued and lands on the next flush.
54func TestUsageFlushAlsoFlushesAnalytics(t *testing.T) {
55 ctx := context.Background()
56 s := release.NewDirStore(t.TempDir())
57 now := time.Date(2026, 9, 7, 9, 30, 0, 0, time.UTC)
58 am := analytics.NewMeter(t.TempDir(), "box1", func() time.Time { return now })
59 am.Observe(analytics.Obs{Acct: "bab", App: "web", Host: "h", Method: "GET", Status: 200, ContentType: "text/html", Path: "/", UA: "Mozilla/5.0 Safari", ClientIP: "1.2.3.4", Bytes: 10, When: now})
60 if err := usageFlush(ctx, s, s, "box1", func() map[string]usage.EdgeRow { return nil }, am, now); err != nil {
61 t.Fatal(err)
62 }
63 sum, err := analytics.ReadRange(ctx, s, "bab", "web", now, now)
64 if err != nil || sum.PV != 1 {
65 t.Fatalf("flushed day: %+v %v", sum, err)
66 }
67 // A failed analytics flush re-queues; the next flush lands it.
68 am.Observe(analytics.Obs{Acct: "bab", App: "web", Host: "h", Method: "GET", Status: 200, ContentType: "text/html", Path: "/", UA: "Mozilla/5.0 Safari", ClientIP: "1.2.3.4", Bytes: 10, When: now})
69 _ = usageFlush(ctx, s, failingStore{Store: s}, "box1", func() map[string]usage.EdgeRow { return nil }, am, now)
70 if err := usageFlush(ctx, s, s, "box1", func() map[string]usage.EdgeRow { return nil }, am, now); err != nil {
71 t.Fatal(err)
72 }
73 if sum, _ := analytics.ReadRange(ctx, s, "bab", "web", now, now); sum.PV != 2 {
74 t.Fatalf("after requeue: %d", sum.PV)
75 }
76}
77