def test_good_message_publish_unknown_error(app, mock_pubsub_calls, mock_set_env_webhook_signature_key): """ tests good message that raises unknown exception when published to topic; ensures we send a non-200 response """ base_url = "functions.googlecloud.com" function_name = "/test_handle_webhook_valid" path = "/test_handle_webhook_valid" content = { "merchant_id": "merchant", "location_id": "location", "event_type": "event", "entity_id": "entity" } to_sign = "://" + base_url + function_name + path + json.dumps(content, sort_keys=True) signature = base64.b64encode(hmac.new(KEY.encode(), to_sign.encode(), sha1).digest()) with app.test_request_context(method='POST', path="/test_handle_webhook_valid", base_url="functions.googlecloud.com", json=content, headers={'X-Square-Signature': signature}): mock_pubsub_calls.return_value.publish.return_value.result.side_effect = Exception with pytest.raises(InternalServerError): main.handle_webhook(flask.request) assert mock_pubsub_calls.return_value.publish.call_count == 1
def test_handle_webhook_publish_timeout(app, mocker, mock_setup): """ test that if the publish call to pubsub times out, we send a non-200 response """ base_url = "functions.googlecloud.com" function_name = "/" + os.environ["FUNCTION_NAME"] path = "/test_handle_webhook_valid" content = { "merchant_id": "merchant", "location_id": "location", "event_type": "event", "entity_id": "entity" } to_sign = "://" + base_url + function_name + path + json.dumps( content, sort_keys=True) signature = base64.b64encode( hmac.new(KEY.encode(), to_sign.encode(), sha1).digest()) with app.test_request_context(method='POST', path=path, base_url=base_url, json=content, headers={"X-Square-Signature": signature}): with pytest.raises(InternalServerError): mocker.patch.object(pubsub_v1.publisher.futures.Future, "result", side_effect=exceptions.TimeoutError()) main.handle_webhook(flask.request)
def test_handle_webhook_valid_json_no_signature(app, mock_set_env_webhook_signature_key): """ Ensures that if there is JSON content but no signature, the message is rejected """ content = { "merchant_id": "merchant", "location_id": "location", "event_type": "event", "entity_id": "entity" } with app.test_request_context(method='POST', json=content): with pytest.raises(KeyError): main.handle_webhook(flask.request)
def test_handle_webhook_invalid_signature(app, mock_set_env_webhook_signature_key): """ Ensures that if the signature is not correct, the message is rejected """ with app.test_request_context(method='POST', path="/test_handle_webhook_valid", base_url="functions.googlecloud.com", json={ "merchant_id": "merchant", "location_id": "location", "event_type": "event", "entity_id": "entity"}, headers={"X-Square-Signature": "NOT_A_VALID_SIGNATURE"}): with pytest.raises(BadRequest): main.handle_webhook(flask.request)
def test_handle_webhook_valid(app, mock_setup): """ tests that a valid message is successfully processed by the function """ base_url = "functions.googlecloud.com" function_name = "/" + os.environ["FUNCTION_NAME"] path = "/test_handle_webhook_valid" content = { "merchant_id": "merchant", "location_id": "location", "event_type": "event", "entity_id": "entity" } to_sign = "://" + base_url + function_name + path + json.dumps( content, sort_keys=True) signature = base64.b64encode( hmac.new(KEY.encode(), to_sign.encode(), sha1).digest()) with app.test_request_context(method='POST', path=path, base_url=base_url, json=content, headers={"X-Square-Signature": signature}): res = main.handle_webhook(flask.request) assert res.status == '200 OK' response = mock_setup.pull(request={ "subscription": SUBSCRIPTION_PATH, "max_messages": 1 }) # ensure that what we sent over the webhook is what we got over pubsub assert json.loads( response.received_messages[0].message.data) == content
def test_good_message_retry(app, mock_pubsub_calls, mock_set_env_webhook_signature_key): """ tests complete path sent as retry with only pubsub mocked out""" base_url = "functions.googlecloud.com" function_name = "/test_handle_webhook_valid" path = "/test_handle_webhook_valid" content = { "merchant_id": "merchant", "location_id": "location", "event_type": "event", "entity_id": "entity" } to_sign = "://" + base_url + function_name + path + json.dumps(content, sort_keys=True) signature = base64.b64encode(hmac.new(KEY.encode(), to_sign.encode(), sha1).digest()) with app.test_request_context(method='POST', path="/test_handle_webhook_valid", base_url="functions.googlecloud.com", json=content, headers={ 'X-Square-Signature': signature, 'Square-Initial-Delivery-Timestamp': datetime.datetime.utcnow().isoformat("T") + "Z", 'Square-Retry-Number': 1, 'Square-Retry-Reason': "500 Internal Server Error", }): response = main.handle_webhook(flask.request) assert response.status_code == 200 assert response.data == b'message_id' assert mock_pubsub_calls.return_value.publish.call_count == 1
def test_handle_webhook_empty_webhook_key(app, monkeypatch): """ Ensures that if the webhook key is not available to the function, the function fails. """ monkeypatch.delenv("SQUARE_WEBHOOK_SIGNATURE_KEY", raising=False) base_url = "functions.googlecloud.com" function_name = "/test_handle_webhook_valid" path = "/test_handle_webhook_valid" content = { "merchant_id": "merchant", "location_id": "location", "event_type": "event", "entity_id": "entity" } to_sign = "://" + base_url + function_name + path + json.dumps(content, sort_keys=True) signature = base64.b64encode(hmac.new(KEY.encode(), to_sign.encode(), sha1).digest()) with app.test_request_context(method='POST', path="/test_handle_webhook_valid", base_url="functions.googlecloud.com", json=content, headers={'X-Square-Signature': signature}): with pytest.raises(KeyError): main.handle_webhook(flask.request)
def test_insufficient_json_fields(app, mock_pubsub_calls, mock_set_env_webhook_signature_key): """ tests invalid message that is missing a required field in JSON but has a valid signature; ensures we return a non-200 response """ base_url = "functions.googlecloud.com" function_name = "/test_handle_webhook_valid" path = "/test_handle_webhook_valid" content = { "merchant_id": "merchant", "location_id": "location", "event_type": "event", } to_sign = "://" + base_url + function_name + path + json.dumps(content, sort_keys=True) signature = base64.b64encode(hmac.new(KEY.encode(), to_sign.encode(), sha1).digest()) with app.test_request_context(method='POST', path="/test_handle_webhook_valid", base_url="functions.googlecloud.com", json=content, headers={'X-Square-Signature': signature}): mock_pubsub_calls.return_value.publish.return_value.result.side_effect = Exception with pytest.raises(BadRequest): main.handle_webhook(flask.request) assert mock_pubsub_calls.return_value.publish.call_count == 0
def test_good_message(app, mock_pubsub_calls, mock_set_env_webhook_signature_key): """ tests complete path with only pubsub mocked out""" base_url = "functions.googlecloud.com" function_name = "/test_handle_webhook_valid" path = "/test_handle_webhook_valid" content = { "merchant_id": "merchant", "location_id": "location", "event_type": "event", "entity_id": "entity" } to_sign = "://" + base_url + function_name + path + json.dumps(content, sort_keys=True) signature = base64.b64encode(hmac.new(KEY.encode(), to_sign.encode(), sha1).digest()) with app.test_request_context(method='POST', path="/test_handle_webhook_valid", base_url="functions.googlecloud.com", json=content, headers={'X-Square-Signature': signature}): response = main.handle_webhook(flask.request) assert response.status_code == 200 assert response.data == b'message_id' assert mock_pubsub_calls.return_value.publish.call_count == 1
def test_handle_webhook_send_non_json(app): """ Ensures that if there is content but it is not JSON, the message is rejected """ with app.test_request_context(method='POST', content_type='text/plain', data='abc123'): with pytest.raises(UnsupportedMediaType): main.handle_webhook(flask.request)
def test_handle_webhook_empty_json(app): """ Ensures that if there is no content, the message is rejected """ with app.test_request_context(method='POST', content_type='application/json'): with pytest.raises(BadRequest): main.handle_webhook(flask.request)
def test_handle_webhook_invalid_method(method, app, mock_set_env_webhook_signature_key): """ Ensures that if the webhook only responds to POST requests. """ with app.test_request_context(method=method): with pytest.raises(MethodNotAllowed): main.handle_webhook(flask.request)