diff --git a/poc/bin/kapow b/poc/bin/kapow index 0dd3b0c..bd3c16f 100755 --- a/poc/bin/kapow +++ b/poc/bin/kapow @@ -16,11 +16,12 @@ # limitations under the License. # -from urllib.parse import urlparse from itertools import repeat +from urllib.parse import urlparse from uuid import uuid4 import asyncio import io +import json import logging import os import shlex @@ -280,13 +281,25 @@ def append_route(app): app.router._frozen = False content = await request.json() name = "ROUTE_" + str(uuid4()).replace('-', '_') - app.router.add_route(content["method"], - content["url_pattern"], - handle_route(content["entrypoint"], - content["command"]), - name=name) - print(f'Route created {content["method"]} {content["url_pattern"]}') - return web.json_response(name) + try: + app.router.add_route(content["method"], + content["url_pattern"], + handle_route(content["entrypoint"], + content["command"]), + name=name) + except KeyError as exc: + missing = list() + for field in ("method", "url_pattern", "entrypoint", "command"): + if field not in content: + missing.append(field) + return web.Response(status=422, + reason="Missing Mandatory Field", + body=json.dumps({"missing_mandatory_fields": missing})) + except ValueError as exc: + return web.Response(status=422, reason="Invalid Route Spec") + else: + print(f'Route created {content["method"]} {content["url_pattern"]}') + return web.json_response(name) return _append_route diff --git a/poc/dist/kapow-0.0.1-py3.7.egg b/poc/dist/kapow-0.0.1-py3.7.egg new file mode 100644 index 0000000..4bc98c7 Binary files /dev/null and b/poc/dist/kapow-0.0.1-py3.7.egg differ diff --git a/spec/test/features/control/append/error_unprocessable.feature b/spec/test/features/control/append/error_unprocessable.feature index 933cf07..2025918 100644 --- a/spec/test/features/control/append/error_unprocessable.feature +++ b/spec/test/features/control/append/error_unprocessable.feature @@ -15,8 +15,8 @@ Feature: Kapow! server reject responses with semantic errors. Then I get 422 as response code And I get "Missing Mandatory Field" as response reason phrase And I get the following entity as response body: - | missing_mandatory_fields | - | "url_pattern", "method" | + | missing_mandatory_fields | + | ["url_pattern", "method"] | Scenario: Error because of wrong route specification. If a request contains an invalid expression in the diff --git a/spec/test/features/steps/steps.py b/spec/test/features/steps/steps.py index c95a2d1..25fce6e 100644 --- a/spec/test/features/steps/steps.py +++ b/spec/test/features/steps/steps.py @@ -1,12 +1,14 @@ -import subprocess +from contextlib import suppress from time import sleep +import json import shlex import socket -from contextlib import suppress +import subprocess import requests -from environconfig import EnvironConfig, StringVar, IntVar +from environconfig import EnvironConfig, StringVar, IntVar, BooleanVar +import logging class Env(EnvironConfig): #: How to run Kapow! server @@ -20,6 +22,30 @@ class Env(EnvironConfig): KAPOW_BOOT_TIMEOUT = IntVar(default=10) + KAPOW_DEBUG_TESTS = BooleanVar(default=True) + + +if Env.KAPOW_DEBUG_TESTS: + # These two lines enable debugging at httplib level + # (requests->urllib3->http.client) You will see the REQUEST, + # including HEADERS and DATA, and RESPONSE with HEADERS but without + # DATA. The only thing missing will be the response.body which is + # not logged. + try: + import http.client as http_client + except ImportError: + # Python 2 + import httplib as http_client + http_client.HTTPConnection.debuglevel = 1 + + # You must initialize logging, otherwise you'll not see debug output. + logging.basicConfig() + logging.getLogger().setLevel(logging.DEBUG) + requests_log = logging.getLogger("requests.packages.urllib3") + requests_log.setLevel(logging.DEBUG) + requests_log.propagate = True + + @given('I have a just started Kapow! server') @given('I have a running Kapow! server') def step_impl(context): @@ -97,30 +123,32 @@ def step_impl(context): if not hasattr(context, 'table'): raise RuntimeError("A table must be set for this step.") - for row in context.table: - response = requests.post(f"{Env.KAPOW_CONTROLAPI_URL}/routes", - json={h: row[h] for h in row.headings}) - response.raise_for_status() + row = context.table[0] + context.response = requests.post(f"{Env.KAPOW_CONTROLAPI_URL}/routes", + json={h: row[h] for h in row.headings}) @then('I get {code} as response code') def step_impl(context, code): - raise NotImplementedError('STEP: Then I get unprocessable entity as response code') + assert context.response.status_code == int(code), f"Got {context.response.status_code} instead" @then('I get "{reason}" as response reason phrase') def step_impl(context, reason): - raise NotImplementedError('STEP: Then I get "Missing Mandatory Field" as response phrase') + assert context.response.reason == reason, f"Got {context.response.reason} instead" @then('I get the following entity as response body') def step_impl(context): - raise NotImplementedError('STEP: Then I get the following entity as response body') + for row in context.table: + for name, value in row.items(): + assert name in context.response.json(), f"Field {name} not present in {context.response.json()}" + assert set(json.loads(value)) == set(context.response.json()[name]) @then('I get an empty response body') def step_impl(context): - raise NotImplementedError('STEP: Then I get an empty response body') + assert context.response.content == b'', f"Response body is not empty. Got {context.response.content} instead." @when('I delete the route with id "{id}"')