mirror of
https://github.com/jcreek/CosmicClash.git
synced 2026-09-12 22:12:08 +00:00
feat: add ranked season rollover policy
This commit is contained in:
@@ -9,37 +9,71 @@ import (
|
||||
func TestProposalRequiresUnanimousAcceptance(t *testing.T) {
|
||||
now := time.Unix(1000, 0)
|
||||
p, err := NewProposal("proposal-123456789", Casual, []string{"b", "a"}, now)
|
||||
if err != nil { t.Fatal(err) }
|
||||
if _, err = p.Respond("a", "response-a-123456", true, 0, now); err != nil { t.Fatal(err) }
|
||||
if p.State != Open || p.Revision != 1 { t.Fatalf("partial acceptance closed proposal: %+v", p) }
|
||||
if _, err = p.Respond("b", "response-b-123456", true, 1, now); err != nil { t.Fatal(err) }
|
||||
if p.State != Accepted || p.Revision != 2 { t.Fatalf("unanimous acceptance not committed: %+v", p) }
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if _, err = p.Respond("a", "response-a-123456", true, 0, now); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p.State != Open || p.Revision != 1 {
|
||||
t.Fatalf("partial acceptance closed proposal: %+v", p)
|
||||
}
|
||||
if _, err = p.Respond("b", "response-b-123456", true, 1, now); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p.State != Accepted || p.Revision != 2 {
|
||||
t.Fatalf("unanimous acceptance not committed: %+v", p)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProposalResponseReplayIsStableAndPayloadReuseConflicts(t *testing.T) {
|
||||
now := time.Unix(1000, 0)
|
||||
p, err := NewProposal("proposal-123456789", Casual, []string{"a", "b"}, now)
|
||||
if err != nil { t.Fatal(err) }
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
first, err := p.Respond("a", "response-a-123456", true, 0, now)
|
||||
if err != nil { t.Fatal(err) }
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
replay, err := p.Respond("a", "response-a-123456", true, 0, now.Add(20*time.Second))
|
||||
if err != nil || replay.Revision != first.Revision { t.Fatalf("replay = %+v, %v", replay, err) }
|
||||
if _, err = p.Respond("a", "response-a-123456", false, 1, now); !errors.Is(err, ErrConflict) { t.Fatalf("payload reuse error = %v", err) }
|
||||
if err != nil || replay.Revision != first.Revision {
|
||||
t.Fatalf("replay = %+v, %v", replay, err)
|
||||
}
|
||||
if _, err = p.Respond("a", "response-a-123456", false, 1, now); !errors.Is(err, ErrConflict) {
|
||||
t.Fatalf("payload reuse error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestProposalExpiryTimesOutPendingParticipantsAndClosesRace(t *testing.T) {
|
||||
now := time.Unix(1000, 0)
|
||||
p, err := NewProposal("proposal-123456789", Ranked, []string{"a", "b", "c", "d", "e", "f"}, now)
|
||||
if err != nil { t.Fatal(err) }
|
||||
if !p.Expire(now.Add(ProposalWindow)) || p.State != Expired || p.Revision != 1 { t.Fatalf("expiry failed: %+v", p) }
|
||||
for _, participant := range p.Participants { if participant.Response != TimedOutResponse { t.Fatalf("pending participant not timed out: %+v", participant) } }
|
||||
if _, err = p.Respond("a", "late-response-123", true, 1, now.Add(ProposalWindow)); !errors.Is(err, ErrProposalClosed) { t.Fatalf("late response error = %v", err) }
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !p.Expire(now.Add(ProposalWindow)) || p.State != Expired || p.Revision != 1 {
|
||||
t.Fatalf("expiry failed: %+v", p)
|
||||
}
|
||||
for _, participant := range p.Participants {
|
||||
if participant.Response != TimedOutResponse {
|
||||
t.Fatalf("pending participant not timed out: %+v", participant)
|
||||
}
|
||||
}
|
||||
if _, err = p.Respond("a", "late-response-123", true, 1, now.Add(ProposalWindow)); !errors.Is(err, ErrProposalClosed) {
|
||||
t.Fatalf("late response error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRankedProposalAndCooldownEscalation(t *testing.T) {
|
||||
now := time.Unix(1000, 0)
|
||||
if _, err := NewProposal("proposal-123456789", Ranked, []string{"a", "b"}, now); err == nil { t.Fatal("ranked proposal accepted fewer than six") }
|
||||
if got := CooldownUntil([]CooldownEvent{{At: now, Playlist: Ranked, Kind: DeclinedResponse}}, Ranked, now); !got.Equal(now.Add(2*time.Minute)) { t.Fatalf("ranked decline cooldown = %v", got) }
|
||||
events := []CooldownEvent{{At: now, Playlist: Ranked, Kind: TimedOutResponse}, {At: now.Add(time.Minute), Playlist: Ranked, Kind: DeclinedResponse}, {At: now.Add(2*time.Minute), Playlist: Ranked, Kind: TimedOutResponse}}
|
||||
if got := CooldownUntil(events, Ranked, now.Add(2*time.Minute)); !got.Equal(now.Add(17*time.Minute)) { t.Fatalf("ranked escalation cooldown = %v", got) }
|
||||
if _, err := NewProposal("proposal-123456789", Ranked, []string{"a", "b"}, now); err == nil {
|
||||
t.Fatal("ranked proposal accepted fewer than six")
|
||||
}
|
||||
if got := CooldownUntil([]CooldownEvent{{At: now, Playlist: Ranked, Kind: DeclinedResponse}}, Ranked, now); !got.Equal(now.Add(2 * time.Minute)) {
|
||||
t.Fatalf("ranked decline cooldown = %v", got)
|
||||
}
|
||||
events := []CooldownEvent{{At: now, Playlist: Ranked, Kind: TimedOutResponse}, {At: now.Add(time.Minute), Playlist: Ranked, Kind: DeclinedResponse}, {At: now.Add(2 * time.Minute), Playlist: Ranked, Kind: TimedOutResponse}}
|
||||
if got := CooldownUntil(events, Ranked, now.Add(2*time.Minute)); !got.Equal(now.Add(17 * time.Minute)) {
|
||||
t.Fatalf("ranked escalation cooldown = %v", got)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user