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()) } }