mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-07 19:56:18 -04:00
feat(#76): harvest request-scoped availability and freshness truth
Backend-only re-cut of the #76 availability work onto main, stacked on the canonical Weekend Context API. Adds request-scoped freshness reporting so aggregate responses cannot report fresh when a component is stale, plus local-first driver summary resolution and cache/pacing truth. The frontend half of #76 is deliberately excluded: it is built on the Weekend shell that failed owner review, including the full-width Partial banner treatment. Availability presentation is re-cut with the shell in #89. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
@@ -49,6 +51,70 @@ func TestRequestPacerNilSafe(t *testing.T) {
|
||||
p.wait() // must not panic
|
||||
}
|
||||
|
||||
func TestRequestPacerCancellationDoesNotCollideReservedWaiters(t *testing.T) {
|
||||
const interval = 80 * time.Millisecond
|
||||
p := &requestPacer{interval: interval}
|
||||
if err := p.waitContext(context.Background()); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p.mu.Lock()
|
||||
initialNext := p.next
|
||||
p.mu.Unlock()
|
||||
|
||||
waitForReservation := func(want time.Time) {
|
||||
t.Helper()
|
||||
deadline := time.Now().Add(250 * time.Millisecond)
|
||||
for time.Now().Before(deadline) {
|
||||
p.mu.Lock()
|
||||
got := p.next
|
||||
p.mu.Unlock()
|
||||
if got.Equal(want) {
|
||||
return
|
||||
}
|
||||
time.Sleep(time.Millisecond)
|
||||
}
|
||||
t.Fatalf("reservation did not reach %v", want)
|
||||
}
|
||||
|
||||
ctxB, cancelB := context.WithCancel(context.Background())
|
||||
bDone := make(chan error, 1)
|
||||
go func() { bDone <- p.waitContext(ctxB) }()
|
||||
waitForReservation(initialNext.Add(interval))
|
||||
|
||||
cDone := make(chan time.Time, 1)
|
||||
go func() {
|
||||
_ = p.waitContext(context.Background())
|
||||
cDone <- time.Now()
|
||||
}()
|
||||
waitForReservation(initialNext.Add(2 * interval))
|
||||
|
||||
cancelStarted := time.Now()
|
||||
cancelB()
|
||||
select {
|
||||
case err := <-bDone:
|
||||
if !errors.Is(err, context.Canceled) {
|
||||
t.Fatalf("B error = %v, want context.Canceled", err)
|
||||
}
|
||||
if elapsed := time.Since(cancelStarted); elapsed > 30*time.Millisecond {
|
||||
t.Fatalf("B cancellation took %v", elapsed)
|
||||
}
|
||||
case <-time.After(50 * time.Millisecond):
|
||||
t.Fatal("B did not return promptly after cancellation")
|
||||
}
|
||||
|
||||
dDone := make(chan time.Time, 1)
|
||||
go func() {
|
||||
_ = p.waitContext(context.Background())
|
||||
dDone <- time.Now()
|
||||
}()
|
||||
waitForReservation(initialNext.Add(3 * interval))
|
||||
|
||||
cAt, dAt := <-cDone, <-dDone
|
||||
if separation := dAt.Sub(cAt); separation < interval/2 {
|
||||
t.Fatalf("C and D collided: wake separation %v, want at least %v", separation, interval/2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetRetriesOn429(t *testing.T) {
|
||||
var calls atomic.Int32
|
||||
srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
Reference in New Issue
Block a user