holy fuking shit
This commit is contained in:
@@ -17,7 +17,7 @@ from dataclasses import dataclass
|
||||
|
||||
from .jsonio import RequestFormatError, dumps, parse_request_object
|
||||
from .rules import ACTION_FIELDS
|
||||
from .simulator import LocalSimulator, SimulatorClosed
|
||||
from .simulator import LocalSimulator
|
||||
|
||||
MAX_BODY_BYTES = 65_536 # [题面 附件2 5.1]
|
||||
MAX_SAFE_READ_BYTES = 1_048_576 # 超限请求最多读 1MB 即断,防内存滥用 [工程]
|
||||
|
||||
@@ -22,7 +22,7 @@ class _ActionHandler(BaseHTTPRequestHandler):
|
||||
server_version = "MCM26B-Referee/1.0"
|
||||
|
||||
# ---- 日志静默(界面"指令与反馈"由会话层记录) ----------------------------
|
||||
def log_message(self, format, *args): # noqa: A002 - 标准库签名
|
||||
def log_message(self, format, *args): # pylint: disable=redefined-builtin(标准库签名)
|
||||
pass
|
||||
|
||||
# ---- 各方法统一走同一边界;未知方法由 do_* 缺失天然 501,这里显式覆盖常用
|
||||
@@ -123,7 +123,7 @@ class ActionInterfaceServer:
|
||||
name=f"referee-action-{session.case_code}",
|
||||
daemon=True).start()
|
||||
|
||||
def close(self, session: LiveSession) -> None:
|
||||
def close(self, _session: LiveSession) -> None:
|
||||
httpd, self._httpd, self._session = self._httpd, None, None
|
||||
if httpd is None:
|
||||
return
|
||||
|
||||
@@ -423,7 +423,7 @@ class Referee:
|
||||
def set_display_limit(self, limit: int) -> None:
|
||||
"""指令与反馈显示条数 100..5000 [题面 附件1 4.5];不影响日志完整性。"""
|
||||
with self._lock:
|
||||
if not isinstance(limit, int) or not (DISPLAY_LIMIT_MIN <= limit <= DISPLAY_LIMIT_MAX):
|
||||
if not isinstance(limit, int) or not DISPLAY_LIMIT_MIN <= limit <= DISPLAY_LIMIT_MAX:
|
||||
raise RefereeError(f"显示条数须为 {DISPLAY_LIMIT_MIN}..{DISPLAY_LIMIT_MAX}")
|
||||
self.display_limit = limit
|
||||
|
||||
|
||||
@@ -19,8 +19,8 @@ from threading import Event, Lock
|
||||
from unicodedata import category
|
||||
from copy import deepcopy
|
||||
|
||||
from .rules import (ACTION_FIELDS, CLEAR_NO_TARGET, CLEAR_RESULTS, CLEAR_SUCCESS,
|
||||
COMMON_FIELDS, DIRECTION, EXIT_USER, MEASURE_RESULTS, NEAR,
|
||||
from .rules import (ACTION_FIELDS, CLEAR_NO_TARGET, CLEAR_SUCCESS,
|
||||
COMMON_FIELDS, DIRECTION, EXIT_USER, NEAR,
|
||||
NO_SIGNAL, POSITION_FIELDS)
|
||||
from .spec import Spec, DEFAULT_SPEC
|
||||
from .timing import clear_cost, measure_cost
|
||||
@@ -58,7 +58,7 @@ class LocalSimulator:
|
||||
if len(channels) != len(set(channels)):
|
||||
raise ValueError("每个频道最多对应一个干扰源")
|
||||
for s in sources:
|
||||
if not (spec.field.channel_min <= s.channel <= spec.field.channel_max):
|
||||
if not spec.field.channel_min <= s.channel <= spec.field.channel_max:
|
||||
raise ValueError("频道超出 1..20")
|
||||
if not s.directional and s.heading_deg != 0.0:
|
||||
raise ValueError("全向源不应有朝向")
|
||||
@@ -109,8 +109,9 @@ class LocalSimulator:
|
||||
owner = True
|
||||
if not owner:
|
||||
pending["done"].wait()
|
||||
if pending["error"] is not None:
|
||||
raise pending["error"]
|
||||
failure: BaseException | None = pending["error"]
|
||||
if failure is not None:
|
||||
raise failure
|
||||
return deepcopy(pending["result"])
|
||||
try:
|
||||
result = self._handle(path, payload)
|
||||
@@ -166,7 +167,8 @@ class LocalSimulator:
|
||||
value = payload.get(name)
|
||||
if (not isinstance(value, str) or not value
|
||||
or any(category(c) in ("Cc", "Cf", "Cs") for c in value)
|
||||
or (field_spec.max_bytes and len(value.encode("utf-8")) > field_spec.max_bytes)):
|
||||
or (field_spec.max_bytes
|
||||
and len(value.encode("utf-8")) > field_spec.max_bytes)):
|
||||
return 400, self.error_body(path)
|
||||
# 2) 动作字段存在性 -> 400
|
||||
action_spec = ACTION_FIELDS[path]
|
||||
|
||||
@@ -14,8 +14,7 @@ from pathlib import Path
|
||||
from urllib.parse import parse_qs, urlsplit
|
||||
|
||||
from .jsonio import dumps
|
||||
from .session import (DISPLAY_LIMIT_MAX, DISPLAY_LIMIT_MIN, LiveSession,
|
||||
Referee, RefereeError)
|
||||
from .session import LiveSession, Referee, RefereeError
|
||||
|
||||
STATIC_DIR = Path(__file__).parent / "static"
|
||||
STATIC_FILES = {
|
||||
@@ -31,7 +30,7 @@ class _UIHandler(BaseHTTPRequestHandler):
|
||||
protocol_version = "HTTP/1.1"
|
||||
server_version = "MCM26B-Referee-UI/1.0"
|
||||
|
||||
def log_message(self, format, *args): # noqa: A002 - 标准库签名
|
||||
def log_message(self, format, *args): # pylint: disable=redefined-builtin(标准库签名)
|
||||
pass
|
||||
|
||||
# ------------------------------------------------------------ 路由
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""真实 HTTP 承载测试:接口仅回环、仅窗口期开放、协议边界与官方一致。"""
|
||||
|
||||
# pylint: disable=redefined-outer-name # pytest 夹具名必须与测试函数形参同名,非遮蔽缺陷
|
||||
|
||||
import http.client
|
||||
import json
|
||||
import time
|
||||
@@ -20,7 +22,7 @@ def wait_until(condition, timeout=5.0, interval=0.02):
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def referee(tmp_path):
|
||||
def local_referee(tmp_path):
|
||||
ref = Referee(action_port=0, data_dir=tmp_path / "data")
|
||||
ActionInterfaceServer(ref)
|
||||
return ref
|
||||
@@ -43,20 +45,20 @@ def post(port, path, obj=None, *, headers=None, method="POST", raw=None):
|
||||
return response.status, parsed
|
||||
|
||||
|
||||
def payload(id="e", **extra):
|
||||
return dict(arena_id="default", robot_id="2026001", request_id=id, **extra)
|
||||
def payload(request_id="e", **extra):
|
||||
return dict(arena_id="default", robot_id="2026001", request_id=request_id, **extra)
|
||||
|
||||
|
||||
def start_open_session(referee, **opts):
|
||||
def start_open_session(local_referee, **opts):
|
||||
opts.setdefault("countdown_s", 0.05)
|
||||
session = referee.start("q3_practice", "2026001", **opts)
|
||||
session = local_referee.start("q3_practice", "2026001", **opts)
|
||||
assert wait_until(lambda: session.phase == "open")
|
||||
return session
|
||||
|
||||
|
||||
def test_binding_is_loopback_only(referee):
|
||||
session = start_open_session(referee)
|
||||
server = referee._open_interface.__self__ # ActionInterfaceServer
|
||||
def test_binding_is_loopback_only(local_referee):
|
||||
_session = start_open_session(local_referee)
|
||||
server = local_referee._open_interface.__self__ # ActionInterfaceServer
|
||||
port = open_port(server)
|
||||
assert server._httpd.server_address[0] == "127.0.0.1"
|
||||
status, body = post(port, "/enter", payload("enter"))
|
||||
@@ -64,17 +66,17 @@ def test_binding_is_loopback_only(referee):
|
||||
assert body["remaining_real_duration_s"] == 1200
|
||||
|
||||
|
||||
def test_interface_refuses_connections_outside_window(referee):
|
||||
session = referee.start("q3_practice", "2026001", countdown_s=5)
|
||||
server = referee._open_interface.__self__
|
||||
def test_interface_refuses_connections_outside_window(local_referee):
|
||||
session = local_referee.start("q3_practice", "2026001", countdown_s=5)
|
||||
server = local_referee._open_interface.__self__
|
||||
assert server.is_open is False # 倒计时期间未监听
|
||||
assert wait_until(lambda: session.phase == "open", timeout=6)
|
||||
assert server.is_open is True # 窗口开启后才绑定端口
|
||||
|
||||
|
||||
def test_full_protocol_round_trip_over_http(referee):
|
||||
session = start_open_session(referee)
|
||||
server = referee._open_interface.__self__
|
||||
def test_full_protocol_round_trip_over_http(local_referee):
|
||||
session = start_open_session(local_referee)
|
||||
server = local_referee._open_interface.__self__
|
||||
port = open_port(server)
|
||||
status, body = post(port, "/enter", payload("enter-1"))
|
||||
assert status == 200 and body["accepted"] is True
|
||||
@@ -94,9 +96,9 @@ def test_full_protocol_round_trip_over_http(referee):
|
||||
assert session.end_reason == "user_exit"
|
||||
|
||||
|
||||
def test_protocol_error_statuses_over_http(referee):
|
||||
start_open_session(referee)
|
||||
server = referee._open_interface.__self__
|
||||
def test_protocol_error_statuses_over_http(local_referee):
|
||||
start_open_session(local_referee)
|
||||
server = local_referee._open_interface.__self__
|
||||
port = open_port(server)
|
||||
assert post(port, "/unknown", payload())[0] == 404
|
||||
assert post(port, "/enter/", payload())[0] == 404
|
||||
@@ -109,9 +111,9 @@ def test_protocol_error_statuses_over_http(referee):
|
||||
assert status == 200 and body["accepted"] is True
|
||||
|
||||
|
||||
def test_interface_closes_after_window_timeout(referee):
|
||||
session = start_open_session(referee, window_s=0.4)
|
||||
server = referee._open_interface.__self__
|
||||
def test_interface_closes_after_window_timeout(local_referee):
|
||||
session = start_open_session(local_referee, window_s=0.4)
|
||||
server = local_referee._open_interface.__self__
|
||||
port = open_port(server)
|
||||
assert wait_until(lambda: session.phase == "finished", timeout=5)
|
||||
assert session.end_reason == "window_timeout"
|
||||
@@ -120,9 +122,9 @@ def test_interface_closes_after_window_timeout(referee):
|
||||
post(port, "/enter", payload("late"))
|
||||
|
||||
|
||||
def test_recorded_feed_contains_every_request(referee):
|
||||
session = start_open_session(referee)
|
||||
server = referee._open_interface.__self__
|
||||
def test_recorded_feed_contains_every_request(local_referee):
|
||||
session = start_open_session(local_referee)
|
||||
server = local_referee._open_interface.__self__
|
||||
port = open_port(server)
|
||||
post(port, "/enter", payload("enter-1"))
|
||||
post(port, "/unknown", payload("bad"))
|
||||
|
||||
@@ -11,8 +11,8 @@ from referee import LocalSimulator, SimulatorClosed
|
||||
from referee.interface import handle_raw
|
||||
|
||||
|
||||
def payload(id="e", **extra):
|
||||
return dict(arena_id="default", robot_id="2026001", request_id=id, **extra)
|
||||
def payload(request_id="e", **extra):
|
||||
return dict(arena_id="default", robot_id="2026001", request_id=request_id, **extra)
|
||||
|
||||
|
||||
def post(sim, path, body=None, headers=None, method="POST", target=None):
|
||||
@@ -60,7 +60,8 @@ def test_raw_size_and_depth_boundaries_are_inclusive():
|
||||
("PUT", "/clear", None, 405),
|
||||
("POST", "/enter", [("Content-Type", "text/plain")], 415),
|
||||
("POST", "/enter", [("Content-Type", "application/json;foo=bar")], 415),
|
||||
("POST", "/enter", [("Content-Type", "application/json"), ("Content-Type", "application/json")], 415),
|
||||
("POST", "/enter",
|
||||
[("Content-Type", "application/json"), ("Content-Type", "application/json")], 415),
|
||||
("POST", "/enter", [("Content-Type", "application/json"), ("Content-Encoding", "gzip")], 415),
|
||||
("POST", "/enter", [("content-type", "Application/JSON; charset=UTF-8")], 200),
|
||||
("POST", "/enter", [("content-type", 'application/json;charset="utf-8"')], 200),
|
||||
@@ -84,7 +85,8 @@ def test_on_request_callback_receives_parsed_payload():
|
||||
seen = []
|
||||
raw = json.dumps(payload("enter-1")).encode()
|
||||
handle_raw(sim, "POST", "/enter", [("Content-Type", "application/json")], raw,
|
||||
on_request=lambda path, payload_dict, result: seen.append((path, payload_dict, result)))
|
||||
on_request=lambda path, payload_dict, result: seen.append(
|
||||
(path, payload_dict, result)))
|
||||
assert seen[0][0] == "/enter"
|
||||
assert seen[0][1]["request_id"] == "enter-1"
|
||||
assert seen[0][2].status == 200
|
||||
|
||||
@@ -20,6 +20,7 @@ HARNESS = REPO / "referee" / "tests" / "js" / "replay_harness.cjs"
|
||||
@pytest.mark.skipif(shutil.which("node") is None, reason="需要 node 环境运行 JS 桩测试")
|
||||
def test_replay_player_logic():
|
||||
result = subprocess.run(["node", str(HARNESS)], capture_output=True, text=True,
|
||||
env={**os.environ, "REPO": str(REPO)}, timeout=60)
|
||||
env={**os.environ, "REPO": str(REPO)}, timeout=60,
|
||||
check=False)
|
||||
assert result.returncode == 0, f"回放器逻辑测试失败:\n{result.stdout}\n{result.stderr}"
|
||||
assert "ALL PASS" in result.stdout
|
||||
|
||||
@@ -65,7 +65,7 @@ def test_cannot_start_while_running_and_restart_after_finish(tmp_path):
|
||||
def test_formal_attempts_accounting(tmp_path):
|
||||
referee = Referee(action_port=0, data_dir=tmp_path)
|
||||
for _ in range(FORMAL_ATTEMPTS):
|
||||
session = referee.start("q3_formal", "2026001", countdown_s=0.05)
|
||||
_session = referee.start("q3_formal", "2026001", countdown_s=0.05)
|
||||
referee.abort() # 正式中止仍占用机会 [题面 4.5]
|
||||
with pytest.raises(RefereeError):
|
||||
referee.start("q3_formal", "2026001", countdown_s=0.05)
|
||||
@@ -121,7 +121,7 @@ def test_record_action_updates_feed_trail_and_counts(tmp_path):
|
||||
referee = Referee(action_port=0, data_dir=tmp_path)
|
||||
session = referee.start("q3_practice", "2026001", countdown_s=0.05, window_s=5)
|
||||
assert wait_until(lambda: session.phase == "open")
|
||||
sim = session.simulator
|
||||
_sim = session.simulator
|
||||
referee.record_action(session, "POST", "/measure",
|
||||
{"request_id": "m1", "position": {"x": 5000, "y": 0},
|
||||
"channel": 1},
|
||||
@@ -168,7 +168,7 @@ def test_display_limit_and_port_settings(tmp_path):
|
||||
referee.set_display_limit(50)
|
||||
with pytest.raises(RefereeError):
|
||||
referee.set_display_limit(5001)
|
||||
session = referee.start("q3_practice", "2026001", countdown_s=5)
|
||||
_session = referee.start("q3_practice", "2026001", countdown_s=5)
|
||||
with pytest.raises(RefereeError):
|
||||
referee.set_action_port(8080) # 测试进行中不能改端口
|
||||
referee.abort()
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
"""评测引擎一致性:时限、状态机、观测判定、幂等与量化(移植自参赛队仓库并扩展)。"""
|
||||
|
||||
from concurrent.futures import ThreadPoolExecutor
|
||||
from dataclasses import replace
|
||||
from threading import Event
|
||||
|
||||
import pytest
|
||||
@@ -9,12 +8,12 @@ import pytest
|
||||
from referee import FieldSpec, LocalSimulator, SimulatorClosed, Source, Spec
|
||||
|
||||
|
||||
def payload(id="e", **extra):
|
||||
return dict(arena_id="default", robot_id="2026001", request_id=id, **extra)
|
||||
def payload(request_id="e", **extra):
|
||||
return dict(arena_id="default", robot_id="2026001", request_id=request_id, **extra)
|
||||
|
||||
|
||||
def measure(id="m", x=0, y=0, channel=1):
|
||||
return payload(id, position=dict(x=x, y=y), channel=channel)
|
||||
def measure(request_id="m", x=0, y=0, channel=1):
|
||||
return payload(request_id, position=dict(x=x, y=y), channel=channel)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------- 时限 [题面 附件1 2.5]
|
||||
@@ -92,7 +91,7 @@ def test_error_rejection_does_not_consume_id():
|
||||
sim = LocalSimulator([])
|
||||
for bad in ["e\n", "e\u200b", "e\u0085", "e\ud800"]:
|
||||
assert sim.handle("/enter", payload(bad))[0] == 400
|
||||
assert sim._idempotency == {}
|
||||
assert not sim._idempotency
|
||||
assert sim.handle("/enter", payload())[1]["accepted"]
|
||||
|
||||
|
||||
@@ -181,6 +180,8 @@ def test_huge_numeric_fields_return_bad_request():
|
||||
(dict(arena_id="other", robot_id="2026001", request_id="e"), False),
|
||||
(dict(arena_id="default", robot_id="someone-else", request_id="e"), False),
|
||||
])
|
||||
# pytest 参数化保留 expect 列(用例矩阵可读性),当前断言恒为 False,故不参与断言
|
||||
# pylint: disable-next=unused-argument
|
||||
def test_unknown_fields_and_mismatch_are_business_rejected(body, expect):
|
||||
sim = LocalSimulator([])
|
||||
status, response = sim.handle("/enter", body)
|
||||
@@ -204,12 +205,14 @@ def test_channel_and_position_validation():
|
||||
]
|
||||
for extra in cases:
|
||||
assert sim.handle("/measure", payload("m", **extra))[0] == 400
|
||||
assert sim.handle("/measure", payload("ok", position=dict(x=0, y=0), channel=1.0))[1]["accepted"]
|
||||
assert sim.handle("/measure",
|
||||
payload("ok", position=dict(x=0, y=0), channel=1.0))[1]["accepted"]
|
||||
|
||||
|
||||
def test_actions_before_enter_are_business_rejected():
|
||||
sim = LocalSimulator([])
|
||||
for path, body in [("/measure", measure()), ("/clear", payload("c", position=dict(x=0, y=0), channel=1)),
|
||||
for path, body in [("/measure", measure()),
|
||||
("/clear", payload("c", position=dict(x=0, y=0), channel=1)),
|
||||
("/exit", payload("x"))]:
|
||||
status, response = sim.handle(path, body)
|
||||
assert status == 200 and response["accepted"] is False
|
||||
@@ -224,7 +227,8 @@ def test_clear_rules():
|
||||
assert ok["clear_result"] == "success"
|
||||
again = sim.handle("/clear", payload("c5b", position=dict(x=10, y=0), channel=5))[1]
|
||||
assert again["clear_result"] == "no_target_in_range" # 同一源只能清除一次
|
||||
assert sim.handle("/clear", payload("c6", position=dict(x=15, y=0), channel=6))[1]["clear_result"] == "success"
|
||||
cleared = sim.handle("/clear", payload("c6", position=dict(x=15, y=0), channel=6))[1]
|
||||
assert cleared["clear_result"] == "success"
|
||||
miss = sim.handle("/clear", payload("c9", position=dict(x=0, y=0), channel=9))[1]
|
||||
assert miss["clear_result"] == "no_target_in_range"
|
||||
assert sim.channel == 1 # /clear 不切换测向机频道 [题面 附件1 2.3]
|
||||
@@ -238,15 +242,20 @@ def test_measure_observation_matrix():
|
||||
sim = LocalSimulator(sources, error_half_deg=0.0)
|
||||
sim.handle("/enter", payload("enter"))
|
||||
# 超出有效接收半径
|
||||
assert sim.handle("/measure", measure("a", -600, 0, channel=1))[1]["measure_result"] == "no_signal"
|
||||
assert sim.handle("/measure",
|
||||
measure("a", -600, 0, channel=1))[1]["measure_result"] == "no_signal"
|
||||
# 接收半径边界:distance == r_eff 仍有信号 [题面 附件2 2.1 "超过"]
|
||||
assert sim.handle("/measure", measure("b", -500, 0, channel=1))[1]["measure_result"] == "direction"
|
||||
assert sim.handle("/measure",
|
||||
measure("b", -500, 0, channel=1))[1]["measure_result"] == "direction"
|
||||
# 覆盖角边界:源2 朝东,正东方向(bearing=0°)恰在 ±90° 覆盖内
|
||||
assert sim.handle("/measure", measure("c", 200, 0, channel=2))[1]["measure_result"] == "direction"
|
||||
assert sim.handle("/measure",
|
||||
measure("c", 200, 0, channel=2))[1]["measure_result"] == "direction"
|
||||
# 覆盖角边界:bearing=90°(正北)仍在覆盖内(含边界)[题面 附件2 2.2]
|
||||
assert sim.handle("/measure", measure("c2", 100, 100, channel=2))[1]["measure_result"] == "direction"
|
||||
assert sim.handle("/measure",
|
||||
measure("c2", 100, 100, channel=2))[1]["measure_result"] == "direction"
|
||||
# 覆盖角外(bearing≈135° > 90°):无信号
|
||||
assert sim.handle("/measure", measure("d", 90, 10, channel=2))[1]["measure_result"] == "no_signal"
|
||||
assert sim.handle("/measure",
|
||||
measure("d", 90, 10, channel=2))[1]["measure_result"] == "no_signal"
|
||||
# 距离过近:不返回 svd_deg [题面 附件2 2.4]
|
||||
near = sim.handle("/measure", measure("near", 998, 0, channel=1))[1]
|
||||
assert near["measure_result"] == "near" and "svd_deg" not in near
|
||||
|
||||
@@ -4,12 +4,12 @@ from referee import CostBreakdown, LocalSimulator, Source, clear_cost, measure_c
|
||||
from referee.timing import move_time
|
||||
|
||||
|
||||
def payload(id="e", **extra):
|
||||
return dict(arena_id="default", robot_id="2026001", request_id=id, **extra)
|
||||
def payload(request_id="e", **extra):
|
||||
return dict(arena_id="default", robot_id="2026001", request_id=request_id, **extra)
|
||||
|
||||
|
||||
def measure(id="m", x=0, y=0, channel=1):
|
||||
return payload(id, position=dict(x=x, y=y), channel=channel)
|
||||
def measure(request_id="m", x=0, y=0, channel=1):
|
||||
return payload(request_id, position=dict(x=x, y=y), channel=channel)
|
||||
|
||||
|
||||
def test_attachment_timing_example():
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
"""WebUI API 测试:状态快照、启动/中止/配置、会话列表与回放数据源。"""
|
||||
|
||||
# pylint: disable=redefined-outer-name # pytest 夹具名必须与测试函数形参同名,非遮蔽缺陷
|
||||
|
||||
import http.client
|
||||
import json
|
||||
import time
|
||||
@@ -21,7 +23,7 @@ def wait_until(condition, timeout=5.0, interval=0.02):
|
||||
|
||||
|
||||
@pytest.fixture()
|
||||
def ui(tmp_path):
|
||||
def webui(tmp_path):
|
||||
referee = Referee(action_port=0, data_dir=tmp_path / "data")
|
||||
ActionInterfaceServer(referee)
|
||||
server = WebUIServer(referee, 0)
|
||||
@@ -43,8 +45,8 @@ def call(port, method, path, obj=None):
|
||||
return response.status, json.loads(data) if data else None
|
||||
|
||||
|
||||
def test_state_idle_snapshot(ui):
|
||||
referee, port = ui
|
||||
def test_state_idle_snapshot(webui):
|
||||
_referee, port = webui
|
||||
status, state = call(port, "GET", "/ui/api/state")
|
||||
assert status == 200
|
||||
assert state["phase"] == "idle"
|
||||
@@ -53,8 +55,8 @@ def test_state_idle_snapshot(ui):
|
||||
assert state["spec"]["omega_radius_m"] == 1800
|
||||
|
||||
|
||||
def test_start_countdown_open_abort_cycle(ui):
|
||||
referee, port = ui
|
||||
def test_start_countdown_open_abort_cycle(webui):
|
||||
referee, port = webui
|
||||
status, body = call(port, "POST", "/ui/api/start",
|
||||
{"module": "q3_practice", "robot_id": "2026001",
|
||||
"countdown_s": 0.05, "window_s": 5})
|
||||
@@ -70,21 +72,21 @@ def test_start_countdown_open_abort_cycle(ui):
|
||||
assert state["truth"]["revealed"] is True # 演练结束显示真值统计
|
||||
|
||||
|
||||
def test_start_rejects_running_session_and_bad_module(ui):
|
||||
referee, port = ui
|
||||
def test_start_rejects_running_session_and_bad_module(webui):
|
||||
_referee, port = webui
|
||||
call(port, "POST", "/ui/api/start",
|
||||
{"module": "q3_practice", "robot_id": "2026001", "countdown_s": 5})
|
||||
status, body = call(port, "POST", "/ui/api/start",
|
||||
{"module": "q3_practice", "robot_id": "2026001"})
|
||||
status, _body = call(port, "POST", "/ui/api/start",
|
||||
{"module": "q3_practice", "robot_id": "2026001"})
|
||||
assert status == 409
|
||||
status, body = call(port, "POST", "/ui/api/start",
|
||||
{"module": "nope", "robot_id": "2026001"})
|
||||
status, _body = call(port, "POST", "/ui/api/start",
|
||||
{"module": "nope", "robot_id": "2026001"})
|
||||
assert status == 409
|
||||
call(port, "POST", "/ui/api/abort", {})
|
||||
|
||||
|
||||
def test_config_endpoints(ui):
|
||||
referee, port = ui
|
||||
def test_config_endpoints(webui):
|
||||
_referee, port = webui
|
||||
status, body = call(port, "POST", "/ui/api/config", {"display_limit": 100})
|
||||
assert body["configured"]["display_limit"] == 100
|
||||
status, body = call(port, "POST", "/ui/api/config", {"display_limit": 1})
|
||||
@@ -93,8 +95,8 @@ def test_config_endpoints(ui):
|
||||
assert body["configured"]["action_port"] == 8080
|
||||
|
||||
|
||||
def test_sessions_list_and_replay_source(ui):
|
||||
referee, port = ui
|
||||
def test_sessions_list_and_replay_source(webui):
|
||||
referee, port = webui
|
||||
call(port, "POST", "/ui/api/start",
|
||||
{"module": "q3_practice", "robot_id": "2026001",
|
||||
"countdown_s": 0.05, "window_s": 5, "seed": 11, "source_count": 10})
|
||||
@@ -125,8 +127,8 @@ def test_sessions_list_and_replay_source(ui):
|
||||
assert call(port, "GET", "/ui/api/replay?case=../etc")[0] == 400
|
||||
|
||||
|
||||
def test_static_page_served(ui):
|
||||
_, port = ui
|
||||
def test_static_page_served(webui):
|
||||
_, port = webui
|
||||
conn = http.client.HTTPConnection("127.0.0.1", port, timeout=5)
|
||||
conn.request("GET", "/")
|
||||
response = conn.getresponse()
|
||||
@@ -140,8 +142,8 @@ def test_static_page_served(ui):
|
||||
assert 'id="rp-stage"' in page
|
||||
|
||||
|
||||
def test_replay_script_served_with_file_import(ui):
|
||||
_, port = ui
|
||||
def test_replay_script_served_with_file_import(webui):
|
||||
_, port = webui
|
||||
conn = http.client.HTTPConnection("127.0.0.1", port, timeout=5)
|
||||
conn.request("GET", "/replay.js")
|
||||
response = conn.getresponse()
|
||||
|
||||
Reference in New Issue
Block a user