"""lookup / merge_json / read_url / prompt – nya steg i rpa/flow_engine.py.""" import json import pytest from rpa import flow_engine from rpa.flow_engine import FlowError, _execute_step def run_step(step, vars_, *, page=None): _execute_step(step, step["type"], page, lambda _m: None, vars_, None, None, {"session": None}) return vars_ # --- lookup ------------------------------------------------------------- TABLE = {"MEDS Gastro|IBD": {"team": "MAGMOT", "orsak": "IBD"}, "X|Y": "platt"} def test_lookup_from_var_ci_and_dict_result(): v = run_step( {"type": "lookup", "from_var": "cfg", "key": "{g}|{s}", "var": "row", "ci": True}, {"cfg": TABLE, "g": "meds gastro", "s": "ibd"}, ) assert v["row"] == {"team": "MAGMOT", "orsak": "IBD"} # dict-resultat -> {row.field} funkar i efterföljande steg run_step({"type": "set_var", "var": "t", "text": "{row.team}"}, v) assert v["t"] == "MAGMOT" def test_lookup_case_sensitive_miss(): with pytest.raises(FlowError, match="ingen rad"): run_step({"type": "lookup", "from_var": "cfg", "key": "x|y", "var": "r", "ci": "false"}, {"cfg": TABLE}) def test_lookup_on_missing_empty_and_default(): v = run_step({"type": "lookup", "from_var": "cfg", "key": "nope", "var": "r", "on_missing": "empty"}, {"cfg": TABLE}) assert v["r"] == "" v = run_step({"type": "lookup", "from_var": "cfg", "key": "nope", "var": "r", "on_missing": "MED"}, {"cfg": TABLE}) assert v["r"] == "MED" def test_lookup_from_file(tmp_path, monkeypatch): monkeypatch.setattr(flow_engine.config, "EXPORT_DIR", tmp_path) (tmp_path / "m.json").write_text(json.dumps(TABLE), encoding="utf-8") v = run_step({"type": "lookup", "file": "m.json", "key": "X|Y", "var": "r"}, {}) assert v["r"] == "platt" def test_lookup_requires_key_and_var(): with pytest.raises(FlowError): run_step({"type": "lookup", "from_var": "cfg", "var": "r"}, {"cfg": TABLE}) def test_lookup_dotted_from_var_walks_nested_dict(): cfg = {"elvis": {"translationTable": TABLE}} v = run_step( {"type": "lookup", "from_var": "cfg.elvis.translationTable", "key": "X|Y", "var": "r"}, {"cfg": cfg}, ) assert v["r"] == "platt" # --- stop ------------------------------------------------------------ def test_stop_raises_interpolated_message(): with pytest.raises(FlowError, match="Gruppen 'MEDS Obesitas' hanteras inte"): run_step({"type": "stop", "message": "Gruppen '{g}' hanteras inte"}, {"g": "MEDS Obesitas"}) # --- read_url pattern ------------------------------------------------ def test_read_url_pattern_extracts_id(): page = type("P", (), {"url": "https://salma.vgregion.se/incident/1269212?x=1"})() v = run_step({"type": "read_url", "var": "nr", "pattern": r"/incident/(?P\d+)"}, {}, page=page) assert v["nr"] == "1269212" v = run_step({"type": "read_url", "var": "nr2", "pattern": r"\d{5,8}"}, {}, page=page) assert v["nr2"] == "1269212" # --- merge_json -------------------------------------------------------- def test_merge_json_creates_and_updates(tmp_path, monkeypatch): monkeypatch.setattr(flow_engine.config, "EXPORT_DIR", tmp_path) run_step({"type": "merge_json", "file": "reg.json", "key": "A", "value": "1"}, {}) run_step({"type": "merge_json", "file": "reg.json", "key": "{k}", "value": "{val}"}, {"k": "B", "val": "2"}) data = json.loads((tmp_path / "reg.json").read_text(encoding="utf-8")) assert data == {"A": "1", "B": "2"} def test_merge_json_rejects_path(monkeypatch, tmp_path): monkeypatch.setattr(flow_engine.config, "EXPORT_DIR", tmp_path) with pytest.raises(FlowError, match="Ogiltigt filnamn"): run_step({"type": "merge_json", "file": "../evil.json", "key": "a", "value": "b"}, {}) # --- read_url -------------------------------------------------------- def test_read_url(): page = type("P", (), {"url": "https://salma.vgregion.se/incident/12345"})() v = run_step({"type": "read_url", "var": "u"}, {}, page=page) assert v["u"].endswith("/12345") # --- prompt (subprocess mockad) -------------------------------------- def _fake_proc(stdout): return type("R", (), {"stdout": stdout, "stderr": "", "returncode": 0})() def test_prompt_confirm_ok(monkeypatch): monkeypatch.setattr(flow_engine.subprocess, "run", lambda *a, **k: _fake_proc('{"ok": true}')) run_step({"type": "prompt", "mode": "confirm", "message": "Fortsätt?"}, {}) # ingen exception def test_prompt_confirm_cancel_aborts(monkeypatch): monkeypatch.setattr(flow_engine.subprocess, "run", lambda *a, **k: _fake_proc('{"ok": false}')) with pytest.raises(FlowError, match="Avbrutet av användaren"): run_step({"type": "prompt", "mode": "confirm", "message": "x"}, {}) def test_prompt_input_sets_var(monkeypatch): monkeypatch.setattr(flow_engine.subprocess, "run", lambda *a, **k: _fake_proc('{"ok": true, "value": "99999-999"}')) v = run_step({"type": "prompt", "mode": "input", "message": "Nummer?", "var": "nr"}, {}) assert v["nr"] == "99999-999" def test_prompt_fields_writes_back_edited_values(monkeypatch): seen = {} def fake_run(*a, **k): seen["input"] = json.loads(k["input"]) return _fake_proc('{"ok": true, "values": {"pnr": "19850101-1234", "datum": "20260115"}}') monkeypatch.setattr(flow_engine.subprocess, "run", fake_run) v = run_step( {"type": "prompt", "mode": "fields", "message": "Kontrollera", "fields": "pnr, datum"}, {"pnr": "1985010I-1234", "datum": "20260115"}, ) assert seen["input"]["fields"] == ["pnr", "datum"] assert v["pnr"] == "19850101-1234" # användaren rättade OCR-felet def test_prompt_interpolates_message(monkeypatch): seen = {} monkeypatch.setattr(flow_engine.subprocess, "run", lambda *a, **k: (seen.update(json.loads(k["input"])), _fake_proc('{"ok": true}'))[1]) run_step({"type": "prompt", "mode": "confirm", "message": "Spara {namn}?"}, {"namn": "Anna"}) assert seen["message"] == "Spara Anna?"