"""Migrering: gammalt nästlat {steps} -> nod/kant-graf, som sedan körs och ger samma variabelutfall som den gamla listmotorn gav.""" import pytest from rpa import graph_engine, migrate from rpa.flow_engine import FlowError def run_steps(steps, vars_=None): g = migrate.steps_to_graph(steps) v = dict(vars_ or {}) logs: list[str] = [] graph_engine.run_graph( g["nodes"], g["edges"], logs.append, page=None, download_dir=None, vars_=v, desktop_holder={"session": None}, start=g["start"], ) return v, g, logs def test_linear_steps_preserve_order_and_ids(): v, g, _ = run_steps([ {"type": "set_var", "id": "s1", "var": "x", "text": "1"}, {"type": "increment", "id": "s2", "var": "x", "by": 2}, ]) assert [n["id"] for n in g["nodes"]] == ["s1", "s2"] assert g["start"] == "s1" assert v["x"] == 3 def test_if_else_merges_and_continues(): steps = [ {"type": "set_var", "var": "r", "text": "start"}, {"type": "if", "branches": [ {"condition": {"kind": "var_equals", "var": "läge", "text": "prod"}, "steps": [{"type": "set_var", "var": "r", "text": "PROD"}]}, ], "else": [{"type": "set_var", "var": "r", "text": "ELSE"}]}, {"type": "set_var", "var": "efter", "text": "körd"}, ] for läge, expected in (("prod", "PROD"), ("test", "ELSE")): v, _, _ = run_steps(steps, {"läge": läge}) assert v["r"] == expected assert v["efter"] == "körd" # båda grenar mynnar i efter-noden def test_if_without_else_falls_through(): steps = [ {"type": "if", "branches": [ {"condition": {"kind": "var_equals", "var": "x", "text": "1"}, "steps": [{"type": "set_var", "var": "r", "text": "träff"}]}, ]}, {"type": "set_var", "var": "efter", "text": "körd"}, ] v, _, _ = run_steps(steps, {"x": "0"}) assert "r" not in v and v["efter"] == "körd" def test_loop_body_loops_back_and_done_continues(): steps = [ {"type": "loop", "over": "poster", "item_var": "it", "index_var": "i", "steps": [ {"type": "increment", "var": "antal", "by": 1}, {"type": "set_var", "var": "sista", "text": "{it}"}, ]}, {"type": "save_var", "var": "sista", "filename": "x.json"}, ] v, g, _ = run_steps(steps, {"poster": ["a", "b"], "antal": "0"}) assert v["antal"] == 2 and v["sista"] == "b" and v["i"] == 2 # loop-noden har en each-kant och en done-kant, kroppen loopar tillbaka till in loop_id = next(n["id"] for n in g["nodes"] if n["type"] == "loop") handles = {e["sourceHandle"] for e in g["edges"] if e["source"] == loop_id} assert {"each", "done"} <= handles assert any(e["target"] == loop_id for e in g["edges"]) # loopback def test_jump_to_label_becomes_a_plain_edge(): steps = [ {"type": "set_var", "id": "a", "var": "r", "text": "1"}, {"type": "jump_to_label", "target": "slut"}, {"type": "set_var", "id": "skip", "var": "r", "text": "HOPPAD"}, {"type": "set_var", "id": "b", "var": "r", "text": "2", "label": "slut"}, ] v, g, _ = run_steps(steps) assert "jump_to_label" not in {n["type"] for n in g["nodes"]} assert v["r"] == "2" # 'skip'-noden nås aldrig def test_flat_node_drops_container_keys(): g = migrate.steps_to_graph([ {"type": "loop", "over": "x", "steps": [{"type": "wait", "seconds": 0}]}, {"type": "if", "branches": [{"condition": {"kind": "var_equals", "var": "a", "text": "b"}, "steps": [{"type": "wait", "seconds": 0}]}], "else": []}, ]) for n in g["nodes"]: assert "steps" not in n if n["type"] == "if": assert all(set(b) == {"condition"} for b in n["branches"]) else: assert "branches" not in n def test_flow_to_graph_is_noop_for_graph_form(): graph = {"nodes": [{"id": "a", "type": "wait", "seconds": 1}], "edges": []} assert migrate.flow_to_graph(graph) is graph