🚧 WIP
This commit is contained in:
+136
-1
@@ -313,4 +313,139 @@ class TestModuleConfig:
|
||||
assert r.status_code == 200
|
||||
# write an output via the internal dispatch and read the mirror
|
||||
app.datastore.write_words(0x0000, [0x1234])
|
||||
assert app.datastore.read_words(0x0200, 1) == [0x1234]
|
||||
assert app.datastore.read_words(0x0200, 1) == [0x1234]
|
||||
|
||||
|
||||
class TestChannelNames:
|
||||
async def test_names_in_layout_and_values(self, client, app):
|
||||
r = await client.put("/api/config/channel-names", json={
|
||||
"channel_names": {"DI0.0": "start_button", "AI4.0": "tank_level"},
|
||||
"persist": False,
|
||||
})
|
||||
assert r.status_code == 200
|
||||
# layout carries the name on the right channel
|
||||
r = await client.get("/api/node")
|
||||
di0 = next(c for m in r.json()["modules"] if m["index"] == 0
|
||||
for c in m["channels"] if c["address"] == "DI0.0")
|
||||
assert di0["channel_name"] == "start_button"
|
||||
# values snapshot carries it too
|
||||
r = await client.get("/api/values")
|
||||
entry = next(v for v in r.json()["values"]
|
||||
if (v["kind"], v["module"], v["channel"]) == ("ai", 4, 0))
|
||||
assert entry["name"] == "tank_level"
|
||||
|
||||
async def test_names_persist_to_yaml(self, client, app, tmp_path):
|
||||
cfg_file = tmp_path / "node.yaml"
|
||||
app.node_config_path = str(cfg_file)
|
||||
r = await client.put("/api/config/channel-names", json={
|
||||
"channel_names": {"DO2.0": "pump_relay"}, "persist": True,
|
||||
})
|
||||
assert r.status_code == 200
|
||||
text = cfg_file.read_text(encoding="utf-8")
|
||||
assert "channel_names:" in text
|
||||
assert "DO2.0: pump_relay" in text
|
||||
# and it loads back
|
||||
from wago_sim.scenario.dsl import load_yaml, load_channel_names
|
||||
names = load_channel_names(load_yaml(cfg_file))
|
||||
assert names == {"DO2.0": "pump_relay"}
|
||||
|
||||
async def test_invalid_code_rejected(self, client):
|
||||
r = await client.put("/api/config/channel-names", json={
|
||||
"channel_names": {"XX0.0": "nope"}, "persist": False,
|
||||
})
|
||||
assert r.status_code == 400
|
||||
assert "invalid I/O code" in r.json()["detail"]
|
||||
|
||||
async def test_names_survive_module_save(self, client, app, tmp_path):
|
||||
"""Module SAVE CONFIG must not wipe channel names (they persist)."""
|
||||
app.node_config_path = str(tmp_path / "node.yaml")
|
||||
await client.put("/api/config/channel-names", json={
|
||||
"channel_names": {"DI0.0": "start_button"}, "persist": True,
|
||||
})
|
||||
r = await client.put("/api/config/modules", json={
|
||||
"modules": ["750-430", "750-530"], "persist": True,
|
||||
})
|
||||
assert r.status_code == 200
|
||||
assert app.node.channel_names.get("DI0.0") == "start_button"
|
||||
# YAML contains both sections
|
||||
text = (tmp_path / "node.yaml").read_text(encoding="utf-8")
|
||||
assert "DI0.0: start_button" in text
|
||||
|
||||
async def test_readonly_blocks_rename(self, app):
|
||||
ro = build_webui(app, user="admin", password="simu", readonly=True)
|
||||
transport = httpx.ASGITransport(app=ro)
|
||||
async with httpx.AsyncClient(transport=transport, base_url="http://test") as c:
|
||||
await c.post("/login", json={"user": "admin", "password": "simu"})
|
||||
r = await c.put("/api/config/channel-names", json={"channel_names": {}})
|
||||
assert r.status_code == 403
|
||||
|
||||
|
||||
class TestConfigExportImport:
|
||||
async def test_export_shape(self, client):
|
||||
r = await client.get("/api/config/export")
|
||||
assert r.status_code == 200
|
||||
data = r.json()
|
||||
assert data["format"] == "wago-sim-config"
|
||||
assert data["version"] == 1
|
||||
assert len(data["modules"]) == 6
|
||||
assert "coupler_settings" in data
|
||||
assert "channel_names" in data
|
||||
|
||||
async def test_export_import_roundtrip(self, client, app, tmp_path):
|
||||
# configure something recognizable
|
||||
await client.put("/api/config/channel-names", json={
|
||||
"channel_names": {"DI0.0": "start_button"}, "persist": False,
|
||||
})
|
||||
await client.put("/api/config/coupler", json={"response_delay_ms": 33})
|
||||
exported = (await client.get("/api/config/export")).json()
|
||||
|
||||
app.node_config_path = str(tmp_path / "node.yaml")
|
||||
r = await client.post("/api/config/import", json=exported)
|
||||
assert r.status_code == 200
|
||||
body = r.json()
|
||||
assert body["ok"] is True
|
||||
assert "750-430" in body["modules"]
|
||||
# names and coupler settings applied
|
||||
assert app.node.channel_names.get("DI0.0") == "start_button"
|
||||
assert app.registers.read_config(0x1037, 1) == [33]
|
||||
# persisted to YAML
|
||||
text = (tmp_path / "node.yaml").read_text(encoding="utf-8")
|
||||
assert "DI0.0: start_button" in text
|
||||
|
||||
async def test_import_rejects_bad_payload(self, client, app):
|
||||
# wrong format marker
|
||||
r = await client.post("/api/config/import", json={"format": "nope"})
|
||||
assert r.status_code == 400
|
||||
# unknown article: nothing applied
|
||||
before = [m.article for m in app.node.modules]
|
||||
r = await client.post("/api/config/import", json={
|
||||
"format": "wago-sim-config", "modules": ["750-9999"],
|
||||
"channel_names": {}, "coupler_settings": {},
|
||||
})
|
||||
assert r.status_code == 400
|
||||
assert [m.article for m in app.node.modules] == before # untouched
|
||||
|
||||
async def test_import_drops_stale_name_codes(self, client, app):
|
||||
# names for channels that don't exist on the imported layout
|
||||
r = await client.post("/api/config/import", json={
|
||||
"format": "wago-sim-config",
|
||||
"modules": ["750-530"],
|
||||
"channel_names": {"DO0.0": "pump", "DI0.0": "stale_name"},
|
||||
"coupler_settings": {},
|
||||
"persist": False,
|
||||
})
|
||||
assert r.status_code == 200
|
||||
body = r.json()
|
||||
assert body["dropped_codes"] == ["DI0.0"]
|
||||
assert app.node.channel_names == {"DO0.0": "pump"}
|
||||
|
||||
async def test_readonly_blocks_import_export(self, app):
|
||||
ro = build_webui(app, user="admin", password="simu", readonly=True)
|
||||
transport = httpx.ASGITransport(app=ro)
|
||||
async with httpx.AsyncClient(transport=transport, base_url="http://test") as c:
|
||||
await c.post("/login", json={"user": "admin", "password": "simu"})
|
||||
r = await c.get("/api/config/export")
|
||||
assert r.status_code == 200 # export is a read
|
||||
r = await c.post("/api/config/import", json={"format": "wago-sim-config",
|
||||
"modules": ["750-430"]})
|
||||
assert r.status_code == 403
|
||||
Reference in New Issue
Block a user