feat(call-to-play): retain terminal outcomes
Keep complete running and cancelled histories visible for fifteen minutes so peers retain the roster, chat, and outcome long enough to understand what happened. Compact them to terminal tombstones afterward without charging settled calls against the active-history limit. Model running and cancelled as durable read-only frontend states, exclude them from active badges, prune retired raw events, and document the lifecycle. Add peer scenario S49 to prove a late joiner reconstructs a terminal call with its roster and chat intact. Test Plan: - just fmt - just clippy - just test - just frontend-test - just build - just peer-cli-tests S48 S49 - python3 -m py_compile crates/lanspread-peer-cli/scripts/run_extended_scenarios.py - git diff --cached --check
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Run the peer-cli scenarios S1-S48 through Docker."""
|
||||
"""Run the peer-cli scenarios S1-S49 through Docker."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -357,6 +357,7 @@ class Runner:
|
||||
("S46", self.s46_receiver_cancel_mid_stream),
|
||||
("S47", self.s47_multi_archive_streams_in_sorted_order),
|
||||
("S48", self.s48_call_to_play_replication_and_late_join),
|
||||
("S49", self.s49_terminal_call_to_play_late_join),
|
||||
]
|
||||
|
||||
for scenario_id, scenario in scenarios:
|
||||
@@ -1824,6 +1825,79 @@ class Runner:
|
||||
|
||||
return "live create/RSVP/chat replicated and a late joiner received deduplicated history"
|
||||
|
||||
def s49_terminal_call_to_play_late_join(self) -> str:
|
||||
alice = self.peer("s49-alice")
|
||||
bob = self.peer("s49-bob")
|
||||
bob.connect_to(alice)
|
||||
|
||||
now = int(time.time() * 1000)
|
||||
create = {
|
||||
"id": "s49-create",
|
||||
"call_id": "s49-call",
|
||||
"actor_id": "",
|
||||
"actor_name": "Alice",
|
||||
"at": now,
|
||||
"action": {
|
||||
"Create": {
|
||||
"game_id": "cnctw",
|
||||
"max_players": 4,
|
||||
"scheduled_for": None,
|
||||
"deadline": now + 600_000,
|
||||
}
|
||||
},
|
||||
}
|
||||
ready = {
|
||||
"id": "s49-ready",
|
||||
"call_id": "s49-call",
|
||||
"actor_id": "",
|
||||
"actor_name": "Bob",
|
||||
"at": now + 1,
|
||||
"action": {"Respond": {"ready_at": None}},
|
||||
}
|
||||
message = {
|
||||
"id": "s49-message-event",
|
||||
"call_id": "s49-call",
|
||||
"actor_id": "",
|
||||
"actor_name": "Bob",
|
||||
"at": now + 2,
|
||||
"action": {
|
||||
"SendMessage": {
|
||||
"message_id": "s49-message",
|
||||
"text": "Ready to launch",
|
||||
}
|
||||
},
|
||||
}
|
||||
start = {
|
||||
"id": "s49-start",
|
||||
"call_id": "s49-call",
|
||||
"actor_id": "",
|
||||
"actor_name": "Alice",
|
||||
"at": now + 3,
|
||||
"action": "Start",
|
||||
}
|
||||
|
||||
alice.publish_call_to_play(create)
|
||||
wait_call_to_play_events(bob, {"s49-create"})
|
||||
bob.publish_call_to_play(ready)
|
||||
bob.publish_call_to_play(message)
|
||||
wait_call_to_play_events(alice, {"s49-create", "s49-ready", "s49-message-event"})
|
||||
alice.publish_call_to_play(start)
|
||||
wait_call_to_play_events(
|
||||
bob,
|
||||
{"s49-create", "s49-ready", "s49-message-event", "s49-start"},
|
||||
)
|
||||
|
||||
charlie = self.peer("s49-charlie")
|
||||
charlie.connect_to(alice)
|
||||
events = wait_call_to_play_events(
|
||||
charlie,
|
||||
{"s49-create", "s49-ready", "s49-message-event", "s49-start"},
|
||||
)
|
||||
if len(events) != 4:
|
||||
raise ScenarioError(f"terminal late-join history is incomplete: {events}")
|
||||
|
||||
return "late joiner reconstructed terminal call outcome, roster, and chat"
|
||||
|
||||
|
||||
def run(command: list[str], description: str) -> subprocess.CompletedProcess[str]:
|
||||
result = subprocess.run(
|
||||
|
||||
Reference in New Issue
Block a user