import asyncio import pytest from app.services.workflow_engine import WorkflowEngine def _engine_with(nodes, edges=None): wf_data = {"nodes": nodes, "edges": edges or []} return WorkflowEngine(workflow_id="wf_all", workflow_data=wf_data) @pytest.mark.asyncio async def test_switch_branch(): node = { "id": "sw1", "type": "switch", "data": {"field": "status", "cases": {"ok": "ok_handle"}, "default": "def"}, } engine = _engine_with([node]) res = await engine.execute_node(node, {"status": "ok"}) assert res["status"] == "success" assert res["branch"] == "ok_handle" @pytest.mark.asyncio async def test_merge_array_strategy(): node = {"id": "m1", "type": "merge", "data": {"strategy": "array"}} engine = _engine_with([node]) res = await engine.execute_node(node, {"a": 1, "b": 2}) assert res["status"] == "success" assert isinstance(res["output"], list) assert len(res["output"]) == 2 @pytest.mark.asyncio async def test_wait_time_mode(): node = { "id": "w1", "type": "wait", "data": {"wait_type": "time", "wait_seconds": 0.01}, } engine = _engine_with([node]) res = await engine.execute_node(node, {"ping": True}) assert res["status"] == "success" assert res["output"]["ping"] is True @pytest.mark.asyncio async def test_json_parse_and_extract(): node = { "id": "j1", "type": "json", "data": {"operation": "extract", "path": "$.data.value"}, } engine = _engine_with([node]) res = await engine.execute_node(node, {"data": {"value": 42}}) assert res["status"] == "success" assert res["output"] == 42 @pytest.mark.asyncio async def test_text_split(): node = { "id": "t1", "type": "text", "data": {"operation": "split", "delimiter": ","}, } engine = _engine_with([node]) res = await engine.execute_node(node, "a,b,c") assert res["status"] == "success" assert res["output"] == ["a", "b", "c"] @pytest.mark.asyncio async def test_cache_set_then_get(): node_set = { "id": "cset", "type": "cache", "data": {"operation": "set", "key": "k1", "ttl": 1}, } node_get = { "id": "cget", "type": "cache", "data": {"operation": "get", "key": "k1", "ttl": 1}, } engine = _engine_with([node_set, node_get]) await engine.execute_node(node_set, {"value": "v"}) res_get = await engine.execute_node(node_get, {}) assert res_get["status"] == "success" assert res_get["output"] == "v" assert res_get["cache_hit"] is True @pytest.mark.asyncio async def test_vector_db_upsert_search_delete(): node = { "id": "vec", "type": "vector_db", "data": {"operation": "upsert", "collection": "col"}, } engine = _engine_with([node]) up = await engine.execute_node(node, {"embedding": [1.0, 0.0], "text": "hi"}) assert up["status"] == "success" node_search = { "id": "vecs", "type": "vector_db", "data": { "operation": "search", "collection": "col", "query_vector": [1.0, 0.0], "top_k": 1, }, } res = await engine.execute_node(node_search, {}) assert res["status"] == "success" assert len(res["output"]) == 1 node_del = { "id": "vecd", "type": "vector_db", "data": {"operation": "delete", "collection": "col"}, } del_res = await engine.execute_node(node_del, {}) assert del_res["status"] == "success" @pytest.mark.asyncio async def test_log_basic(): node = { "id": "log1", "type": "log", "data": {"level": "info", "message": "hello {x}", "include_data": False}, } engine = _engine_with([node]) res = await engine.execute_node(node, {"x": 1}) assert res["status"] == "success" assert res["log"]["message"].startswith("节点执行") or res["log"]["message"].startswith("hello") @pytest.mark.asyncio async def test_error_handler_notify(): node = { "id": "err1", "type": "error_handler", "data": {"on_error": "notify"}, } engine = _engine_with([node]) res = await engine.execute_node(node, {"status": "failed", "error": "boom"}) assert res["status"] == "error_handled" assert res["error"] == "boom" @pytest.mark.asyncio async def test_csv_parse_and_generate(): node_parse = { "id": "csvp", "type": "csv", "data": {"operation": "parse", "delimiter": ",", "headers": True}, } engine = _engine_with([node_parse]) csv_text = "a,b\n1,2\n" res = await engine.execute_node(node_parse, csv_text) assert res["status"] == "success" assert res["output"][0]["a"] == "1" node_gen = { "id": "csvg", "type": "csv", "data": {"operation": "generate", "delimiter": ",", "headers": True}, } res_gen = await engine.execute_node(node_gen, [{"a": 1, "b": 2}]) assert res_gen["status"] == "success" assert "a,b" in res_gen["output"] @pytest.mark.asyncio async def test_object_storage_upload_download(): node_up = { "id": "osup", "type": "object_storage", "data": { "operation": "upload", "provider": "s3", "bucket": "bk", "key": "file.txt", }, } engine = _engine_with([node_up]) res_up = await engine.execute_node(node_up, {"file": "data"}) assert res_up["status"] == "success" assert res_up["output"]["status"] == "uploaded" node_down = { "id": "osdown", "type": "object_storage", "data": { "operation": "download", "provider": "s3", "bucket": "bk", "key": "file.txt", }, } res_down = await engine.execute_node(node_down, {}) assert res_down["status"] == "success" assert res_down["output"]["status"] == "downloaded" # 集成/外部依赖重的节点标记跳过,避免网络/编译/二进制依赖 heavy_nodes = [ "llm", "agent", "http", "webhook", "email", "message_queue", "database", "file", "pdf", "image", "excel", "slack", "dingtalk", "wechat_work", "sms", ] @pytest.mark.skip(reason="重依赖/外部IO,保留集成测试") @pytest.mark.asyncio async def test_heavy_nodes_placeholder(): assert True