def test_webhook_throws_informative_error_if_flow_script_path_not_set( sample_flow): webhook = Webhook( build_request_kwargs={ "url": "https://content.dropboxapi.com/2/files", "headers": { "Content-Type": "application/octet-stream" }, }, build_request_http_method="POST", get_flow_request_kwargs={ "url": "https://content.dropboxapi.com/2/files", "headers": { "Accept": "application/octet-stream" }, }, get_flow_request_http_method="GET", stored_as_script=True, ) webhook.add_flow(sample_flow) error_msg = "flow_script_path must be provided if stored_as_script=True" with pytest.raises(RuntimeError, match=error_msg): res = webhook.build() assert isinstance(res, Webhook)
def test_webhook_throws_informative_error_if_flow_script_file_does_not_exist( sample_flow, ): nonexistent_file = "{}.py".format(str(uuid.uuid4())) webhook = Webhook( build_request_kwargs={ "url": "https://content.dropboxapi.com/2/files", "headers": { "Content-Type": "application/octet-stream" }, }, build_request_http_method="POST", get_flow_request_kwargs={ "url": "https://content.dropboxapi.com/2/files", "headers": { "Accept": "application/octet-stream" }, }, get_flow_request_http_method="GET", stored_as_script=True, flow_script_path=nonexistent_file, ) webhook.add_flow(sample_flow) error_msg = "passed to flow_script_path does not exist" with pytest.raises(RuntimeError, match=error_msg): res = webhook.build() assert isinstance(res, Webhook)
def test_webhook_raises_error_on_get_flow_failure(sample_flow): webhook = Webhook( build_request_kwargs={"url": "https://content.dropboxapi.com/2/files"}, build_request_http_method="POST", get_flow_request_kwargs={ "url": "https://content.dropboxapi.com/2/files" }, get_flow_request_http_method="GET", ) def _mock_failed_get(*args, **kwargs): return _MockResponse(status_code=500, json={}) def _mock_successful_post(*args, **kwargs): return _MockResponse(status_code=200, json={"id": "abc"}) webhook._method_to_function = { "GET": _mock_failed_get, "POST": _mock_successful_post, } webhook.add_flow(sample_flow) webhook.build() with pytest.raises(HTTPError, match="test-error-message"): webhook.get_flow()
def test_add_flow_and_contains_work_as_expected(sample_flow): webhook = Webhook( build_request_kwargs={"url": "https://content.dropboxapi.com/2/files"}, build_request_http_method="POST", get_flow_request_kwargs={ "url": "https://content.dropboxapi.com/2/files" }, get_flow_request_http_method="GET", ) assert sample_flow.name not in webhook out = webhook.add_flow(sample_flow) assert isinstance(out, str) assert sample_flow.name in webhook assert str(uuid.uuid4()) not in webhook # should return False if input is not a string assert sample_flow not in webhook
def test_webhook_build_works_with_no_arguments(sample_flow): webhook = Webhook( build_request_kwargs={"url": "https://content.dropboxapi.com/2/files"}, build_request_http_method="POST", get_flow_request_kwargs={ "url": "https://content.dropboxapi.com/2/files" }, get_flow_request_http_method="GET", ) def _mock_successful_get(*args, **kwargs): return _MockResponse(status_code=200, json={}, content=cloudpickle.dumps(sample_flow)) def _mock_successful_post(*args, **kwargs): return _MockResponse(status_code=200, json={"id": "abc"}) webhook._method_to_function = { "GET": _mock_successful_get, "POST": _mock_successful_post, } webhook.add_flow(sample_flow) res = webhook.build() assert isinstance(res, Webhook) res = webhook.get_flow() assert isinstance(res, Flow)
def test_all_valid_http_verb_combinations_work(): possible_verbs = ["GET", "PATCH", "POST", "PUT"] for build_verb in possible_verbs: for get_verb in possible_verbs: storage = Webhook( build_request_kwargs={"url": "whatever"}, build_request_http_method=build_verb, get_flow_request_kwargs={"url": "whatever"}, get_flow_request_http_method=get_verb, ) assert storage.build_request_http_method == build_verb assert storage.get_flow_request_http_method == get_verb
def test_webhook_fails_for_bad_get_flow_request_http_method(): with pytest.raises(RuntimeError, match="HTTP method 'BET' not recognized"): Webhook( build_request_kwargs={ "url": "https://content.dropboxapi.com/2/files" }, build_request_http_method="POST", get_flow_request_kwargs={ "url": "https://content.dropboxapi.com/2/files" }, get_flow_request_http_method="BET", )
def test_webhook_raises_warning_if_data_in_build_request_kwargs(sample_flow): webhook = Webhook( build_request_kwargs={ "url": "https://content.dropboxapi.com/2/files", "data": cloudpickle.dumps(sample_flow), }, build_request_http_method="POST", get_flow_request_kwargs={ "url": "https://content.dropboxapi.com/2/files" }, get_flow_request_http_method="GET", ) def _mock_successful_get(*args, **kwargs): return _MockResponse(status_code=200, json={}, content=cloudpickle.dumps(sample_flow)) def _mock_successful_post(*args, **kwargs): return _MockResponse(status_code=200, json={"id": "abc"}) webhook._method_to_function = { "GET": _mock_successful_get, "POST": _mock_successful_post, } webhook.add_flow(sample_flow) with pytest.warns(RuntimeWarning, match="flow content and should not be set directly"): res = webhook.build() assert isinstance(res, Webhook)
def test_render_dict_raises_expected_exception_on_missing_secret(monkeypatch): monkeypatch.delenv("ANOTHER_SECRET", raising=False) webhook = Webhook( build_request_kwargs={ "url": "https://content.dropboxapi.com/2/files?key=${ANOTHER_SECRET}" }, build_request_http_method="POST", get_flow_request_kwargs={ "url": "https://content.dropboxapi.com/2/files" }, get_flow_request_http_method="GET", ) with pytest.raises(RuntimeError, match="does not refer to an environment variable or"): _render_dict(webhook.build_request_kwargs)
def test_webhook_works_with_file_storage(sample_flow, tmpdir): script_file = os.path.join(tmpdir, "{}.py".format(str(uuid.uuid4()))) script_contents = """from prefect import Flow\nf=Flow('test-flow')""" with open(script_file, "w") as f: f.write(script_contents) webhook = Webhook( build_request_kwargs={ "url": "https://content.dropboxapi.com/2/files", "headers": { "Content-Type": "application/octet-stream" }, }, build_request_http_method="POST", get_flow_request_kwargs={ "url": "https://content.dropboxapi.com/2/files", "headers": { "Accept": "application/octet-stream" }, }, get_flow_request_http_method="GET", stored_as_script=True, flow_script_path=script_file, ) def _mock_successful_get(*args, **kwargs): file_contents = """from prefect import Flow\nf=Flow('test-flow')""" return _MockResponse(status_code=200, json={}, content=file_contents.encode("utf-8")) def _mock_successful_post(*args, **kwargs): return _MockResponse(status_code=200, json={"id": "abc"}) webhook._method_to_function = { "GET": _mock_successful_get, "POST": _mock_successful_post, } webhook.add_flow(sample_flow) res = webhook.build() assert isinstance(res, Webhook) res = webhook.get_flow() assert isinstance(res, Flow) assert res.name == "test-flow"
def test_render_dict_raises_expected_exception_on_missing_env_var(monkeypatch): monkeypatch.delenv("SOME_CRED", raising=False) webhook = Webhook( build_request_kwargs={ "url": "https://content.dropboxapi.com/2/files", "headers": { "X-Api-Key": "Token ${SOME_CRED}" }, }, build_request_http_method="POST", get_flow_request_kwargs={ "url": "https://content.dropboxapi.com/2/files" }, get_flow_request_http_method="GET", ) with pytest.raises(RuntimeError, match="SOME_CRED"): _render_dict(webhook.build_request_kwargs)
def test_create_webhook_storage(): build_request_kwargs = { "url": "https://content.dropboxapi.com/2/files/upload" } get_flow_request_kwargs = { "url": "https://content.dropboxapi.com/2/files/download" } storage = Webhook( build_request_kwargs=build_request_kwargs, build_request_http_method="PATCH", get_flow_request_kwargs=get_flow_request_kwargs, get_flow_request_http_method="GET", ) assert storage assert storage.logger assert storage.build_request_kwargs == build_request_kwargs assert storage.build_request_http_method == "PATCH" assert storage.get_flow_request_kwargs == get_flow_request_kwargs assert storage.get_flow_request_http_method == "GET" assert storage.secrets == [] assert storage.stored_as_script is False
def test_render_dict_gets_env_variables(monkeypatch): some_cred = str(uuid.uuid4()) another_secret = str(uuid.uuid4()) monkeypatch.setenv("SOME_CRED", some_cred) webhook = Webhook( build_request_kwargs={ "url": "https://content.dropboxapi.com/2/files", "headers": { "X-Api-Key": "${SOME_CRED}", "X-Custom-Key": "${ANOTHER_SECRET}", }, }, build_request_http_method="POST", get_flow_request_kwargs={ "url": "https://content.dropboxapi.com/2/files" }, get_flow_request_http_method="GET", ) # set a local secret context.setdefault("secrets", {}) context.secrets["ANOTHER_SECRET"] = another_secret new_kwargs = _render_dict(webhook.build_request_kwargs) # _render_dict should not have side effects assert webhook.build_request_kwargs == { "url": "https://content.dropboxapi.com/2/files", "headers": { "X-Api-Key": "${SOME_CRED}", "X-Custom-Key": "${ANOTHER_SECRET}" }, } # env variables and secrets should have been filled in assert new_kwargs["headers"]["X-Api-Key"] == some_cred assert new_kwargs["headers"]["X-Custom-Key"] == another_secret