diff --git a/spec/test/.gherkin-lintrc b/spec/test/.gherkin-lintrc new file mode 100644 index 0000000..108e70e --- /dev/null +++ b/spec/test/.gherkin-lintrc @@ -0,0 +1,38 @@ +{ + "no-empty-file": "off", + "no-files-without-scenarios" : "off", + "no-unnamed-features": "off", + + "no-unnamed-scenarios": "on", + "no-dupe-scenario-names": ["on", "in-feature"], + "no-dupe-feature-names": "on", + "no-partially-commented-tag-lines": "on", + "indentation": [ + "on", { + "Scenario": 2, + "Background": 2, + "given": 4, + "when": 6, + "then": 6, + "and": 6, + "but": 6, + "example": 2, + "Examples": 4, + "scenario tag": 2} + ], + "no-trailing-spaces": "on", + "new-line-at-eof": ["on", "yes"], + "no-multiple-empty-lines": "on", + "no-scenario-outlines-without-examples": "on", + "name-length": ["on", {"Feature": 50}], + "no-restricted-tags": ["on", {"tags": ["@watch", "@wip"]}], + "use-and": "on", + "no-duplicate-tags": "on", + "no-superfluous-tags": "on", + "no-homogenous-tags": "on", + "one-space-between-tags": "on", + "no-unused-variables": "on", + "no-background-only-scenario": "on", + "no-empty-background": "on", + "scenario-size": ["on", { "steps-length": {"Background": 15, "Scenario": 15}}] +} diff --git a/spec/test/Makefile b/spec/test/Makefile index b87d4ee..a73f203 100644 --- a/spec/test/Makefile +++ b/spec/test/Makefile @@ -1,2 +1,3 @@ test: - pipenv run behave + gherkin-lint + pipenv run behave --no-capture diff --git a/spec/test/Pipfile b/spec/test/Pipfile index dc3b7c7..1c271cd 100644 --- a/spec/test/Pipfile +++ b/spec/test/Pipfile @@ -4,9 +4,13 @@ url = "https://pypi.org/simple" verify_ssl = true [dev-packages] +ipython = "*" +ipdb = "*" [packages] behave = "*" +environconfig = "*" +requests = "*" [requires] python_version = "3.7" diff --git a/spec/test/Pipfile.lock b/spec/test/Pipfile.lock index 57fa00e..30a33c2 100644 --- a/spec/test/Pipfile.lock +++ b/spec/test/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "b316a33dc153e346058ef2c5cfe6512e97a3f7e6710b32fefbbc0f3a94ecf4b7" + "sha256": "203306870267e9afb4beb819586ddc910310d39399cbd7294a6b9148c88dd3b3" }, "pipfile-spec": 6, "requires": { @@ -24,6 +24,34 @@ "index": "pypi", "version": "==1.2.6" }, + "certifi": { + "hashes": [ + "sha256:046832c04d4e752f37383b628bc601a7ea7211496b4638f6514d0e5b9acc4939", + "sha256:945e3ba63a0b9f577b1395204e13c3a231f9bc0223888be653286534e5873695" + ], + "version": "==2019.6.16" + }, + "chardet": { + "hashes": [ + "sha256:84ab92ed1c4d4f16916e05906b6b75a6c0fb5db821cc65e70cbd64a3e2a5eaae", + "sha256:fc323ffcaeaed0e0a02bf4d117757b98aed530d9ed4531e3e15460124c106691" + ], + "version": "==3.0.4" + }, + "environconfig": { + "hashes": [ + "sha256:06c50d8a3a7e73404276ee5b47539f41d5490409edeba64c40ed748d2daeea20" + ], + "index": "pypi", + "version": "==1.7.0" + }, + "idna": { + "hashes": [ + "sha256:c357b3f628cf53ae2c4c05627ecc484553142ca23264e593d327bcde5e9c3407", + "sha256:ea8b7f6188e6fa117537c3df7da9fc686d485087abf6ac197f9c46432f7e4a3c" + ], + "version": "==2.8" + }, "parse": { "hashes": [ "sha256:1b68657434d371e5156048ca4a0c5aea5afc6ca59a2fea4dd1a575354f617142" @@ -37,13 +65,137 @@ ], "version": "==0.5.2" }, + "requests": { + "hashes": [ + "sha256:11e007a8a2aa0323f5a921e9e6a2d7e4e67d9877e85773fba9ba6419025cbeb4", + "sha256:9cf5292fcd0f598c671cfc1e0d7d1a7f13bb8085e9a590f48c010551dc6c4b31" + ], + "index": "pypi", + "version": "==2.22.0" + }, "six": { "hashes": [ "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" ], "version": "==1.12.0" + }, + "urllib3": { + "hashes": [ + "sha256:b246607a25ac80bedac05c6f282e3cdaf3afb65420fd024ac94435cabe6e18d1", + "sha256:dbe59173209418ae49d485b87d1681aefa36252ee85884c31346debd19463232" + ], + "version": "==1.25.3" } }, - "develop": {} + "develop": { + "backcall": { + "hashes": [ + "sha256:38ecd85be2c1e78f77fd91700c76e14667dc21e2713b63876c0eb901196e01e4", + "sha256:bbbf4b1e5cd2bdb08f915895b51081c041bac22394fdfcfdfbe9f14b77c08bf2" + ], + "version": "==0.1.0" + }, + "decorator": { + "hashes": [ + "sha256:86156361c50488b84a3f148056ea716ca587df2f0de1d34750d35c21312725de", + "sha256:f069f3a01830ca754ba5258fde2278454a0b5b79e0d7f5c13b3b97e57d4acff6" + ], + "version": "==4.4.0" + }, + "ipdb": { + "hashes": [ + "sha256:473fdd798a099765f093231a8b1fabfa95b0b682fce12de0c74b61a4b4d8ee57" + ], + "index": "pypi", + "version": "==0.12.2" + }, + "ipython": { + "hashes": [ + "sha256:1d3a1692921e932751bc1a1f7bb96dc38671eeefdc66ed33ee4cbc57e92a410e", + "sha256:537cd0176ff6abd06ef3e23f2d0c4c2c8a4d9277b7451544c6cbf56d1c79a83d" + ], + "index": "pypi", + "version": "==7.7.0" + }, + "ipython-genutils": { + "hashes": [ + "sha256:72dd37233799e619666c9f639a9da83c34013a73e8bbc79a7a6348d93c61fab8", + "sha256:eb2e116e75ecef9d4d228fdc66af54269afa26ab4463042e33785b887c628ba8" + ], + "version": "==0.2.0" + }, + "jedi": { + "hashes": [ + "sha256:786b6c3d80e2f06fd77162a07fed81b8baa22dde5d62896a790a331d6ac21a27", + "sha256:ba859c74fa3c966a22f2aeebe1b74ee27e2a462f56d3f5f7ca4a59af61bfe42e" + ], + "version": "==0.15.1" + }, + "parso": { + "hashes": [ + "sha256:63854233e1fadb5da97f2744b6b24346d2750b85965e7e399bec1620232797dc", + "sha256:666b0ee4a7a1220f65d367617f2cd3ffddff3e205f3f16a0284df30e774c2a9c" + ], + "version": "==0.5.1" + }, + "pexpect": { + "hashes": [ + "sha256:2094eefdfcf37a1fdbfb9aa090862c1a4878e5c7e0e7e7088bdb511c558e5cd1", + "sha256:9e2c1fd0e6ee3a49b28f95d4b33bc389c89b20af6a1255906e90ff1262ce62eb" + ], + "markers": "sys_platform != 'win32'", + "version": "==4.7.0" + }, + "pickleshare": { + "hashes": [ + "sha256:87683d47965c1da65cdacaf31c8441d12b8044cdec9aca500cd78fc2c683afca", + "sha256:9649af414d74d4df115d5d718f82acb59c9d418196b7b4290ed47a12ce62df56" + ], + "version": "==0.7.5" + }, + "prompt-toolkit": { + "hashes": [ + "sha256:11adf3389a996a6d45cc277580d0d53e8a5afd281d0c9ec71b28e6f121463780", + "sha256:2519ad1d8038fd5fc8e770362237ad0364d16a7650fb5724af6997ed5515e3c1", + "sha256:977c6583ae813a37dc1c2e1b715892461fcbdaa57f6fc62f33a528c4886c8f55" + ], + "version": "==2.0.9" + }, + "ptyprocess": { + "hashes": [ + "sha256:923f299cc5ad920c68f2bc0bc98b75b9f838b93b599941a6b63ddbc2476394c0", + "sha256:d7cc528d76e76342423ca640335bd3633420dc1366f258cb31d05e865ef5ca1f" + ], + "version": "==0.6.0" + }, + "pygments": { + "hashes": [ + "sha256:71e430bc85c88a430f000ac1d9b331d2407f681d6f6aec95e8bcfbc3df5b0127", + "sha256:881c4c157e45f30af185c1ffe8d549d48ac9127433f2c380c24b84572ad66297" + ], + "version": "==2.4.2" + }, + "six": { + "hashes": [ + "sha256:3350809f0555b11f552448330d0b52d5f24c91a322ea4a15ef22629740f3761c", + "sha256:d16a0141ec1a18405cd4ce8b4613101da75da0e9a7aec5bdd4fa804d0e0eba73" + ], + "version": "==1.12.0" + }, + "traitlets": { + "hashes": [ + "sha256:9c4bd2d267b7153df9152698efb1050a5d84982d3384a37b2c1f7723ba3e7835", + "sha256:c6cb5e6f57c5a9bdaa40fa71ce7b4af30298fbab9ece9815b5d995ab6217c7d9" + ], + "version": "==4.3.2" + }, + "wcwidth": { + "hashes": [ + "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e", + "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c" + ], + "version": "==0.1.7" + } + } } diff --git a/spec/test/features/control/list/success.feature b/spec/test/features/control/list/success.feature index b3f3096..19eac99 100644 --- a/spec/test/features/control/list/success.feature +++ b/spec/test/features/control/list/success.feature @@ -1,14 +1,14 @@ -Feature: Listing routes in a Kapow server +Feature: Listing routes in a Kapow! server Listing routes allow users to know what commands are - available on a Kapow server. The List endpoint returns + available on a Kapow! server. The List endpoint returns a list of the routes the server has configured. Scenario: Listing routes on a fresh started server A fresh server, just started or with all routes removed, will show an empty list of routes. - Given I have a just started Kapow server + Given I have a just started Kapow! server When I request a routes listing Then I get an empty list @@ -16,11 +16,10 @@ Feature: Listing routes in a Kapow server After some route creation/insertion operations the server must return an ordered list of routes stored. - Given I have a Kapow server whith this routes appended: + Given I have a Kapow! server whith this routes appended: | method | url_pattern | entrypoint | command | | GET | /listRootDir | /bin/sh -c | ls -la / \| response /body | | GET | /listDir/{dirname} | /bin/sh -c | ls -la /request/params/dirname \| response /body | - When I request a routes listing Then I get a list with the following elements: | method | url_pattern | entrypoint | command | index | id | diff --git a/spec/test/features/environment.py b/spec/test/features/environment.py new file mode 100644 index 0000000..4a80ad9 --- /dev/null +++ b/spec/test/features/environment.py @@ -0,0 +1,4 @@ +def after_scenario(context, scenario): + if hasattr(context, 'server'): + context.server.terminate() + context.server.wait() diff --git a/spec/test/features/steps/steps.py b/spec/test/features/steps/steps.py new file mode 100644 index 0000000..1039b95 --- /dev/null +++ b/spec/test/features/steps/steps.py @@ -0,0 +1,71 @@ +import subprocess +from time import sleep + +import requests +from environconfig import EnvironConfig, StringVar + + +class Env(EnvironConfig): + #: How to run Kapow! server + KAPOW_SERVER_CMD = StringVar(default="kapow server") + + #: Where the Control API is + KAPOW_CONTROLAPI_URL = StringVar(default="http://localhost:8081") + + #: Where the Data API is + KAPOW_DATAAPI_URL = StringVar(default="http://localhost:8080") + + +@given('I have a just started Kapow! server') +def step_impl(context): + context.server = subprocess.Popen( + Env.KAPOW_SERVER_CMD, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + shell=True) + is_running = context.server.poll() is None + assert is_running, "Server is not running!" + + +@when('I request a routes listing') +def step_impl(context): + context.response = requests.get(f"{Env.KAPOW_CONTROLAPI_URL}/routes") + + +@then('I get an empty list') +def step_impl(context): + context.response.raise_for_status() + assert context.response.json() == [] + + +@given('I have a Kapow! server whith this routes appended') +def step_impl(context): + context.server = subprocess.Popen( + Env.KAPOW_SERVER_CMD, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + shell=True) + is_running = context.server.poll() is None + assert is_running, "Server is not running!" + + 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 a list with the following elements') +def step_impl(context): + context.response.raise_for_status() + + if not hasattr(context, 'table'): + raise RuntimeError("A table must be set for this step.") + + for entry, row in zip(context.response.json(), context.table): + for header in row.headings: + assert header in entry, f"Response does not contain the key {header}" + if row[header] != '*': + assert entry[header] == row[header], f"Values mismatch" diff --git a/spec/test/steps/steps.py b/spec/test/steps/steps.py deleted file mode 100644 index 8b13789..0000000 --- a/spec/test/steps/steps.py +++ /dev/null @@ -1 +0,0 @@ -