Files
overwatch/simulation/tests/test_server_runtime.py
thebadking 34d4406611
Some checks failed
AERIS-10 CI / Python Lint + Tests (push) Has been cancelled
AERIS-10 CI / MCU Firmware Tests (push) Has been cancelled
AERIS-10 CI / FPGA Regression (push) Has been cancelled
AERIS-10 CI / Cross-Layer Contract Tests (push) Has been cancelled
Initial commit.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-10-21 07:49:17 +00:00

80 lines
2.8 KiB
Python

import asyncio
from types import SimpleNamespace
from unittest.mock import AsyncMock
import pytest
from defense_sim.server import socketio_server as server
class FakeSimulation:
def __init__(self, run_id, steps=4):
self.run_id = run_id
self.projectiles = [SimpleNamespace(active=True)]
self.events = []
self.remaining = steps
self.time_s = 0.
self.dt = .001
def step(self):
base = len(self.events)
self.events.extend({"type": "probe", "index": base+i} for i in range(100))
self.remaining -= 1
self.time_s += self.dt
self.projectiles[0].active = self.remaining > 0
def snapshot(self):
return {"run_id": self.run_id, "events": self.events[-250:]}
def test_event_history_cursor_and_socket_isolation(monkeypatch):
emit = AsyncMock()
monkeypatch.setattr(server.sio, 'emit', emit)
async def run():
await asyncio.gather(server._publish_loop(FakeSimulation('a'), 'client-a'),
server._publish_loop(FakeSimulation('b'), 'client-b'))
asyncio.run(run())
for sid in ('client-a', 'client-b'):
events = [call.args[1] for call in emit.call_args_list
if call.args[0] == server.EVENT and call.kwargs['to'] == sid]
assert len(events) == 400
assert [event['index'] for event in events] == list(range(400))
assert all(call.kwargs['to'] in {'client-a', 'client-b'} for call in emit.call_args_list)
def test_cancel_only_requested_connection(monkeypatch):
async def run():
waiting = asyncio.Event()
a, b = asyncio.create_task(waiting.wait()), asyncio.create_task(waiting.wait())
monkeypatch.setattr(server, 'tasks', {'a': a, 'b': b})
await server._cancel_run('a')
assert a.cancelled() and not b.done()
await server._cancel_run('b')
asyncio.run(run())
def test_live_clock_never_runs_ahead_and_transport_cost_does_not_add_drift(monkeypatch):
clock = [100.]
sleeps = []
sim = FakeSimulation('paced', steps=8)
sim.dt = .1
sim.time_s = 7. # restarting a nonzero engine must establish a new origin
real_sleep = asyncio.sleep
monkeypatch.setattr(server.time, 'perf_counter', lambda: clock[0])
async def sleep(delay):
sleeps.append(delay)
clock[0] += delay
await real_sleep(0)
async def emit(event, payload, **kwargs):
assert sim.time_s-7. <= clock[0]-100.+1e-9
clock[0] += .0001 # network emission takes wall time
monkeypatch.setattr(server.asyncio, 'sleep', sleep)
monkeypatch.setattr(server.sio, 'emit', emit)
asyncio.run(server._publish_loop(sim, 'client'))
assert sim.time_s == pytest.approx(7.8)
assert .8 <= clock[0]-100. < .825
assert any(delay > 0 for delay in sleeps)