Skip to content

Commit

Permalink
[feature/PI-129-postman_collection] Locally generated postman collection
Browse files Browse the repository at this point in the history
  • Loading branch information
jaklinger committed Dec 27, 2023
1 parent 1a2e9f2 commit 9291b67
Show file tree
Hide file tree
Showing 17 changed files with 98 additions and 69 deletions.
93 changes: 49 additions & 44 deletions feature_tests/end_to_end/environment.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import json
from contextlib import nullcontext
from pathlib import Path

from behave import use_fixture
from behave.model import Feature, Scenario, Step
Expand All @@ -11,74 +12,78 @@
mock_requests,
)
from feature_tests.end_to_end.steps.postman import (
FeatureItem,
BASE_URL,
PostmanCollection,
ScenarioItem,
StepItem,
PostmanItem,
)
from feature_tests.feature_test_helpers import TestMode
from test_helpers.aws_session import aws_session
from test_helpers.constants import PROJECT_ROOT
from test_helpers.dynamodb import clear_dynamodb_table
from test_helpers.terraform import read_terraform_output

PATH_TO_HERE = Path(__file__).parent
LOCAL_TABLE_NAME = "my-table"
GENERIC_SCENARIO_DESCRIPTION = "The following steps demonstrate this scenario:"


def before_all(context: Context):
context.postman_collection = PostmanCollection()
context.test_mode = TestMode.parse(config=context.config)
context.table_name = LOCAL_TABLE_NAME
context.base_url = BASE_URL
context.session = nullcontext
context.headers = {}

test_mode = TestMode.parse(config=context.config)
if test_mode is not TestMode.INTEGRATION:
use_fixture(mock_requests, context=context)
use_fixture(mock_dynamodb, context=context, table_name="my-table")
use_fixture(mock_environment, context=context, table_name="my-table")


def after_all(context: Context):
test_mode = TestMode.parse(config=context.config)
if context.test_mode is TestMode.INTEGRATION:
context.table_name = read_terraform_output("dynamodb_table_name.value")
context.base_url = read_terraform_output("invoke_url.value") + "/"
context.session = aws_session

if context.postman_collection.item and test_mode is TestMode.INTEGRATION:
postman_collection = context.postman_collection.dict(
exclude_none=True, by_alias=True
)
with open(PROJECT_ROOT / "postman-collection.json", "w") as f:
json.dump(fp=f, obj=postman_collection, indent=4)
if context.test_mode is TestMode.LOCAL:
use_fixture(mock_requests, context=context)
use_fixture(mock_dynamodb, context=context, table_name=context.table_name)
use_fixture(mock_environment, context=context, table_name=context.table_name)


def before_feature(context: Context, feature: Feature):
context.postman_feature = FeatureItem(name=feature.name)
context.postman_feature = PostmanItem(
name=feature.name,
description=" ".join(feature.description),
)


def after_feature(context: Context, scenario: Scenario):
context.postman_collection.item.append(context.postman_feature)
def before_scenario(context: Context, scenario: Scenario):
context.postman_scenario = PostmanItem(
name=scenario.name,
description=GENERIC_SCENARIO_DESCRIPTION,
)
with context.session():
client = dynamodb_client()
clear_dynamodb_table(client=client, table_name=context.table_name)


def before_scenario(context: Context, scenario: Scenario):
context.postman_scenario = ScenarioItem(name=scenario.name)
def before_step(context: Context, step: Step):
context.postman_step = PostmanItem(
name=f"{step.keyword.lower().title()} {step.name}", item=None
)

context.headers = {}

test_mode = TestMode.parse(config=context.config)
if test_mode is TestMode.INTEGRATION:
table_name = read_terraform_output("dynamodb_table_name.value")
context.base_url = read_terraform_output("invoke_url.value") + "/"
with aws_session():
client = dynamodb_client()
clear_dynamodb_table(client=client, table_name=table_name)
else:
context.base_url = ""
client = dynamodb_client()
clear_dynamodb_table(client=client, table_name="my-table")
def after_step(context: Context, step: Step):
context.table = None
if context.postman_step:
context.postman_scenario.item.append(context.postman_step)


def after_scenario(context: Context, scenario: Scenario):
context.postman_feature.item.append(context.postman_scenario)
context.headers = {}
if context.postman_scenario:
context.postman_feature.item.append(context.postman_scenario)


def before_step(context: Context, step: Step):
context.postman_step = StepItem(name=f"{step.keyword.lower().title()} {step.name}")
def after_feature(context: Context, feature: Feature):
if context.postman_feature:
context.postman_collection.item.append(context.postman_feature)


def after_step(context: Context, step: Step):
if context.postman_step.request is not None:
context.postman_scenario.item.append(context.postman_step)
context.table = None
def after_all(context: Context):
context.postman_collection.save(path=PATH_TO_HERE)
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Feature: Create Device - failure scenarios
These scenarios demonstrate failures to create a new Device

Background:
Given "default" request headers:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Feature: Create Device - success scenarios
These scenarios demonstrate successful Device creation

Background:
Given "default" request headers:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Feature: Create Product Team - failure scenarios
These scenarios demonstrate failures to create a new Product Team (FHIR "Organization")

Background:
Given "default" request headers:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Feature: Create Product Team - success scenarios
These scenarios demonstrate successful Product Team (FHIR "Organization") creation

Background:
Given "default" request headers:
Expand Down
1 change: 1 addition & 0 deletions feature_tests/end_to_end/features/headers.failure.feature
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Feature: Headers - failure scenarios
These scenarios demonstrate invalid header values

Scenario: Version is missing
Given "bad" request headers:
Expand Down
1 change: 1 addition & 0 deletions feature_tests/end_to_end/features/headers.success.feature
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Feature: Headers - success scenarios
These scenarios demonstrate valid header values, and associated behaviour

Scenario Outline: Headers are case insensitive
Given "default" request headers:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Feature: Read Device - failure scenarios
These scenarios demonstrate failures from the GET Device endpoint

Background:
Given "default" request headers:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Feature: Read Device - success scenarios
These scenarios demonstrate successful reads from the GET Device endpoint

Background:
Given "default" request headers:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Feature: Read Product Team - failure scenarios
These scenarios demonstrate failures from the GET Organization (i.e. Product Team) endpoint

Background:
Given "default" request headers:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Feature: Read Product Team - success scenarios
These scenarios demonstrate successful reads from the GET Organization (i.e. Product Team) endpoint

Background:
Given "default" request headers:
Expand Down
1 change: 1 addition & 0 deletions feature_tests/end_to_end/features/status.success.feature
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
Feature: Status
These scenarios demonstrate the expected behaviour of the status endpoint

Background:
Given "default" request headers:
Expand Down
18 changes: 9 additions & 9 deletions feature_tests/end_to_end/steps/context.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
from dataclasses import dataclass
from typing import ContextManager

from behave.model import Table
from behave.runner import Context as BehaveContext
from requests import Response

from feature_tests.end_to_end.steps.postman import (
FeatureItem,
PostmanCollection,
ScenarioItem,
StepItem,
)
from feature_tests.end_to_end.steps.postman import PostmanCollection, PostmanItem
from feature_tests.feature_test_helpers import TestMode


@dataclass
Expand All @@ -18,8 +15,11 @@ class Context(BehaveContext):
headers: dict[str, dict[str, str]] = None
response: Response = None
table: Table = None
test_mode: TestMode = None
table_name: str = None
session: ContextManager = None

postman_collection: PostmanCollection = None
postman_feature: FeatureItem = None
postman_scenario: ScenarioItem = None
postman_step: StepItem = None
postman_feature: PostmanItem = None
postman_scenario: PostmanItem = None
postman_step: PostmanItem = None
36 changes: 23 additions & 13 deletions feature_tests/end_to_end/steps/postman.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
import json
from pathlib import Path
from typing import Literal, Optional

from pydantic import BaseModel, Field

POSTMAN_COLLECTION_FILENAME = "postman-collection.json"
BASE_URL = r"{{baseUrl}}/"
OPTIONS = lambda: {"raw": {"language": "json"}}


Expand Down Expand Up @@ -30,31 +34,37 @@ class PostmanRequest(BaseModel):
url: Url


class StepItem(BaseModel):
class PostmanItem(BaseModel):
name: str
request: PostmanRequest = None
description: str = Field(default_factory=str)
request: Optional[PostmanRequest] = None
item: None | list["PostmanItem"] = Field(default_factory=list)


class ScenarioItem(BaseModel):
name: str
item: list[StepItem] = Field(default_factory=list)


class FeatureItem(BaseModel):
name: str
item: list[ScenarioItem] = Field(default_factory=list)
def __bool__(self):
return bool(self.item) or bool(self.request)


class Info(BaseModel):
name: Literal["connecting-party-manager"] = "connecting-party-manager"
name: Literal["Feature Tests"] = "Feature Tests"
the_schema: Literal[
"https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
] = Field(
default="https://schema.getpostman.com/json/collection/v2.1.0/collection.json",
alias="schema",
)
description: Literal[
"The following 'Gherkin' Features demonstrate the behaviour of this API"
] = "The following 'Gherkin' Features demonstrate the behaviour of this API"


class PostmanCollection(BaseModel):
info: Info = Field(default_factory=Info)
item: list[FeatureItem] = Field(default_factory=list)
item: list[PostmanItem] = Field(default_factory=list)

def save(self, path: Path):
collection = self.dict(exclude_none=True, by_alias=True)
with open(path / POSTMAN_COLLECTION_FILENAME, "w") as f:
json.dump(fp=f, obj=collection, indent=4)

def __bool__(self):
return bool(self.item)
3 changes: 2 additions & 1 deletion feature_tests/end_to_end/steps/requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -67,9 +67,10 @@ def _mocked_request(
):
"""Implement the desired mocked behaviour of the 'request' function"""
endpoint_lambda_mapping = get_endpoint_lambda_mapping()
_, path = url.split(sep="/", maxsplit=1)
path_params, query_params, handler = parse_api_path(
method=method,
path=url,
path=path,
endpoint_lambda_mapping=endpoint_lambda_mapping,
)

Expand Down
4 changes: 2 additions & 2 deletions feature_tests/end_to_end/steps/tests/test_mock_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ def test__mock_requests():
},
):
response = make_request(
base_url="my_url/my_id/my_something",
base_url="BASE_URL/my_url/my_id/my_something",
http_method="GET",
endpoint="/the_endpoint",
body={"key": "value"},
Expand All @@ -36,5 +36,5 @@ def test__mock_requests():
},
"status_code": 200,
"reason": "OK",
"url": "my_url/my_id/my_something/the_endpoint",
"url": "BASE_URL/my_url/my_id/my_something/the_endpoint",
}
2 changes: 2 additions & 0 deletions scripts/builder/build.mk
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
.PHONY: build

BUILD_TIMESTAMP = $(TIMESTAMP_DIR)/.build.stamp
POSTMAN_COLLECTION = $(CURDIR)/feature_tests/end_to_end/postman-collection.json

clean: poetry--clean swagger--clean fhir--models--clean ## Complete clear-out of the project installation and artifacts
[[ -d $(TIMESTAMP_DIR) ]] && rm -r $(TIMESTAMP_DIR) || :
[[ -d $(DOWNLOADS_DIR) ]] && rm -r $(DOWNLOADS_DIR) || :
[[ -f $(POSTMAN_COLLECTION) ]] && rm $(POSTMAN_COLLECTION) || :

clean--build:
[[ -f $(BUILD_TIMESTAMP) ]] && rm $(BUILD_TIMESTAMP) || :
Expand Down

0 comments on commit 9291b67

Please sign in to comment.