mirror of
https://tangled.org/evan.jarrett.net/at-container-registry
synced 2026-08-29 04:06:58 +00:00
64 lines
2.2 KiB
Go
64 lines
2.2 KiB
Go
package token
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// TestHandler_GateAndFetchRunConcurrently asserts that the authorizer and
|
|
// service-auth fetcher run as parallel goroutines rather than sequentially.
|
|
//
|
|
// Both stubs share a cross-wired barrier: each closes its own `started`
|
|
// channel and then waits on the other's. If the handler runs them
|
|
// sequentially, the first stub blocks forever waiting for the second — its
|
|
// barrier-timeout fires and the request fails with a 403. If they run in
|
|
// parallel, both goroutines reach the barrier and release each other.
|
|
//
|
|
// This is deterministic — no wall-clock budget — so it works regardless of
|
|
// runner speed or the race detector's overhead.
|
|
func TestHandler_GateAndFetchRunConcurrently(t *testing.T) {
|
|
keyPath := getSharedTestKey(t)
|
|
issuer, err := NewIssuer(keyPath, "atcr.io", "registry", 5*time.Minute)
|
|
if err != nil {
|
|
t.Fatalf("NewIssuer() error = %v", err)
|
|
}
|
|
|
|
deviceStore, database := setupTestDeviceStore(t)
|
|
deviceSecret := createTestDevice(t, deviceStore, database, "did:plc:alice123", "alice.bsky.social")
|
|
|
|
gateStarted := make(chan struct{})
|
|
fetchStarted := make(chan struct{})
|
|
const barrierTimeout = 2 * time.Second
|
|
|
|
handler := NewHandler(issuer, deviceStore)
|
|
handler.SetAuthorizer(&stubAuthorizer{
|
|
started: gateStarted,
|
|
partnerStarted: fetchStarted,
|
|
barrierTimeout: barrierTimeout,
|
|
})
|
|
handler.SetServiceAuthFetcher(&stubServiceAuthFetcher{
|
|
expiresAt: time.Now().Add(4 * time.Minute),
|
|
started: fetchStarted,
|
|
partnerStarted: gateStarted,
|
|
barrierTimeout: barrierTimeout,
|
|
})
|
|
|
|
req := httptest.NewRequest(http.MethodGet, "/auth/token?service=registry&scope=repository:alice.bsky.social/myapp:pull,push", nil)
|
|
req.SetBasicAuth("alice", deviceSecret)
|
|
w := httptest.NewRecorder()
|
|
|
|
handler.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
// A barrier-timeout from sequential execution surfaces as a 403 with
|
|
// the "partner goroutine did not start" message in the body.
|
|
if strings.Contains(w.Body.String(), "sequential execution") {
|
|
t.Fatalf("gate and fetch ran sequentially: %s", w.Body.String())
|
|
}
|
|
t.Fatalf("expected 200, got %d. Body: %s", w.Code, w.Body.String())
|
|
}
|
|
}
|