mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-11 08:23:45 +00:00
feat: persist participant-scoped proposal recovery
This commit is contained in:
@@ -21,6 +21,19 @@ type queueBackendSpy struct{ createCalls, heartbeatCalls, cancelCalls, getCalls
|
||||
|
||||
type sessionBackendSpy struct{ calls int }
|
||||
|
||||
type proposalBackendSpy struct {
|
||||
proposal domain.Proposal
|
||||
calls int
|
||||
}
|
||||
|
||||
func (b *proposalBackendSpy) Get(_ context.Context, playerID, _ string, _ time.Time) (domain.Proposal, error) {
|
||||
b.calls++
|
||||
if !b.proposal.HasParticipant(playerID) {
|
||||
return domain.Proposal{}, domain.ErrNotParticipant
|
||||
}
|
||||
return b.proposal, nil
|
||||
}
|
||||
|
||||
func (s *sessionBackendSpy) Authenticate(_ context.Context, sessionID, _ string, _ time.Time) (domain.Session, error) {
|
||||
s.calls++
|
||||
return domain.Session{SessionID: sessionID, PlayerID: "player-1"}, nil
|
||||
@@ -341,6 +354,49 @@ func TestStateChangingAPIActionsPublishTargetedEvents(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestProposalRecoveryUsesDurableBackendAndRemainsParticipantScoped(t *testing.T) {
|
||||
now := time.Unix(1000, 0).UTC()
|
||||
proposal, err := domain.NewProposal("proposal-1234567890123456", domain.Casual, []string{"player-1", "player-2"}, now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
backend := &proposalBackendSpy{proposal: proposal}
|
||||
sessions := domain.NewSessionStore()
|
||||
participantSession, participantToken, err := sessions.Issue("player-1", time.Hour, now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
outsiderSession, outsiderToken, err := sessions.Issue("outsider", time.Hour, now)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
service := &Service{Sessions: sessions, Proposals: map[string]*domain.Proposal{}, ProposalBackend: backend, Now: func() time.Time { return now }}
|
||||
server := httptest.NewServer(service.Handler())
|
||||
defer server.Close()
|
||||
get := func(session domain.Session, token string) int {
|
||||
request, err := http.NewRequest(http.MethodGet, server.URL+"/v1/proposals/"+proposal.ProposalID, nil)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
request.Header.Set("Authorization", "Bearer "+session.SessionID+":"+token)
|
||||
response, err := server.Client().Do(request)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
_ = response.Body.Close()
|
||||
return response.StatusCode
|
||||
}
|
||||
if status := get(participantSession, participantToken); status != http.StatusOK {
|
||||
t.Fatalf("participant recovery status = %d", status)
|
||||
}
|
||||
if status := get(outsiderSession, outsiderToken); status != http.StatusNotFound {
|
||||
t.Fatalf("outsider recovery status = %d", status)
|
||||
}
|
||||
if backend.calls != 2 {
|
||||
t.Fatalf("durable backend calls = %d", backend.calls)
|
||||
}
|
||||
}
|
||||
|
||||
func TestQueueAPIRejectsUnauthenticatedUnknownAndOversizedInput(t *testing.T) {
|
||||
service := &Service{Sessions: domain.NewSessionStore(), Queue: domain.NewQueue(), Candidate: func(string, string) (domain.Candidate, error) { return domain.Candidate{}, nil }}
|
||||
server := httptest.NewServer(service.Handler())
|
||||
|
||||
Reference in New Issue
Block a user