Underground-Digital/Workflow-Engine
0
1import time2import uuid3from urllib.parse import urlencode4 5import pytest6 7from core.app.entities.app_invoke_entities import InvokeFrom8from core.workflow.entities.variable_pool import VariablePool9from core.workflow.enums import SystemVariableKey10from core.workflow.graph_engine.entities.graph import Graph11from core.workflow.graph_engine.entities.graph_init_params import GraphInitParams12from core.workflow.graph_engine.entities.graph_runtime_state import GraphRuntimeState13from core.workflow.nodes.http_request.node import HttpRequestNode14from models.enums import UserFrom15from models.workflow import WorkflowType16from tests.integration_tests.workflow.nodes.__mock.http import setup_http_mock17 18 19def init_http_node(config: dict):20 graph_config = {21 "edges": [22 {23 "id": "start-source-next-target",24 "source": "start",25 "target": "1",26 },27 ],28 "nodes": [{"data": {"type": "start"}, "id": "start"}, config],29 }30 31 graph = Graph.init(graph_config=graph_config)32 33 init_params = GraphInitParams(34 tenant_id="1",35 app_id="1",36 workflow_type=WorkflowType.WORKFLOW,37 workflow_id="1",38 graph_config=graph_config,39 user_id="1",40 user_from=UserFrom.ACCOUNT,41 invoke_from=InvokeFrom.DEBUGGER,42 call_depth=0,43 )44 45 # construct variable pool46 variable_pool = VariablePool(47 system_variables={SystemVariableKey.FILES: [], SystemVariableKey.USER_ID: "aaa"},48 user_inputs={},49 environment_variables=[],50 conversation_variables=[],51 )52 variable_pool.add(["a", "b123", "args1"], 1)53 variable_pool.add(["a", "b123", "args2"], 2)54 55 return HttpRequestNode(56 id=str(uuid.uuid4()),57 graph_init_params=init_params,58 graph=graph,59 graph_runtime_state=GraphRuntimeState(variable_pool=variable_pool, start_at=time.perf_counter()),60 config=config,61 )62 63 64@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)65def test_get(setup_http_mock):66 node = init_http_node(67 config={68 "id": "1",69 "data": {70 "title": "http",71 "desc": "",72 "method": "get",73 "url": "http://example.com",74 "authorization": {75 "type": "api-key",76 "config": {77 "type": "basic",78 "api_key": "ak-xxx",79 "header": "api-key",80 },81 },82 "headers": "X-Header:123",83 "params": "A:b",84 "body": None,85 },86 }87 )88 89 result = node._run()90 assert result.process_data is not None91 data = result.process_data.get("request", "")92 93 assert "?A=b" in data94 assert "X-Header: 123" in data95 96 97@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)98def test_no_auth(setup_http_mock):99 node = init_http_node(100 config={101 "id": "1",102 "data": {103 "title": "http",104 "desc": "",105 "method": "get",106 "url": "http://example.com",107 "authorization": {108 "type": "no-auth",109 "config": None,110 },111 "headers": "X-Header:123",112 "params": "A:b",113 "body": None,114 },115 }116 )117 118 result = node._run()119 assert result.process_data is not None120 data = result.process_data.get("request", "")121 122 assert "?A=b" in data123 assert "X-Header: 123" in data124 125 126@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)127def test_custom_authorization_header(setup_http_mock):128 node = init_http_node(129 config={130 "id": "1",131 "data": {132 "title": "http",133 "desc": "",134 "method": "get",135 "url": "http://example.com",136 "authorization": {137 "type": "api-key",138 "config": {139 "type": "custom",140 "api_key": "Auth",141 "header": "X-Auth",142 },143 },144 "headers": "X-Header:123",145 "params": "A:b",146 "body": None,147 },148 }149 )150 151 result = node._run()152 assert result.process_data is not None153 data = result.process_data.get("request", "")154 155 assert "?A=b" in data156 assert "X-Header: 123" in data157 158 159@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)160def test_template(setup_http_mock):161 node = init_http_node(162 config={163 "id": "1",164 "data": {165 "title": "http",166 "desc": "",167 "method": "get",168 "url": "http://example.com/{{#a.b123.args2#}}",169 "authorization": {170 "type": "api-key",171 "config": {172 "type": "basic",173 "api_key": "ak-xxx",174 "header": "api-key",175 },176 },177 "headers": "X-Header:123\nX-Header2:{{#a.b123.args2#}}",178 "params": "A:b\nTemplate:{{#a.b123.args2#}}",179 "body": None,180 },181 }182 )183 184 result = node._run()185 assert result.process_data is not None186 data = result.process_data.get("request", "")187 188 assert "?A=b" in data189 assert "Template=2" in data190 assert "X-Header: 123" in data191 assert "X-Header2: 2" in data192 193 194@pytest.mark.parametrize("setup_http_mock", [["none"]], indirect=True)195def test_json(setup_http_mock):196 node = init_http_node(197 config={198 "id": "1",199 "data": {200 "title": "http",201 "desc": "",202 "method": "post",203 "url": "http://example.com",204 "authorization": {205 "type": "api-key",206 "config": {207 "type": "basic",208 "api_key": "ak-xxx",209 "header": "api-key",210 },211 },212 "headers": "X-Header:123",213 "params": "A:b",214 "body": {215 "type": "json",216 "data": [217 {218 "key": "",219 "type": "text",220 "value": '{"a": "{{#a.b123.args1#}}"}',221 },222 ],223 },224 },225 }226 )227 228 result = node._run()229 assert result.process_data is not None230 data = result.process_data.get("request", "")231 232 assert '{"a": "1"}' in data233 assert "X-Header: 123" in data234 235 236def test_x_www_form_urlencoded(setup_http_mock):237 node = init_http_node(238 config={239 "id": "1",240 "data": {241 "title": "http",242 "desc": "",243 "method": "post",244 "url": "http://example.com",245 "authorization": {246 "type": "api-key",247 "config": {248 "type": "basic",249 "api_key": "ak-xxx",250 "header": "api-key",251 },252 },253 "headers": "X-Header:123",254 "params": "A:b",255 "body": {256 "type": "x-www-form-urlencoded",257 "data": [258 {259 "key": "a",260 "type": "text",261 "value": "{{#a.b123.args1#}}",262 },263 {264 "key": "b",265 "type": "text",266 "value": "{{#a.b123.args2#}}",267 },268 ],269 },270 },271 }272 )273 274 result = node._run()275 assert result.process_data is not None276 data = result.process_data.get("request", "")277 278 assert "a=1&b=2" in data279 assert "X-Header: 123" in data280 281 282def test_form_data(setup_http_mock):283 node = init_http_node(284 config={285 "id": "1",286 "data": {287 "title": "http",288 "desc": "",289 "method": "post",290 "url": "http://example.com",291 "authorization": {292 "type": "api-key",293 "config": {294 "type": "basic",295 "api_key": "ak-xxx",296 "header": "api-key",297 },298 },299 "headers": "X-Header:123",300 "params": "A:b",301 "body": {302 "type": "form-data",303 "data": [304 {305 "key": "a",306 "type": "text",307 "value": "{{#a.b123.args1#}}",308 },309 {310 "key": "b",311 "type": "text",312 "value": "{{#a.b123.args2#}}",313 },314 ],315 },316 },317 }318 )319 320 result = node._run()321 assert result.process_data is not None322 data = result.process_data.get("request", "")323 324 assert 'form-data; name="a"' in data325 assert "1" in data326 assert 'form-data; name="b"' in data327 assert "2" in data328 assert "X-Header: 123" in data329 330 331def test_none_data(setup_http_mock):332 node = init_http_node(333 config={334 "id": "1",335 "data": {336 "title": "http",337 "desc": "",338 "method": "post",339 "url": "http://example.com",340 "authorization": {341 "type": "api-key",342 "config": {343 "type": "basic",344 "api_key": "ak-xxx",345 "header": "api-key",346 },347 },348 "headers": "X-Header:123",349 "params": "A:b",350 "body": {"type": "none", "data": []},351 },352 }353 )354 355 result = node._run()356 assert result.process_data is not None357 data = result.process_data.get("request", "")358 359 assert "X-Header: 123" in data360 assert "123123123" not in data361 362 363def test_mock_404(setup_http_mock):364 node = init_http_node(365 config={366 "id": "1",367 "data": {368 "title": "http",369 "desc": "",370 "method": "get",371 "url": "http://404.com",372 "authorization": {373 "type": "no-auth",374 "config": None,375 },376 "body": None,377 "params": "",378 "headers": "X-Header:123",379 },380 }381 )382 383 result = node._run()384 assert result.outputs is not None385 resp = result.outputs386 387 assert 404 == resp.get("status_code")388 assert "Not Found" in resp.get("body", "")389 390 391def test_multi_colons_parse(setup_http_mock):392 node = init_http_node(393 config={394 "id": "1",395 "data": {396 "title": "http",397 "desc": "",398 "method": "get",399 "url": "http://example.com",400 "authorization": {401 "type": "no-auth",402 "config": None,403 },404 "params": "Referer:http://example1.com\nRedirect:http://example2.com",405 "headers": "Referer:http://example3.com\nRedirect:http://example4.com",406 "body": {407 "type": "form-data",408 "data": [409 {410 "key": "Referer",411 "type": "text",412 "value": "http://example5.com",413 },414 {415 "key": "Redirect",416 "type": "text",417 "value": "http://example6.com",418 },419 ],420 },421 },422 }423 )424 425 result = node._run()426 assert result.process_data is not None427 assert result.outputs is not None428 resp = result.outputs429 430 assert urlencode({"Redirect": "http://example2.com"}) in result.process_data.get("request", "")431 assert 'form-data; name="Redirect"\r\n\r\nhttp://example6.com' in result.process_data.get("request", "")432 # assert "http://example3.com" == resp.get("headers", {}).get("referer")433 434 435def test_image_file(monkeypatch):436 from types import SimpleNamespace437 438 monkeypatch.setattr(439 "core.tools.tool_file_manager.ToolFileManager.create_file_by_raw",440 lambda *args, **kwargs: SimpleNamespace(id="1"),441 )442 443 node = init_http_node(444 config={445 "id": "1",446 "data": {447 "title": "http",448 "desc": "",449 "method": "get",450 "url": "https://cloud.dify.ai/logo/logo-site.png",451 "authorization": {452 "type": "no-auth",453 "config": None,454 },455 "params": "",456 "headers": "",457 "body": None,458 },459 }460 )461 462 result = node._run()463 assert result.process_data is not None464 assert result.outputs is not None465 resp = result.outputs466 assert len(resp.get("files", [])) == 1467 