feat: add authenticated queue recovery

This commit is contained in:
Josh Creek
2026-08-31 21:29:33 +01:00
parent 236cca30ba
commit 0f1cc17af6
5 changed files with 82 additions and 3 deletions
+47
View File
@@ -112,6 +112,53 @@ func TestQueueAPIRejectsUnauthenticatedUnknownAndOversizedInput(t *testing.T) {
_ = response.Body.Close()
}
func TestQueueRecoveryAPIIsAuthenticatedOwnerOnlyAndExpiresStaleTickets(t *testing.T) {
now := time.Unix(1000, 0).UTC()
sessions := domain.NewSessionStore()
ownerSession, ownerToken, err := sessions.Issue("player-1", time.Hour, now)
if err != nil {
t.Fatal(err)
}
otherSession, otherToken, err := sessions.Issue("player-2", time.Hour, now)
if err != nil {
t.Fatal(err)
}
queue := domain.NewQueue()
service := &Service{Sessions: sessions, Queue: queue, Now: func() time.Time { return now }, Candidate: func(playerID, ticketID string) (domain.Candidate, error) {
return domain.Candidate{PlayerID: playerID, TicketID: ticketID, EnqueuedAt: now}, nil
}}
server := httptest.NewServer(service.Handler())
defer server.Close()
create, _ := http.NewRequest(http.MethodPost, server.URL+"/v1/queue", strings.NewReader(`{"ticket_id":"ticket-recovery-123456"}`))
create.Header.Set("Authorization", "Bearer "+ownerSession.SessionID+":"+ownerToken)
create.Header.Set("Idempotency-Key", "queue-create-recovery-123456")
response, err := http.DefaultClient.Do(create)
if err != nil || response.StatusCode != http.StatusCreated {
t.Fatalf("create status=%v err=%v", response.StatusCode, err)
}
_ = response.Body.Close()
get, _ := http.NewRequest(http.MethodGet, server.URL+"/v1/queue/ticket-recovery-123456", nil)
get.Header.Set("Authorization", "Bearer "+ownerSession.SessionID+":"+ownerToken)
response, err = http.DefaultClient.Do(get)
if err != nil || response.StatusCode != http.StatusOK {
t.Fatalf("owner recovery status=%v err=%v", response.StatusCode, err)
}
_ = response.Body.Close()
get.Header.Set("Authorization", "Bearer "+otherSession.SessionID+":"+otherToken)
response, err = http.DefaultClient.Do(get)
if err != nil || response.StatusCode != http.StatusForbidden {
t.Fatalf("cross-player recovery status=%v err=%v", response.StatusCode, err)
}
_ = response.Body.Close()
service.Now = func() time.Time { return now.Add(domain.QueueExpiryWindow) }
get.Header.Set("Authorization", "Bearer "+ownerSession.SessionID+":"+ownerToken)
response, err = http.DefaultClient.Do(get)
if err != nil || response.StatusCode != http.StatusGone {
t.Fatalf("expired recovery status=%v err=%v", response.StatusCode, err)
}
_ = response.Body.Close()
}
func TestAuthenticatedProposalAPIUsesRevisionAndIdempotencyPolicy(t *testing.T) {
now := time.Unix(1000, 0).UTC()
sessions := domain.NewSessionStore()