carlos / platform Public

edge: add end-to-end cache-before-wake test

Paul Campbell f810271360f4dcd8c58f3b5664397793960d8dcb parent bbbe47c
1 file changed, +75 −0
  • internal/edge/edge_test.go +75 −0
diff --git a/internal/edge/edge_test.go b/internal/edge/edge_test.go
index 591e6379f..a14968d3f 100644
--- a/internal/edge/edge_test.go
+++ b/internal/edge/edge_test.go
@@ -691,3 +691,78 @@ func TestServeHTTPUpgradeRequestBypassesRecording(t *testing.T) {
e.ServeHTTP(rec, req)
}
+
+// activatorFunc adapts a plain function to the Activator interface, for
+// tests that want custom wake-counting behavior without a full fake type.
+type activatorFunc func(w http.ResponseWriter, r *http.Request, rt registry.Route, proxy http.Handler) bool
+
+func (f activatorFunc) ServeHTTP(w http.ResponseWriter, r *http.Request, rt registry.Route, proxy http.Handler) bool {
+ return f(w, r, rt, proxy)
+}
+
+func (f activatorFunc) RetireExcept(keep map[string]bool) {
+ // No-op for tests that don't need retirement tracking.
+}
+
+// TestCacheBeforeWakeFullCycle exercises the sequence the whole feature
+// exists for: a cold miss wakes the instance and populates the cache; a
+// repeat request hits the cache and does NOT wake the instance again; a
+// write's purge call makes the next request miss and see fresh content.
+func TestCacheBeforeWakeFullCycle(t *testing.T) {
+ body := "version 1"
+ backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
+ w.Header().Set("Cache-Control", "max-age=60")
+ w.WriteHeader(200)
+ w.Write([]byte(body))
+ }))
+ t.Cleanup(backend.Close)
+
+ wakes := 0
+ act := activatorFunc(func(w http.ResponseWriter, r *http.Request, rt registry.Route, proxy http.Handler) bool {
+ wakes++
+ proxy.ServeHTTP(w, r)
+ return true
+ })
+
+ reg := openReg(t)
+ if err := reg.Upsert(registry.Route{Host: "cycle.example.com", Kind: "instance", Addr: backend.Listener.Addr().String(), Status: "ready", Hibernate: true}); err != nil {
+ t.Fatalf("upsert: %v", err)
+ }
+ e, err := New(reg, act)
+ if err != nil {
+ t.Fatalf("New: %v", err)
+ }
+ cache := NewCache(DefaultCacheMaxEntries, DefaultCacheMaxBodyBytes)
+ e.EnableCache(cache)
+
+ get := func() string {
+ rec := httptest.NewRecorder()
+ e.ServeHTTP(rec, httptest.NewRequest("GET", "http://cycle.example.com/event", nil))
+ return rec.Body.String()
+ }
+
+ if got := get(); got != "version 1" {
+ t.Fatalf("first request body = %q, want %q", got, "version 1")
+ }
+ if wakes != 1 {
+ t.Fatalf("expected exactly 1 wake for the cold request, got %d", wakes)
+ }
+
+ if got := get(); got != "version 1" {
+ t.Fatalf("second request body = %q, want %q", got, "version 1")
+ }
+ if wakes != 1 {
+ t.Fatalf("expected the second request to hit cache without waking again, wakes = %d", wakes)
+ }
+
+ // Simulate the write + purge an instance does after a real change.
+ body = "version 2"
+ cache.PurgeURL("cycle.example.com", "/event", "")
+
+ if got := get(); got != "version 2" {
+ t.Fatalf("post-purge request body = %q, want %q", got, "version 2")
+ }
+ if wakes != 2 {
+ t.Fatalf("expected the post-purge request to miss and wake once more, wakes = %d", wakes)
+ }
+}