carlos / platform Public

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

Plain git — no account needed to clone.

Download

Download this file

1package edge
2
3import (
4 "context"
5 "net/http/httptest"
6 "testing"
7 "time"
8
9 "github.com/carlosframework/platform/internal/domains"
10 "github.com/carlosframework/platform/internal/registry"
11 "github.com/carlosframework/platform/internal/release"
12)
13
14// TestPathFlipEndToEnd walks the whole chain a real flip takes — claim
15// published, reconcile pass, fingerprint moves, edge reloads, traffic changes
16// — and then walks it backwards. Each earlier task proved one layer against a
17// hand-written input; this is the only test that proves they compose.
18//
19// The registry write and the edge reload are driven directly rather than
20// waited on: the 2s tick is a latency bound, not a correctness property, and
21// polling it would make this test slow and flaky. What must be true is that
22// the fingerprint MOVES, which is asserted rather than assumed.
23func TestPathFlipEndToEnd(t *testing.T) {
24 ctx := context.Background()
25 classic, _ := hostEchoBackend(t, "classic")
26 golive, _ := hostEchoBackend(t, "go")
27
28 reg := openReg(t)
29 if err := reg.Upsert(registry.Route{
30 Host: "ti.to", Kind: "service", Account: "tito", App: "classic",
31 Addr: classic.Listener.Addr().String(), Status: "ready",
32 }); err != nil {
33 t.Fatal(err)
34 }
35 if err := reg.Upsert(registry.Route{
36 Host: "go.tito.io", Kind: "service", Account: "tito", App: "go",
37 Addr: golive.Listener.Addr().String(), Status: "ready",
38 }); err != nil {
39 t.Fatal(err)
40 }
41
42 store := release.NewDirStore(t.TempDir())
43 if _, _, err := domains.AttachClaim(ctx, store, domains.Claim{
44 Schema: 1, Host: "ti.to", Account: "tito", App: "classic",
45 Route: "ti.to", CreatedAt: "2026-09-03T00:00:00Z",
46 }); err != nil {
47 t.Fatal(err)
48 }
49
50 e, err := New(reg, nil)
51 if err != nil {
52 t.Fatal(err)
53 }
54 srv := httptest.NewServer(e)
55 defer srv.Close()
56
57 // Before the flip: everything is classic.
58 if _, body := getHostPath(t, srv.URL, "ti.to", "/paulca/awesomeconf"); body != "classic" {
59 t.Fatalf("before the flip: body = %q, want classic", body)
60 }
61
62 fpBefore, err := reg.Fingerprint()
63 if err != nil {
64 t.Fatal(err)
65 }
66
67 // The flip: Tito publishes its map, the pass folds it, the edge reloads.
68 if _, err := domains.SetClaimPaths(ctx, store, "ti.to", "tito", "classic",
69 map[string]string{"paulca": "go.tito.io"}); err != nil {
70 t.Fatal(err)
71 }
72 if err := domains.ReconcileAliases(ctx, store, reg, time.Now, nil, nil); err != nil {
73 t.Fatal(err)
74 }
75 fpAfter, err := reg.Fingerprint()
76 if err != nil {
77 t.Fatal(err)
78 }
79 if fpBefore == fpAfter {
80 t.Fatal("fingerprint did not move across a flip — the edge's watcher would never reload")
81 }
82 if err := e.Reload(); err != nil {
83 t.Fatal(err)
84 }
85
86 // After the flip: that account is on Go, its neighbours are not.
87 if _, body := getHostPath(t, srv.URL, "ti.to", "/paulca/awesomeconf"); body != "go" {
88 t.Fatalf("after the flip: body = %q, want go", body)
89 }
90 if _, body := getHostPath(t, srv.URL, "ti.to", "/someoneelse/event"); body != "classic" {
91 t.Fatalf("after the flip: a neighbouring account moved too, body = %q", body)
92 }
93 if _, body := getHostPath(t, srv.URL, "ti.to", "/paulcarson"); body != "classic" {
94 t.Fatalf("after the flip: a longer account name was dragged along, body = %q", body)
95 }
96
97 // The rollback: republish without that account.
98 if _, err := domains.SetClaimPaths(ctx, store, "ti.to", "tito", "classic", nil); err != nil {
99 t.Fatal(err)
100 }
101 if err := domains.ReconcileAliases(ctx, store, reg, time.Now, nil, nil); err != nil {
102 t.Fatal(err)
103 }
104 if err := e.Reload(); err != nil {
105 t.Fatal(err)
106 }
107 if _, body := getHostPath(t, srv.URL, "ti.to", "/paulca/awesomeconf"); body != "classic" {
108 t.Fatalf("after the rollback: body = %q, want classic", body)
109 }
110 rows, err := reg.ListPaths()
111 if err != nil {
112 t.Fatal(err)
113 }
114 if len(rows) != 0 {
115 t.Fatalf("after the rollback: %d path rows remain, want 0", len(rows))
116 }
117}
118