activator: a beacon to a hibernating route leaves lastActive alone
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
1 file changed,
+40
−0
internal/activator/activator_test.go+40 −0
diff --git a/internal/activator/activator_test.go b/internal/activator/activator_test.go| index 5893ae761..8ead7934a 100644 |
| --- a/internal/activator/activator_test.go |
| +++ b/internal/activator/activator_test.go |
| @@ -3995,3 +3995,43 @@ func TestChildProcStopClosesTheControlSocketWhenTheChildIsAlreadyGone(t *testing |
| t.Fatalf("control socket after Stop on an exited child: stat err = %v, want it unlinked", err) |
| } |
| } |
| + |
| +// A beacon to a hibernating route is answered by the edge before the |
| +// activator branch: lastActive is untouched and nothing is started |
| +// (spec §4: nothing here wakes an instance). |
| +func TestBrowserBeaconDoesNotStampActivity(t *testing.T) { |
| + const host = "beacon.bab.oncarlos.com" |
| + a, b, clk, reg := testActivator(t, time.Hour) |
| + rt := testRoute(host) |
| + arrangeAwake(t, a, rt) |
| + if err := reg.Upsert(rt); err != nil { |
| + t.Fatalf("Upsert %s: %v", rt.Host, err) |
| + } |
| + a.mu.Lock() |
| + before := a.entries[rt.Host].lastActive |
| + a.mu.Unlock() |
| + clk.advance(time.Minute) |
| + _, _, starts0 := b.counts() |
| + |
| + e, err := edge.New(reg, a) |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + rec := httptest.NewRecorder() |
| + req := httptest.NewRequest("POST", "https://"+host+"/__carlos/a", strings.NewReader(`{"name":"n"}`)) |
| + req.Host = host |
| + req.Header.Set("Origin", "https://"+host) |
| + e.ServeHTTP(rec, req) |
| + if rec.Code != http.StatusAccepted { |
| + t.Fatalf("status %d", rec.Code) |
| + } |
| + a.mu.Lock() |
| + after := a.entries[rt.Host].lastActive |
| + a.mu.Unlock() |
| + if !after.Equal(before) { |
| + t.Errorf("lastActive moved %v -> %v", before, after) |
| + } |
| + if _, _, starts := b.counts(); starts != starts0 { |
| + t.Errorf("a beacon started an instance") |
| + } |
| +} |