diff --git a/LICENCE.md b/LICENCE.md new file mode 100644 index 0000000000000000000000000000000000000000..10bb2c1d9c3f4c2ce46ae0c2dc0e06f93491a807 --- /dev/null +++ b/LICENCE.md @@ -0,0 +1,7 @@ +Copyright 2023 [NOC](https://noc.ac.uk) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. \ No newline at end of file diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000000000000000000000000000000000000..4f0114614a2dd902b8518573b9a6f67edc89eb5d --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,2 @@ +include features/*.feature +include fixtures/*.json diff --git a/README.md b/README.md index 82d8c17b2cbc397fe78afa446cb39144da88af31..0bc3abc4e468861f677d14edba434bfe01d95162 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,52 @@ # backbone-adapter-testsuite -Gherkin feature definitions and fixtures to run against all language ports of the adapter. \ No newline at end of file +Gherkin feature definitions and fixtures to run against all language ports of the backbone-adapter. + +## Contents + +### /features + +Gherkin `.feature` files describing a suite of tests which +should be run against all language ports of the adapter + +### /fixtures + +A set of fixtures to be used to configure the tests. + +## Implementations + +You can see existing implementations of the test suite here + +- [JS-Cucumber](https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-javascript) +Step definitions are in `test/cucumber` + +## Installing + +The intention is to install a tagged version of the testsuite into each adapter so that if we develop +the testsuite existing adapter implementations won't break. + +In JS the target tag/branch/commit is specified with # after the git url + +### NPM + +```bash +npm install --saveDev git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#[tag|branch|commit] +``` + +### Yarn + +```bash +yarn add --dev git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git#[tag|branch|commit] +``` + +### PIP + +In pip the target tag/branch/commit is specified with @ after the git url + +```bash +echo +"git+https://git.noc.ac.uk/communications-backbone-system/backbone-adapter-testsuite.git@[tag|branch|commit]#egg=backbone-adapter-testsuite" >> requirements.txt +pip install -r reqiurements.txt +# copy features and fixtures into test directory +copy_tests +``` diff --git a/copy_tests.py b/copy_tests.py new file mode 100644 index 0000000000000000000000000000000000000000..631c83117177a9810406e1131eaef7fe204efabe --- /dev/null +++ b/copy_tests.py @@ -0,0 +1,25 @@ +import os +from shutil import copytree +import site + + +def copy_directory(src, dest, directory): + copytree(os.path.join(src, directory), os.path.join(dest, directory)) + + +def copy(): + here = os.getcwd(); + package_locations = site.getsitepackages() + + for root_path in package_locations: + dirs = [x[0] for x in os.walk(root_path)] + for dirx in dirs: + if dirx.endswith('testsuite'): + src = dirx + dest = os.path.join(here, 'test') + copy_directory(src, dest, 'features') + copy_directory(src, dest, 'fixtures') + + +if __name__ == '__main__': + copy() \ No newline at end of file diff --git a/features/adapter_auth.feature b/features/adapter_auth.feature new file mode 100644 index 0000000000000000000000000000000000000000..c55e27199db07f4ca6145afc63ed0ddf36cad4a9 --- /dev/null +++ b/features/adapter_auth.feature @@ -0,0 +1,13 @@ +Feature: Does the adapter authenticate? + When an adapter instance is created it authenticates and receives a token + + Scenario: A token is granted with valid config + Given valid config + When the adapter instance is created + When the auth method is called + Then the adapter credentials are populated + + Scenario: Auth fails with invalid config + Given invalid config + When the adapter instance is created + Then the adapter auth fails \ No newline at end of file diff --git a/features/adapter_broadcast.feature b/features/adapter_broadcast.feature new file mode 100644 index 0000000000000000000000000000000000000000..e37344afab59cc9f6cea2ce9be74ba7bcf8ba9de --- /dev/null +++ b/features/adapter_broadcast.feature @@ -0,0 +1,18 @@ +Feature: Can the adapter broadcast messages? + The adapter publish method works as expected + + Scenario: A message can be published successfully + Given valid config + When the adapter instance is created + When the auth method is called + When a mock notify API response is configured to return success + When the broadcast method is called + Then a successful response is returned with status 200 + + Scenario: A failed publish returns a 403 + Given valid config + When the adapter instance is created + When the auth method is called + When a mock notify API response is configured to return an error + When the broadcast method is called + Then an error response is returned with status 403 \ No newline at end of file diff --git a/features/adapter_get-authorization-header.feature b/features/adapter_get-authorization-header.feature new file mode 100644 index 0000000000000000000000000000000000000000..554acdf0378c917328ca72921843c39315a1f580 --- /dev/null +++ b/features/adapter_get-authorization-header.feature @@ -0,0 +1,21 @@ +Feature: Can the adapter create an authorization header? + The adapter getAuthorizationHeader method works as expected + + Scenario: getAuthorizationHeader returns a bearer token + Given valid config + When the adapter instance is created + When the auth method is called + When the getAuthorizationHeader method is called + Then a headers object is returned containing a bearer token authorization header + + Scenario: getAuthorizationHeader implicitly calls auth if required + Given valid config + When the adapter instance is created + When the getAuthorizationHeader method is called + Then a headers object is returned containing a bearer token authorization header + + Scenario: getAuthorizationHeader implicitly calls auth if required + Given invalid config + When the adapter instance is created + When the getAuthorizationHeader method is called + Then an error response is returned with status 403 \ No newline at end of file diff --git a/features/adapter_poll.feature b/features/adapter_poll.feature new file mode 100644 index 0000000000000000000000000000000000000000..ff05566d17c68397fac3a3673a0f87412b87e60e --- /dev/null +++ b/features/adapter_poll.feature @@ -0,0 +1,42 @@ +# When the queue contains x messages +# only mocks the API response +# Testing how the API behaves with a full queue are defined in the API + +Feature: Can the adapter receive messages? + The adapter poll method works as expected + + Scenario: No messages are received successfully if the queue is empty + Given valid config + When the adapter instance is created + When the auth method is called + When a mock receive API response is configured to return 0 messages + When the poll method is called + Then a successful response is returned with 0 messages + + Scenario: 2 messages are received successfully if the queue contains 2 messages + Given valid config + When the adapter instance is created + When the auth method is called + When a mock receive API response is configured to return 2 messages + When the poll method is called + Then a successful response is returned with 2 messages + Then the protocol "validate" method is called 2 times + Then the protocol "decode" method is called 2 times + + Scenario: 10 messages are received successfully if the queue contains 10 messages + Given valid config + When the adapter instance is created + When the auth method is called + When a mock receive API response is configured to return 10 messages + When the poll method is called + Then a successful response is returned with 10 messages + Then the protocol "validate" method is called 10 times + Then the protocol "decode" method is called 10 times + + Scenario: An invalid token returns a forbidden response + Given valid config + When the adapter instance is created + When the auth method is called + When a mock receive API response is configured to return an error + When the poll method is called + Then an error response is returned with status 403 diff --git a/features/adapter_publish.feature b/features/adapter_publish.feature new file mode 100644 index 0000000000000000000000000000000000000000..7e7e7de1681a3220f61b829b457e7e35904ca7c4 --- /dev/null +++ b/features/adapter_publish.feature @@ -0,0 +1,18 @@ +Feature: Can the adapter publish messages? + The adapter publish method works as expected + + Scenario: A message can be published successfully + Given valid config + When the adapter instance is created + When the auth method is called + When a mock send API response is configured to return success + When the publish method is called + Then a successful response is returned with status 200 + + Scenario: A failed publish returns a 403 + Given valid config + When the adapter instance is created + When the auth method is called + When a mock send API response is configured to return an error + When the publish method is called + Then an error response is returned with status 403 diff --git a/features/adapter_token-valid.feature b/features/adapter_token-valid.feature new file mode 100644 index 0000000000000000000000000000000000000000..c7290a71413c627e0cefbd5df3683d482dbe8c9a --- /dev/null +++ b/features/adapter_token-valid.feature @@ -0,0 +1,27 @@ +# tokenValid returns true if there is a token and the expiry is in the future +# tokenValid returns false if the token expiry is in the past +# or if there is no token +# In the case where invalid credentials are supplied +# the auth will fail so there will be no token available + +Feature: Is the token valid? + The adapter tokenValid method works as expected + + Scenario: If adapter has not authed then the tokenValid returns false + Given valid config + When the adapter instance is created + Then tokenValid returns false + + Scenario: If credentials.expiry is in the future then the token is valid + Given valid config + When the adapter instance is created + When the auth method is called + When the token expiry is in the future + Then tokenValid returns true + + Scenario: If credentials.expiry is in the past then the token is invalid + Given valid config + When the adapter instance is created + When the auth method is called + When the token expiry is in the past + Then tokenValid returns false diff --git a/features/adapter_validate.feature b/features/adapter_validate.feature new file mode 100644 index 0000000000000000000000000000000000000000..c2af9f1ed21550495bc96952abc6793f436f236a --- /dev/null +++ b/features/adapter_validate.feature @@ -0,0 +1,16 @@ +Feature: Can the adapter validate messages? + The adapter validate method works as expected + + Scenario: A valid message is successfully validated against the protocol schema + Given valid config + Given a valid message + When the adapter instance is created + When the validate method is called + Then the message is validated successfully + + Scenario: An invalid message fails to validate against the protocol schema + Given valid config + Given an invalid message + When the adapter instance is created + When the validate method is called + Then the message fails to validate \ No newline at end of file diff --git a/features/protocol_decode.feature b/features/protocol_decode.feature new file mode 100644 index 0000000000000000000000000000000000000000..ab2a50fe616062fa715253515d6b8f5480407721 --- /dev/null +++ b/features/protocol_decode.feature @@ -0,0 +1,11 @@ +# Decode and encode are provided as stubs which are intended to be overridden +# These can be used to translate the message or to invoke other functions +# to take action based on the type and content of messages + +Feature: Decode stubs passthru message unchanged + The protocol decode method works as expected + + Scenario: Decode passes the message through unaltered + Given a valid message + When the protocol.decode method is called + Then the message is returned unaltered diff --git a/features/protocol_encode.feature b/features/protocol_encode.feature new file mode 100644 index 0000000000000000000000000000000000000000..41bb57c287ba6b3981a97399c424e2257672d3b5 --- /dev/null +++ b/features/protocol_encode.feature @@ -0,0 +1,11 @@ +# Decode and encode are provided as stubs which are intended to be overridden +# These can be used to translate the message or to invoke other functions +# to take action based on the type and content of messages + +Feature: Encode stubs passthru message unchanged + The protocol encode method works as expected + + Scenario: Encode passes the message through unaltered + Given a valid message + When the protocol.encode method is called + Then the message is returned unaltered \ No newline at end of file diff --git a/features/protocol_get-type.feature b/features/protocol_get-type.feature new file mode 100644 index 0000000000000000000000000000000000000000..829ae6da8dfa6ed632a90007a0c11f3496e4449b --- /dev/null +++ b/features/protocol_get-type.feature @@ -0,0 +1,12 @@ +Feature: Can the protocol determine message type + The protocol getType method works as expected + + Scenario: A valid message is successfully typed + Given a valid message + When protocol getType is called + Then getType returns message.payload.message_type if present + + Scenario: An invalid message returns type:null + Given an invalid message + When protocol getType is called + Then getType returns null if message.payload.message_type is not present diff --git a/features/protocol_validate.feature b/features/protocol_validate.feature new file mode 100644 index 0000000000000000000000000000000000000000..a5293c46dc081a864a4e5ec3fd059175b65340a7 --- /dev/null +++ b/features/protocol_validate.feature @@ -0,0 +1,12 @@ +Feature: Can the protocol validate messages? + The adapter validate method works as expected + + Scenario: A valid message is successfully validated against the protocol schema + Given a valid message + When the protocol.validate method is called + Then the message is validated successfully + + Scenario: An invalid message fails to validate against the protocol schema + Given an invalid message + When the protocol.validate method is called + Then the message fails to validate \ No newline at end of file diff --git a/features/schema_validate.feature b/features/schema_validate.feature new file mode 100644 index 0000000000000000000000000000000000000000..34f457a2543f1bf91948a4e367945e6565a985a9 --- /dev/null +++ b/features/schema_validate.feature @@ -0,0 +1,10 @@ +# If the mock schema fixture fails to validate +# it can cause invalid messages to show as valid + +Feature: Is the mock schema valid? + The mock schema must validate in order for the adapter test to work + + Scenario: The schema matches the OpenAPI specification + Given the test schema + When it is validated + Then it matches the OpenAPI specification \ No newline at end of file diff --git a/fixtures/config-invalid.json b/fixtures/config-invalid.json new file mode 100644 index 0000000000000000000000000000000000000000..82625f6ebf737eb84933aa05088f3047bcebbd62 --- /dev/null +++ b/fixtures/config-invalid.json @@ -0,0 +1,7 @@ +{ + "api": "https://example.backbone.com/api", + "client_id": "invalid-client-id", + "client_name": "InvalidClientName", + "subscription": "dot.delimited.topic.subscription.#", + "secret": "TheCollaredDoveCoosInTheChimneyPot" +} \ No newline at end of file diff --git a/fixtures/config-valid.json b/fixtures/config-valid.json new file mode 100644 index 0000000000000000000000000000000000000000..8b765ecfafaa8867795121d123534b56a58e8d43 --- /dev/null +++ b/fixtures/config-valid.json @@ -0,0 +1,7 @@ +{ + "api": "https://example.backbone.com/api", + "client_id": "unique-client-id", + "client_name": "UniqueClientName", + "subscription": "dot.delimited.topic.subscription.#", + "secret": "TheGeeseFlySouthInWinter" +} \ No newline at end of file diff --git a/fixtures/message-vehicle-status-invalid.json b/fixtures/message-vehicle-status-invalid.json new file mode 100644 index 0000000000000000000000000000000000000000..c59f56d02636abfa7d3b52a7b028126d0f25d2fc --- /dev/null +++ b/fixtures/message-vehicle-status-invalid.json @@ -0,0 +1,20 @@ +{ + "metadata": { + "source": "ae", + "destination": "soar.po.ecosub.eco1", + "delivery_type": "publish", + "message_id": "test" + }, + "payload": { + "messagetype": "VehicleStatus", + "operatorID": 1, + "vehicleID": 12, + "coordinates": { + "latitude": "monkeys", + "longitude": "janvier", + "depth": "twenty five metres please", + "projection": 4326 + }, + "battery_percentage": "plenty" + } +} \ No newline at end of file diff --git a/fixtures/message-vehicle-status.json b/fixtures/message-vehicle-status.json new file mode 100644 index 0000000000000000000000000000000000000000..55cdd818781ab404ebf70d3087a7f6c83396cc3a --- /dev/null +++ b/fixtures/message-vehicle-status.json @@ -0,0 +1,20 @@ +{ + "metadata": { + "source": "ae", + "destination": "soar.po.ecosub.eco1", + "delivery_type": "publish", + "message_id": "test" + }, + "payload": { + "message_type": "VehicleStatus", + "operator_id": "po", + "vehicle_id": "eco1", + "coordinates": { + "latitude": 57.234, + "longitude": -8.432, + "depth": 50, + "projection": 4326 + }, + "battery_percentage": 64 + } +} \ No newline at end of file diff --git a/fixtures/response-denied-token.json b/fixtures/response-denied-token.json new file mode 100644 index 0000000000000000000000000000000000000000..d5fb311657bbafe5d89d35d1a24313c0ad784feb --- /dev/null +++ b/fixtures/response-denied-token.json @@ -0,0 +1,3 @@ +{ + "message": "Invalid client credentials" +} \ No newline at end of file diff --git a/fixtures/response-valid-token.json b/fixtures/response-valid-token.json new file mode 100644 index 0000000000000000000000000000000000000000..87306ff195913ce9ba866065ede968128439d3fa --- /dev/null +++ b/fixtures/response-valid-token.json @@ -0,0 +1,4 @@ +{ + "token": "gAAAAABjwB-vxtER44M2en6xYyt7G1WXp8QwfsiHw-ijCqNBZpQPwxxrBHzUU1fQ9lfPPo4QHj50p-yh203dV6zLLoTzuiReqGzE2InqAxOwv4gddlQWNFJKyrmg4mVVMX2VZe2cCAljmHxEo66BHgt_T24AieedMnI4VR2kw4SFiooFv5nr2W8=", + "expiry": "2030-12-31T23:59:59.000000" +} \ No newline at end of file diff --git a/fixtures/schema-swagger.json b/fixtures/schema-swagger.json new file mode 100644 index 0000000000000000000000000000000000000000..c0638af1d59fe560ca0cacb7533ffe37e5918ca6 --- /dev/null +++ b/fixtures/schema-swagger.json @@ -0,0 +1,252 @@ +{ + "openapi": "3.0.3", + "info": { + "title": "soar", + "version": "1.0", + "description": "SoAR message schemas" + }, + "paths": {}, + "components": { + "schemas": { + "Message": { + "properties": { + "metadata": { + "$ref": "#/components/schemas/Metadata" + }, + "payload": { + "$ref": "#/components/schemas/Payload" + } + }, + "required": ["metadata", "payload"] + }, + "Payload": { + "discriminator": { + "propertyName": "message_type", + "mapping": { + "VehicleStatus": "#/components/schemas/VehicleStatus", + "VehicleMission": "#/components/schemas/VehicleMission", + "AreaOfInterest": "#/components/schemas/AreaOfInterest" + } + }, + "oneOf": [ + { + "$ref": "#/components/schemas/VehicleStatus" + }, + { + "$ref": "#/components/schemas/VehicleMission" + }, + { + "$ref": "#/components/schemas/AreaOfInterest" + } + ] + }, + "Metadata": { + "properties": { + "source": { + "type": "string", + "description": "The sender.", + "example": "autonomy-engine" + }, + "destination": { + "type": "string", + "description": "Publisher topic.", + "example": "soar.noc.autosub.ah1.status" + }, + "delivery_type": { + "type": "string", + "description": "Published or broadcast", + "enum": ["broadcast", "publish"], + "example": "2.0.0" + }, + "message_id": { + "type": "string", + "description": "An identifier for the type of message received.", + "example": "VehicleStatus" + } + }, + "required": ["source","destination","message_id"], + "type": "object" + }, + "Coordinates": { + "properties": { + "latitude": { + "type": "number", + "description": "Latitude in decimal degrees.", + "example": 54.234 + }, + "longitude": { + "type": "number", + "description": "Longitude in decimal degrees.", + "example": -1.432 + }, + "depth": { + "type": "number", + "description": "Target depth", + "default": 0, + "example": 50 + }, + "altitude": { + "type": "number", + "description": "Target altitude above bottom", + "default": 0, + "example": 50 + }, + "projection": { + "type": "integer", + "description": "EPSG Projection Code", + "example": 4326, + "default": 4326 + } + }, + "required": ["latitude", "longitude"], + "type": "object" + }, + "VehicleStatus": { + "properties": { + "message_type": { + "type": "string", + "description": "An identifier for the payload type.", + "example": "VehicleStatus", + "enum": ["VehicleStatus"] + }, + "operator_id": { + "type": "string", + "description": "An identifier for the operator.", + "example": "noc" + }, + "vehicle_id": { + "type": "string", + "description": "An identifier for the vehicle.", + "example": "noc_ah1" + }, + "coordinates": { + "$ref": "#/components/schemas/Coordinates" + }, + "battery_percentage": { + "type": "number", + "description": "The remaining battery capacity.", + "example": 64 + } + }, + "required": ["message_type", "operator_id", "vehicle_id", "coordinates", "battery_percentage"], + "type": "object" + }, + "VehicleMission": { + "properties": { + "message_type": { + "type": "string", + "description": "An identifier for the payload type.", + "example": "VehicleMission", + "enum": ["VehicleMission"] + }, + "operator_id": { + "type": "string", + "description": "An identifier for the operator.", + "example": "noc" + }, + "vehicle_id": { + "type": "string", + "description": "An identifier for the vehicle.", + "example": "noc_ah1" + }, + "coordinates": { + "$ref": "#/components/schemas/Coordinates" + }, + "actions": { + "type": "array", + "items": { + "discriminator": { + "propertyName": "action_type", + "mapping": { + "GoToWaypoint": "#/components/schemas/GoToWaypoint", + "DescendToAltitude": "#/components/schemas/DescendToAltitude", + "AscendToSurface": "#/components/schemas/AscendToSurface" + } + }, + "oneOf": [ + { + "$ref": "#/components/schemas/GoToWaypoint" + }, + { + "$ref": "#/components/schemas/DescendToAltitude" + }, + { + "$ref": "#/components/schemas/AscendToSurface" + } + ] + } + } + }, + "required": ["message_type", "operator_id", "vehicle_id", "coordinates", "actions"], + "type": "object" + }, + "AreaOfInterest": { + "properties": { + "message_type": { + "type": "string", + "description": "An identifier for the payload type.", + "example": "AreaOfInterest", + "enum": ["AreaOfInterest"] + }, + "operator_id": { + "type": "string", + "description": "An identifier for the operator.", + "example": "noc" + }, + "vehicle_id": { + "type": "string", + "description": "An identifier for the vehicle.", + "example": "noc_ah1" + }, + "coordinates": { + "$ref": "#/components/schemas/Coordinates" + } + }, + "required": ["message_type", "operator_id", "vehicle_id", "coordinates"], + "type": "object" + }, + "GoToWaypoint": { + "properties": { + "action_type": { + "type": "string", + "description": "An identifier for the payload type.", + "example": "GoToWaypoint", + "enum": ["GoToWaypoint"] + }, + "coordinates": { + "$ref": "#/components/schemas/Coordinates" + } + }, + "type": "object" + }, + "DescendToAltitude": { + "properties": { + "action_type": { + "type": "string", + "description": "An identifier for the payload type.", + "example": "DescendToAltitude", + "enum": ["DescendToAltitude"] + }, + "coordinates": { + "$ref": "#/components/schemas/Coordinates" + } + }, + "type": "object" + }, + "AscendToSurface": { + "properties": { + "action_type": { + "type": "string", + "description": "An identifier for the payload type.", + "example": "AscendToSurface", + "enum": ["AscendToSurface"] + }, + "coordinates": { + "$ref": "#/components/schemas/Coordinates" + } + }, + "type": "object" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000000000000000000000000000000000000..6cc5121f7b712e073fbd85317e8d24fe0e041326 --- /dev/null +++ b/package.json @@ -0,0 +1,24 @@ +{ + "name": "backbone-adapter-testsuite", + "version": "0.0.1", + "description": "Common test suite to run on all adapter language ports", + "repository": { + "type": "git", + "url": "git@git.noc.ac.uk:communications-backbone-system/backbone-adapter-testsuite.git" + }, + "contributors": [ + { + "name": "James Kirk", + "email": "james.kirk@noc.ac.uk" + }, + { + "name": "Dan Jones", + "email": "dan.jones@noc.ac.uk" + }, + { + "name": "Trishna Saeharaseelan", + "email": "trishna.saeharaseelan@noc.ac.uk" + } + ], + "license": "MIT" +} diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000000000000000000000000000000000000..a8aeb66e297d4cc00b2a3a959536d842f9ec6b7e --- /dev/null +++ b/setup.cfg @@ -0,0 +1,3 @@ +[options.entry_points] +console_scripts= + copy_tests = testsuite.copy_tests:copy \ No newline at end of file diff --git a/setup.py b/setup.py new file mode 100644 index 0000000000000000000000000000000000000000..b025934aa2d98dc14581deb0665f623266d8a088 --- /dev/null +++ b/setup.py @@ -0,0 +1,14 @@ +from setuptools import setup + + +setup( + name='backbone_adapter_testsuite', + packages=['testsuite'], + package_dir={ + 'testsuite': '.', + }, + package_data={ + 'testsuite': ['features/*.feature', 'fixtures/*.json'], + }, + include_package_data=True, +)