mirror of
https://github.com/deepmodeling/Uni-Lab-OS
synced 2026-05-25 09:35:58 +00:00
138 lines
5.6 KiB
Python
138 lines
5.6 KiB
Python
"""P9 — ``_augment_states_with_liquid_history`` 单元测试(OS→Cloud sync 链路 Phase C)。
|
||
|
||
详见 ``product_designs/protocol_convert/09-liquid-history-unknown-debug.md`` §6.3 / §8 T4。
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
from dataclasses import dataclass, field
|
||
from typing import Any, Dict, List
|
||
|
||
import pytest
|
||
|
||
from unilabos.resources.resource_tracker import _augment_states_with_liquid_history
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Fixtures:纯 dataclass 模拟 PLR 资源树(避免引入 PLR 真实实例化)
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
@dataclass
|
||
class FakeTracker:
|
||
liquid_history: Any = field(default_factory=list)
|
||
|
||
|
||
@dataclass
|
||
class FakeResource:
|
||
name: str
|
||
tracker: Any = None
|
||
children: List["FakeResource"] = field(default_factory=list)
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# Tests
|
||
# ---------------------------------------------------------------------------
|
||
|
||
|
||
class TestAugmentStatesWithLiquidHistory:
|
||
def test_single_well_history_attached(self) -> None:
|
||
well = FakeResource("well_A1", tracker=FakeTracker(liquid_history=[
|
||
{"name": "Plasma", "volume": 100, "action": "set"}
|
||
]))
|
||
states: Dict[str, Any] = {"well_A1": {"liquids": [], "pending_liquids": []}}
|
||
|
||
_augment_states_with_liquid_history(well, states)
|
||
|
||
assert "liquid_history" in states["well_A1"]
|
||
assert states["well_A1"]["liquid_history"] == [
|
||
{"name": "Plasma", "volume": 100, "action": "set"}
|
||
]
|
||
|
||
def test_recursive_walk_attaches_to_all_wells(self) -> None:
|
||
"""resource 树有多层时,每个有 tracker 的节点都会被并入 states。"""
|
||
wells = [
|
||
FakeResource(f"well_{i}", tracker=FakeTracker(liquid_history=[
|
||
{"name": f"L_{i}", "volume": i * 10, "action": "set"}
|
||
]))
|
||
for i in range(3)
|
||
]
|
||
plate = FakeResource("plate", children=wells)
|
||
deck = FakeResource("deck", children=[plate])
|
||
states: Dict[str, Any] = {
|
||
"deck": {"liquids": []},
|
||
"plate": {"liquids": []},
|
||
"well_0": {"liquids": []},
|
||
"well_1": {"liquids": []},
|
||
"well_2": {"liquids": []},
|
||
}
|
||
|
||
_augment_states_with_liquid_history(deck, states)
|
||
|
||
assert states["well_0"]["liquid_history"] == [{"name": "L_0", "volume": 0, "action": "set"}]
|
||
assert states["well_1"]["liquid_history"] == [{"name": "L_1", "volume": 10, "action": "set"}]
|
||
assert states["well_2"]["liquid_history"] == [{"name": "L_2", "volume": 20, "action": "set"}]
|
||
|
||
def test_no_tracker_node_skipped(self) -> None:
|
||
"""没有 tracker 的节点(如 deck 自身)跳过,state dict 不被污染。"""
|
||
deck = FakeResource("deck") # tracker=None
|
||
states: Dict[str, Any] = {"deck": {"some_field": 1}}
|
||
|
||
_augment_states_with_liquid_history(deck, states)
|
||
|
||
assert "liquid_history" not in states["deck"]
|
||
|
||
def test_existing_liquid_history_in_state_not_overwritten(self) -> None:
|
||
"""state 已经有 liquid_history 字段(例如 PLR 升级未来支持了)→ 不覆盖。"""
|
||
well = FakeResource("well_A1", tracker=FakeTracker(liquid_history=[
|
||
{"name": "Plasma", "volume": 100, "action": "set"}
|
||
]))
|
||
states: Dict[str, Any] = {"well_A1": {"liquid_history": ["preexisting"]}}
|
||
|
||
_augment_states_with_liquid_history(well, states)
|
||
|
||
assert states["well_A1"]["liquid_history"] == ["preexisting"]
|
||
|
||
def test_history_is_shallow_copied(self) -> None:
|
||
"""augment 后的 history 应是独立 list(避免运行时 mutate 污染 dump 结果)。"""
|
||
original_history = [{"name": "X", "volume": 1, "action": "set"}]
|
||
well = FakeResource("well_A1", tracker=FakeTracker(liquid_history=original_history))
|
||
states: Dict[str, Any] = {"well_A1": {}}
|
||
|
||
_augment_states_with_liquid_history(well, states)
|
||
|
||
# mutate runtime history 不应反映到 augmented state
|
||
original_history.append({"name": "Y", "volume": 2, "action": "set"})
|
||
assert len(states["well_A1"]["liquid_history"]) == 1
|
||
|
||
def test_node_not_in_states_silently_skipped(self) -> None:
|
||
"""resource 树中的节点 name 不在 ``states`` 字典里 → 静默跳过。"""
|
||
well = FakeResource("well_orphan", tracker=FakeTracker(liquid_history=[
|
||
{"name": "X", "volume": 1, "action": "set"}
|
||
]))
|
||
states: Dict[str, Any] = {"well_A1": {}}
|
||
|
||
_augment_states_with_liquid_history(well, states)
|
||
|
||
# 不应该新增 well_orphan 键,也不应污染 well_A1
|
||
assert "well_orphan" not in states
|
||
assert "liquid_history" not in states["well_A1"]
|
||
|
||
def test_non_list_liquid_history_skipped(self) -> None:
|
||
"""tracker.liquid_history 非 list 时(异常情况)→ 跳过,不写入 state。"""
|
||
well = FakeResource("well_A1", tracker=FakeTracker(liquid_history="broken"))
|
||
states: Dict[str, Any] = {"well_A1": {}}
|
||
|
||
_augment_states_with_liquid_history(well, states)
|
||
|
||
assert "liquid_history" not in states["well_A1"]
|
||
|
||
def test_empty_history_still_written(self) -> None:
|
||
"""tracker.liquid_history = [] 是合法状态 → 应写入空 list(表示"未有任何液体操作")。"""
|
||
well = FakeResource("well_A1", tracker=FakeTracker(liquid_history=[]))
|
||
states: Dict[str, Any] = {"well_A1": {}}
|
||
|
||
_augment_states_with_liquid_history(well, states)
|
||
|
||
assert states["well_A1"]["liquid_history"] == []
|