lct-hack/backend/tests/test_dds_desk.py
gglamer 5a3761a830 refactor: пульт ДДС владеет живыми карточками, сессия не копирует их поля
Связка 112→ДДС больше не подменяет таймеры оператора таймерами первой
карточки ДДС, поэтому время заполнения карточки 112 снова попадает в оценку.
2026-09-26 21:38:09 +00:00

127 lines
4.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Пульт ДДС без сокетов и хаба: очередь, переключение, завершение, темп."""
from datetime import timedelta
from pathlib import Path
from uuid import uuid4
from app.domain.statuses import ServiceStatus
from app.domain.timers import TimerCode
from app.scenarios.loader import load_file
from app.session.dds import DdsCardRecord, DdsDesk
from app.session.timers import SessionTimers, now_utc
LIBRARY = Path(__file__).resolve().parents[2] / "scenarios"
NAMES = ("fire-apartment-l2.yaml", "tickets/t20-2-stroke.yaml",
"tickets/t01-1-fire-container.yaml")
def scenarios(count: int = 3):
return [load_file(LIBRARY / name, LIBRARY) for name in NAMES[:count]]
def record(card) -> DdsCardRecord:
return DdsCardRecord(card_id=card.card_id, scenario_id=card.scenario.id, reply_text="",
metrics=[], findings=[], actions=[], duration_ms=0)
def started(interval: int = 0, max_waiting: int = 3, now=None) -> DdsDesk:
desk = DdsDesk()
desk.start(scenarios(), limits=SessionTimers().limits,
arrival_interval_seconds=interval, max_waiting=max_waiting, now=now or now_utc())
return desk
def test_first_card_becomes_active_and_rest_wait_when_no_pace():
desk = started()
assert [card.scenario.id for card in desk.ordered()] == [
"fire-apartment-l2", "t20-2-stroke", "t01-1-fire-container"]
assert desk.active is desk.ordered()[0]
assert desk.next_arrival_at is None
def test_paced_queue_delivers_next_card_only_after_interval():
start = now_utc()
desk = started(interval=60, now=start)
first = desk.active
assert desk.deliver_due(start + timedelta(seconds=59)) == 0
assert desk.deliver_due(start + timedelta(seconds=61)) == 1
assert len(desk.cards) == 2
assert desk.active is first
def test_waiting_cap_holds_back_delivery():
desk = started(max_waiting=1)
assert len(desk.cards) == 2
assert desk.next_index == 2
def test_open_switches_by_reference_without_copying_fields():
desk = started()
first, second = desk.ordered()[:2]
first.status_log.append(object())
assert desk.open(second.card_id)
second.reply_text = "ответ по второй"
assert desk.open(first.card_id)
assert desk.active is first
assert len(first.status_log) == 1 and second.status_log == []
assert second.reply_text == "ответ по второй" and first.reply_text == ""
assert desk.card_index == 0
def test_open_unknown_card_keeps_active():
desk = started()
active = desk.active
assert not desk.open(uuid4())
assert desk.active is active
def test_complete_moves_to_following_card_and_keeps_record():
desk = started()
first, second, _third = desk.ordered()
following = desk.complete(first.card_id, record(first))
assert following is second and desk.active is second
assert first.card_id not in desk.cards
assert desk.is_completed(first.card_id)
def test_completing_last_card_leaves_desk_between_cards_with_last_index():
desk = started(interval=60, max_waiting=1)
only = desk.active
assert desk.complete(only.card_id, record(only)) is None
assert desk.active is None and desk.active_id is None
assert desk.card_index == 0
assert desk.deliver_due(now_utc() + timedelta(seconds=61)) == 1
assert desk.active.scenario.id == "t20-2-stroke"
def test_each_card_runs_own_decision_timer_with_configured_limit():
desk = DdsDesk()
limits = {**SessionTimers().limits, TimerCode.DDS_ACK: 45_000}
desk.start(scenarios(2), limits=limits, now=now_utc())
first, second = desk.ordered()
assert first.timers is not second.timers
assert first.timers.limits[TimerCode.DDS_ACK] == 45_000
assert TimerCode.DDS_ACK in second.timers.timers
def test_card_status_mark_lands_in_that_card_only():
desk = started()
first, second = desk.ordered()[:2]
service = first.dispatched_card.notify[0]
first.set_service_status(service, ServiceStatus.ACCEPTED, "Основание: доклад")
assert [mark.status for mark in first.status_log] == [ServiceStatus.ACCEPTED]
assert second.status_log == []