Added features for append/error_malformed, delete/list_order, insert/error_malformed and insert/list_order. Updated append/error_unprocessable and resolve conflicts in list/success.

This commit is contained in:
Héctor Hurtado
2019-08-20 10:36:24 +02:00
14 changed files with 221 additions and 144 deletions
+14 -5
View File
@@ -306,13 +306,16 @@ def delete_route(app):
######################################################################## ########################################################################
async def run_init_script(app, scripts): async def run_init_script(app, scripts, interactive):
""" """
Run the init script if given, then wait for the shell to finish. Run the init script if given, then wait for the shell to finish.
""" """
if not scripts: if not scripts:
# No script given # No script given
if not interactive:
return
else:
cmd = "/bin/bash" cmd = "/bin/bash"
else: else:
def build_filenames(): def build_filenames():
@@ -320,7 +323,10 @@ async def run_init_script(app, scripts):
yield shlex.quote(filename) yield shlex.quote(filename)
yield "<(echo)" yield "<(echo)"
filenames = " ".join(build_filenames()) filenames = " ".join(build_filenames())
if interactive:
cmd = f"/bin/bash --init-file <(cat {filenames})" cmd = f"/bin/bash --init-file <(cat {filenames})"
else:
cmd = f"/bin/bash <(cat {filenames})"
shell_task = await asyncio.create_subprocess_shell( shell_task = await asyncio.create_subprocess_shell(
cmd, cmd,
@@ -330,16 +336,17 @@ async def run_init_script(app, scripts):
}) })
await shell_task.wait() await shell_task.wait()
if interactive:
await app.cleanup() await app.cleanup()
os._exit(shell_task.returncode) os._exit(shell_task.returncode)
async def start_background_tasks(app): async def start_background_tasks(app):
global loop global loop
app["debug_tasks"] = loop.create_task(run_init_script(app, app["scripts"])) app["debug_tasks"] = loop.create_task(run_init_script(app, app["scripts"], app["interactive"]))
async def start_kapow_server(bind, scripts, certfile=None, keyfile=None): async def start_kapow_server(bind, scripts, certfile=None, interactive=False, keyfile=None):
user_app = web.Application(client_max_size=1024**3) user_app = web.Application(client_max_size=1024**3)
user_runner = web.AppRunner(user_app) user_runner = web.AppRunner(user_app)
await user_runner.setup() await user_runner.setup()
@@ -366,6 +373,7 @@ async def start_kapow_server(bind, scripts, certfile=None, keyfile=None):
web.put('/handlers/{id}/{field:.*}', set_field), web.put('/handlers/{id}/{field:.*}', set_field),
]) ])
control_app["scripts"] = scripts control_app["scripts"] = scripts
control_app["interactive"] = interactive
control_app.on_startup.append(start_background_tasks) control_app.on_startup.append(start_background_tasks)
control_runner = web.AppRunner(control_app) control_runner = web.AppRunner(control_app)
@@ -391,12 +399,13 @@ def kapow(ctx):
@click.option("--certfile", default=None) @click.option("--certfile", default=None)
@click.option("--keyfile", default=None) @click.option("--keyfile", default=None)
@click.option("--bind", default="0.0.0.0:8080") @click.option("--bind", default="0.0.0.0:8080")
@click.option("-i", "--interactive", is_flag=True)
@click.argument("scripts", nargs=-1) @click.argument("scripts", nargs=-1)
def server(certfile, keyfile, bind, scripts): def server(certfile, keyfile, bind, interactive, scripts):
if bool(certfile) ^ bool(keyfile): if bool(certfile) ^ bool(keyfile):
print("For SSL both 'certfile' and 'keyfile' should be provided.") print("For SSL both 'certfile' and 'keyfile' should be provided.")
sys.exit(1) sys.exit(1)
loop.run_until_complete(start_kapow_server(bind, scripts, certfile, keyfile)) loop.run_until_complete(start_kapow_server(bind, scripts, certfile, interactive, keyfile))
loop.run_forever() loop.run_forever()
@kapow.group() @kapow.group()
+3 -3
View File
@@ -12,8 +12,8 @@
"Scenario": 2, "Scenario": 2,
"Background": 2, "Background": 2,
"given": 4, "given": 4,
"when": 6, "when": 4,
"then": 6, "then": 4,
"and": 6, "and": 6,
"but": 6, "but": 6,
"example": 2, "example": 2,
@@ -24,7 +24,7 @@
"new-line-at-eof": ["on", "yes"], "new-line-at-eof": ["on", "yes"],
"no-multiple-empty-lines": "on", "no-multiple-empty-lines": "on",
"no-scenario-outlines-without-examples": "on", "no-scenario-outlines-without-examples": "on",
"name-length": ["on", {"Feature": 50}], "name-length": ["on", {"Feature": 80}],
"no-restricted-tags": ["on", {"tags": ["@watch", "@wip"]}], "no-restricted-tags": ["on", {"tags": ["@watch", "@wip"]}],
"use-and": "on", "use-and": "on",
"no-duplicate-tags": "on", "no-duplicate-tags": "on",
+3 -1
View File
@@ -1,3 +1,5 @@
test: test:
gherkin-lint gherkin-lint
pipenv run behave --no-capture pipenv run behave --no-capture --stop
catalog:
pipenv run behave --steps-catalog
@@ -12,8 +12,8 @@ Feature: Kapow! server reject responses with semantic errors.
When I append the route: When I append the route:
| entrypoint | command | | entrypoint | command |
| /bin/sh -c | ls -la / \| response /body | | /bin/sh -c | ls -la / \| response /body |
Then I get unprocessable entity as response code Then I get 422 as response code
And I get "Missing Mandatory Field" as response phrase And I get "Missing Mandatory Field" as response reason phrase
And I get the following entity as response body: And I get the following entity as response body:
| missing_mandatory_fields | | missing_mandatory_fields |
| "url_pattern", "method" | | "url_pattern", "method" |
@@ -26,8 +26,8 @@ Feature: Kapow! server reject responses with semantic errors.
When I append the route: When I append the route:
| method | url_pattern | entrypoint | command | | method | url_pattern | entrypoint | command |
| GET | +123-- | /bin/sh -c | ls -la / \| response /body | | GET | +123-- | /bin/sh -c | ls -la / \| response /body |
Then I get unprocessable entity as response code Then I get 422 as response code
And I get "Invalid Route Spec" as response phrase And I get "Invalid Route Spec" as response reason phrase
And I get an empty response body And I get an empty response body
Scenario: Error because of wrong method value. Scenario: Error because of wrong method value.
@@ -38,6 +38,6 @@ Feature: Kapow! server reject responses with semantic errors.
When I append the route: When I append the route:
| method | url_pattern | entrypoint | command | | method | url_pattern | entrypoint | command |
| AVECES | +123-- | /bin/sh -c | ls -la / \| response /body | | AVECES | +123-- | /bin/sh -c | ls -la / \| response /body |
Then I get unprocessable entity as response code Then I get 422 as response code
And I get "Invalid Data Type" as response phrase And I get "Invalid Data Type" as response reason phrase
And I get an empty response body And I get an empty response body
@@ -11,8 +11,8 @@ Feature: Append new routes in Kapow! server.
When I append the route: When I append the route:
| method | url_pattern | entrypoint | command | | method | url_pattern | entrypoint | command |
| GET | /listRootDir | /bin/sh -c | ls -la / \| response /body | | GET | /listRootDir | /bin/sh -c | ls -la / \| response /body |
Then I get created as response code Then I get 201 as response code
And I get "Created" as response phrase And I get "Created" as response reason phrase
And I get the following entity as response body: And I get the following entity as response body:
| method | url_pattern | entrypoint | command | index | id | | method | url_pattern | entrypoint | command | index | id |
| GET | /listRootDir | /bin/sh -c | ls -la / \| response /body | 0 | * | | GET | /listRootDir | /bin/sh -c | ls -la / \| response /body | 0 | * |
@@ -28,8 +28,8 @@ Feature: Append new routes in Kapow! server.
When I append the route: When I append the route:
| method | url_pattern | entrypoint | command | | method | url_pattern | entrypoint | command |
| GET | /listEtcDir | /bin/sh -c | ls -la /etc \| response /body | | GET | /listEtcDir | /bin/sh -c | ls -la /etc \| response /body |
Then I get created as response code Then I get 201 as response code
And I get "Created" as response phrase And I get "Created" as response reason phrase
And I get the following entity as response body: And I get the following entity as response body:
| method | url_pattern | entrypoint | command | index | id | | method | url_pattern | entrypoint | command | index | id |
| GET | /listEtcDir | /bin/sh -c | ls -la /etc \| response /body | 2 | * | | GET | /listEtcDir | /bin/sh -c | ls -la /etc \| response /body | 2 | * |
@@ -8,6 +8,6 @@ Feature: Fail to delete a route in Kapow! server.
Given I have a just started Kapow! server Given I have a just started Kapow! server
When I delete the route with id "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx" When I delete the route with id "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx"
Then I get not found as response code Then I get 404 as response code
And I get "Not Found" as response phrase And I get "Not Found" as response reason phrase
And I get an empty response body And I get an empty response body
@@ -8,6 +8,6 @@ Feature: Delete routes in Kapow! server.
Given I have a running Kapow! server Given I have a running Kapow! server
And It has a route with id "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx" And It has a route with id "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx"
When I delete the route with id "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx" When I delete the route with id "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx"
Then I get ok as response code Then I get 200 as response code
And I get "OK" as response phrase And I get "OK" as response reason phrase
And I get an empty response body And I get an empty response body
@@ -11,8 +11,8 @@ Feature: Kapow! server reject insert responses with semantic errors.
When I insert the route: When I insert the route:
| entrypoint | command | | entrypoint | command |
| /bin/sh -c | ls -la / \| response /body | | /bin/sh -c | ls -la / \| response /body |
Then I get unprocessable entity as response code Then I get 422 as response code
And I get "Missing Mandatory Field" as response phrase And I get "Missing Mandatory Field" as response reason phrase
And I get the following entity as response body: And I get the following entity as response body:
| missing_mandatory_fields | | missing_mandatory_fields |
| "url_pattern", "method" | | "url_pattern", "method" |
@@ -25,8 +25,8 @@ Feature: Kapow! server reject insert responses with semantic errors.
When I insert the route: When I insert the route:
| method | url_pattern | entrypoint | command | index | | method | url_pattern | entrypoint | command | index |
| GET | /listRootDir | /bin/sh -c | ls -la / \| response /body | 0 | | GET | /listRootDir | /bin/sh -c | ls -la / \| response /body | 0 |
Then I get unprocessable entity as response code Then I get 422 as response code
And I get "Invalid Route Spec" as response phrase And I get "Invalid Route Spec" as response reason phrase
And I get an empty response body And I get an empty response body
Scenario: Error because of wrong method value. Scenario: Error because of wrong method value.
@@ -37,6 +37,6 @@ Feature: Kapow! server reject insert responses with semantic errors.
When I insert the route: When I insert the route:
| method | url_pattern | entrypoint | command | index | | method | url_pattern | entrypoint | command | index |
| AVECES | /listRootDir | /bin/sh -c | ls -la / \| response /body | 0 | | AVECES | /listRootDir | /bin/sh -c | ls -la / \| response /body | 0 |
Then I get unprocessable entity as response code Then I get 422 as response code
And I get "Invalid Data Type" as response phrase And I get "Invalid Data Type" as response reason phrase
And I get an empty response body And I get an empty response body
@@ -16,8 +16,8 @@ Feature: Insert new routes in Kapow! server.
When I insert the route: When I insert the route:
| method | url_pattern | entrypoint | command | index | | method | url_pattern | entrypoint | command | index |
| GET | /listRootDir | /bin/sh -c | ls -la / \| response /body | 0 | | GET | /listRootDir | /bin/sh -c | ls -la / \| response /body | 0 |
Then I get ok as response code Then I get 200 as response code
And I get "OK" as response phrase And I get "OK" as response reason phrase
And I get the following entity as response body: And I get the following entity as response body:
| method | url_pattern | entrypoint | command | index | id | | method | url_pattern | entrypoint | command | index | id |
| GET | /listRootDir | /bin/sh -c | ls -la / \| response /body | 0 | * | | GET | /listRootDir | /bin/sh -c | ls -la / \| response /body | 0 | * |
@@ -30,8 +30,8 @@ Feature: Insert new routes in Kapow! server.
When I insert the route: When I insert the route:
| method | url_pattern | entrypoint | command | index | | method | url_pattern | entrypoint | command | index |
| GET | /listRootDir | /bin/sh -c | ls -la / \| response /body | 1 | | GET | /listRootDir | /bin/sh -c | ls -la / \| response /body | 1 |
Then I get ok as response code Then I get 200 as response code
And I get "OK" as response phrase And I get "OK" as response reason phrase
And I get the following entity as response body: And I get the following entity as response body:
| method | url_pattern | entrypoint | command | index | id | | method | url_pattern | entrypoint | command | index | id |
| GET | /listRootDir | /bin/sh -c | ls -la / \| response /body | 1 | * | | GET | /listRootDir | /bin/sh -c | ls -la / \| response /body | 1 | * |
+69 -3
View File
@@ -1,8 +1,11 @@
import subprocess import subprocess
from time import sleep from time import sleep
import shlex
import socket
from contextlib import suppress
import requests import requests
from environconfig import EnvironConfig, StringVar from environconfig import EnvironConfig, StringVar, IntVar
class Env(EnvironConfig): class Env(EnvironConfig):
@@ -15,16 +18,31 @@ class Env(EnvironConfig):
#: Where the Data API is #: Where the Data API is
KAPOW_DATAAPI_URL = StringVar(default="http://localhost:8080") KAPOW_DATAAPI_URL = StringVar(default="http://localhost:8080")
KAPOW_BOOT_TIMEOUT = IntVar(default=10)
@given('I have a just started Kapow! server') @given('I have a just started Kapow! server')
@given('I have a running Kapow! server')
def step_impl(context): def step_impl(context):
context.server = subprocess.Popen( context.server = subprocess.Popen(
Env.KAPOW_SERVER_CMD, shlex.split(Env.KAPOW_SERVER_CMD),
stdout=subprocess.DEVNULL, stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
shell=True) shell=False)
# Check process is running with reachable APIs
open_ports = False
for _ in range(Env.KAPOW_BOOT_TIMEOUT):
is_running = context.server.poll() is None is_running = context.server.poll() is None
assert is_running, "Server is not running!" assert is_running, "Server is not running!"
with suppress(requests.exceptions.ConnectionError):
open_ports = (
requests.head(Env.KAPOW_CONTROLAPI_URL, timeout=1).status_code
and requests.head(Env.KAPOW_DATAAPI_URL, timeout=1).status_code)
if open_ports:
break
sleep(1)
assert open_ports, "API is unreachable after KAPOW_BOOT_TIMEOUT"
@when('I request a routes listing') @when('I request a routes listing')
@@ -69,3 +87,51 @@ def step_impl(context):
assert header in entry, f"Response does not contain the key {header}" assert header in entry, f"Response does not contain the key {header}"
if row[header] != '*': if row[header] != '*':
assert entry[header] == row[header], f"Values mismatch" assert entry[header] == row[header], f"Values mismatch"
#
#
#
@when('I append the route')
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()
@then('I get {code} as response code')
def step_impl(context, code):
raise NotImplementedError('STEP: Then I get unprocessable entity as response code')
@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')
@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')
@then('I get an empty response body')
def step_impl(context):
raise NotImplementedError('STEP: Then I get an empty response body')
@when('I delete the route with id "{id}"')
def step_impl(context, id):
raise NotImplementedError('STEP: When I delete the route with id "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx"')
@given('It has a route with id "{id}"')
def step_impl(context, id):
raise NotImplementedError('STEP: Given It has a route with id "xxxxxxxx-xxxx-Mxxx-Nxxx-xxxxxxxxxxxx"')
@when('I insert the route')
def step_impl(context):
raise NotImplementedError('STEP: When I insert the route')