Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
d7731cb
feat: message provider implementation
tuan-pham Jan 20, 2021
f566814
feat: add basic verify flow
tuan-pham Jan 21, 2021
02f307e
feat: add missing flask config
tuan-pham Jan 21, 2021
f05c2c7
feat: initial flask start setup
williaminfante Jan 21, 2021
efa2523
feat: and sample provider test, rename MessageProvider constructor
tuan-pham Jan 22, 2021
82106a6
feat: move handler to provider
williaminfante Jan 22, 2021
b2f3e1f
feat: pass handler as python argument
williaminfante Jan 22, 2021
a955fef
feat: pass handler as python argument
williaminfante Jan 22, 2021
597ce1e
feat: create setup endpoint for message handlers, add setup_state fn,…
tuan-pham Jan 25, 2021
edf1ade
feat: enable context manager in message provider, allow provider to p…
tuan-pham Jan 26, 2021
491a239
fix: flake8
tuan-pham Jan 26, 2021
59049d3
fix: revert bad merge to http_proxy; add pydocstyle
tuan-pham Jan 26, 2021
f3afbbc
feat: parse content, update readme and test
williaminfante Jan 27, 2021
69bac3d
test: add missing tests for message provider
tuan-pham Jan 27, 2021
36699af
fix: check the pact files exists before running the vefivication
tuan-pham Jan 28, 2021
1db9531
fix: flake8
tuan-pham Jan 28, 2021
f4be491
feat: rebase feature from master branch
tuan-pham Jan 29, 2021
5c16017
fix: remove dead code
tuan-pham Jan 29, 2021
d97c425
feat: add http_proxy test, replace print with log, use flask localsta…
tuan-pham Jan 31, 2021
171966c
fix: change PROXY_PORT to 1234 to fix broken build
tuan-pham Jan 31, 2021
b417bb1
fix: flake8
tuan-pham Jan 31, 2021
ebc6996
chore: skip provider test to make the build pass (troubleshooting)
tuan-pham Jan 31, 2021
7de781a
chore: skip 2 tests that causes BrokenPipeError for investigation
tuan-pham Jan 31, 2021
6a3bca7
chore: comment out the broken tests
tuan-pham Jan 31, 2021
cfe08af
fix: change default proxy port to 1234
tuan-pham Jan 31, 2021
51b3a79
feat: updated message consumer test to be compatible with message pro…
pulphix Jul 27, 2021
57f07ce
fix: Updated tests that where failing on master branch as well
pulphix Jul 27, 2021
ab24e70
feat: ported message_provider to fastapi
pulphix Jul 27, 2021
5245537
fix: fixed comment
pulphix Jul 27, 2021
8a07589
fix: removed unnecessary pact file updated flask
pulphix Jul 27, 2021
938c4da
fix: added detectcontentlambda-contentprovider.json to .gitignore
pulphix Jul 27, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# pact-python specific ignores
e2e/pacts
userserviceclient-userservice.json
detectcontentlambda-contentprovider.json
pact/bin

# Byte-compiled / optimized / DLL files
Expand Down
6 changes: 2 additions & 4 deletions examples/message/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class MessageHandler(object):
```

Below is a snippet from a test where the message handler has no error.
Since the expected event contains a key `documentType` with value `microsoft-word`, message handler does not throw an error and a pact file `f"pacts/{expected_json}"` is expected to be generated.
Since the expected event contains a key `documentType` with value `microsoft-word`, message handler does not throw an error and a pact file `f"{PACT_FILE}""` is expected to be generated.

```python
def test_generate_new_pact_file(pact):
Expand Down Expand Up @@ -69,7 +69,7 @@ def test_generate_new_pact_file(pact):
assert isfile(f"{PACT_FILE}") == 1
```

For a similar test where the event does not contain a key `documentType` with value `microsoft-word`, a `CustomError` is generated and there there is no generated json file `f"pacts/{expected_json}"`.
For a similar test where the event does not contain a key `documentType` with value `microsoft-word`, a `CustomError` is generated and there there is no generated json file `f"{PACT_FILE}"`.

```python
def test_throw_exception_handler(pact):
Expand Down Expand Up @@ -97,8 +97,6 @@ def test_throw_exception_handler(pact):
assert isfile(f"{PACT_FILE}") == 0
```

Otherwise, no pact file is generated.

## Provider

Note: The current example only tests the consumer side.
Expand Down
64 changes: 34 additions & 30 deletions examples/message/tests/consumer/test_message_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,17 @@
PACT_BROKER_URL = "http://localhost"
PACT_BROKER_USERNAME = "pactbroker"
PACT_BROKER_PASSWORD = "pactbroker"
PACT_DIR = 'pacts'
PACT_DIR = "pacts"

CONSUMER_NAME = 'DetectContentLambda'
PROVIDER_NAME = 'ContentProvider'
PACT_FILE = (f"{PACT_DIR}/{CONSUMER_NAME.lower().replace(' ', '_')}_message-"
+ f"{PROVIDER_NAME.lower().replace(' ', '_')}_message.json")
CONSUMER_NAME = "DetectContentLambda"
PROVIDER_NAME = "ContentProvider"
PACT_FILE = (f"{PACT_DIR}/{CONSUMER_NAME.lower().replace(' ', '_')}-"
+ f"{PROVIDER_NAME.lower().replace(' ', '_')}.json")

@pytest.fixture(scope='session')

@pytest.fixture(scope="session")
def pact(request):
version = request.config.getoption('--publish-pact')
version = request.config.getoption("--publish-pact")
publish = True if version else False

pact = MessageConsumer(CONSUMER_NAME, version=version).has_pact_with(
Expand Down Expand Up @@ -54,57 +55,59 @@ def progressive_delay(file, time_to_wait=10, second_interval=0.5, verbose=False)
time.sleep(second_interval)
time_counter += 1
if verbose:
print(f'Trying for {time_counter*second_interval} seconds')
print(f"Trying for {time_counter*second_interval} seconds")
if time_counter > time_to_wait:
if verbose:
print(f'Already waited {time_counter*second_interval} seconds')
print(f"Already waited {time_counter*second_interval} seconds")
break


def test_throw_exception_handler(pact):
cleanup_json(PACT_FILE)

wrong_event = {
'documentName': 'spreadsheet.xls',
'creator': 'WI',
'documentType': 'microsoft-excel'
"event": "ObjectCreated:Put",
"documentName": "spreadsheet.xls",
"creator": "WI",
"documentType": "microsoft-excel"
}

(pact
.given('Another document in Document Service')
.expects_to_receive('Description')
.given("Document unsupported type")
.expects_to_receive("Description")
.with_content(wrong_event)
.with_metadata({
'Content-Type': 'application/json'
"Content-Type": "application/json"
}))

with pytest.raises(CustomError):
with pact:
# handler needs 'documentType' == 'microsoft-word'
# handler needs "documentType" == "microsoft-word"
MessageHandler(wrong_event)

progressive_delay(f"{PACT_FILE}")
assert isfile(f"{PACT_FILE}") == 0


def test_generate_new_pact_file(pact):
def test_put_file(pact):
cleanup_json(PACT_FILE)

expected_event = {
'documentName': 'document.doc',
'creator': 'TP',
'documentType': 'microsoft-word'
"event": "ObjectCreated:Put",
"documentName": "document.doc",
"creator": "TP",
"documentType": "microsoft-word"
}

(pact
.given('A document create in Document Service')
.expects_to_receive('Description')
.given("A document created successfully")
.expects_to_receive("Description")
.with_content(expected_event)
.with_metadata({
'Content-Type': 'application/json'
"Content-Type": "application/json"
}))

with pact:
# handler needs 'documentType' == 'microsoft-word'
MessageHandler(expected_event)

progressive_delay(f"{PACT_FILE}")
Expand All @@ -121,17 +124,18 @@ def test_publish_to_broker(pact):
`pytest tests/consumer/test_message_consumer.py::test_publish_pact_to_broker --publish-pact 2`
"""
expected_event = {
'documentName': 'document.doc',
'creator': 'TP',
'documentType': 'microsoft-word'
"event": "ObjectCreated:Delete",
"documentName": "document.doc",
"creator": "TP",
"documentType": "microsoft-word"
}

(pact
.given('A document create in Document Service with broker')
.expects_to_receive('Description with broker')
.given("A document deleted successfully")
.expects_to_receive("Description with broker")
.with_content(expected_event)
.with_metadata({
'Content-Type': 'application/json'
"Content-Type": "application/json"
}))

with pact:
Expand Down
Empty file.
51 changes: 51 additions & 0 deletions examples/message/tests/provider/test_message_provider.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import pytest
from pact import MessageProvider


def document_created_handler():
return {
"event": "ObjectCreated:Put",
"documentName": "document.doc",
"creator": "TP",
"documentType": "microsoft-word"
}


def document_deleted_handler():
return {
"event": "ObjectCreated:Delete",
"documentName": "document.doc",
"creator": "TP",
"documentType": "microsoft-word"
}


def test_verify_success():
provider = MessageProvider(
message_providers={
'A document created successfully': document_created_handler,
'A document deleted successfully': document_deleted_handler
},
provider='ContentProvider',
consumer='DetectContentLambda',
pact_dir='pacts'

)
with provider:
provider.verify()


def test_verify_failure_when_a_provider_missing():
provider = MessageProvider(
message_providers={
'A document created successfully': document_created_handler,
},
provider='ContentProvider',
consumer='DetectContentLambda',
pact_dir='pacts'

)

with pytest.raises(AssertionError):
with provider:
provider.verify()
6 changes: 4 additions & 2 deletions pact/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,15 @@
from .broker import Broker
from .consumer import Consumer
from .matchers import EachLike, Like, SomethingLike, Term, Format
from .message_pact import MessagePact
from .message_consumer import MessageConsumer
from .message_pact import MessagePact
from .message_provider import MessageProvider
from .pact import Pact
from .provider import Provider
from .verifier import Verifier

from .__version__ import __version__ # noqa: F401

__all__ = ('Broker', 'Consumer', 'EachLike', 'Like', 'MessageConsumer', 'MessagePact',
__all__ = ('Broker', 'Consumer', 'EachLike', 'Like',
'MessageConsumer', 'MessagePact', 'MessageProvider',
'Pact', 'Provider', 'SomethingLike', 'Term', 'Format', 'Verifier')
2 changes: 1 addition & 1 deletion pact/broker.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def publish(self, consumer_name, version, pact_dir=None,
for tag in consumer_tags:
command.extend(['-t', tag])

print(f"PactBroker command: {command}")
log.debug(f"PactBroker publish command: {command}")

publish_process = Popen(command)
publish_process.wait()
Expand Down
58 changes: 58 additions & 0 deletions pact/http_proxy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
"""Http Proxy to be used as provider url in verifier."""
from fastapi import FastAPI, status, Request, HTTPException
import uvicorn as uvicorn
import logging
log = logging.getLogger(__name__)
logging.basicConfig(level=logging.DEBUG)

app = FastAPI()
PROXY_PORT = 1234
UVICORN_LOGGING_LEVEL = "error"
items = {
"states": None
}


def _match_states(payload):
"""Match states in payload against stored message handlers."""
log.debug(f'Find handler from payload: {payload}')
handlers = items["states"]
states = handlers['messageHandlers']
log.debug(f'Setup states: {handlers}')
provider_states = payload['providerStates']

for state in provider_states:
matching_state = state['name']
if matching_state in states:
return states[matching_state]
raise HTTPException(status_code=500, detail='No matched handler.')


@app.post("/")
async def root(request: Request):
"""Match states with provided message handlers."""
payload = await request.json()
message = _match_states(payload)
return {'contents': message}


@app.get('/ping', status_code=status.HTTP_200_OK)
def ping():
"""Check whether the server is available before setting up states."""
return {"ping": "pong"}


@app.post("/setup", status_code=status.HTTP_201_CREATED)
async def setup(request: Request):
"""Endpoint to setup states.

Use localstack to store payload.
"""
payload = await request.json()
items["states"] = payload
return items["states"]


def run_proxy():
"""Rub HTTP Proxy."""
uvicorn.run("pact.http_proxy:app", port=PROXY_PORT, log_level=UVICORN_LOGGING_LEVEL)
7 changes: 4 additions & 3 deletions pact/message_pact.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from .broker import Broker
from .constants import MESSAGE_PATH
from .matchers import from_term


class MessagePact(Broker):
Expand Down Expand Up @@ -136,7 +137,7 @@ def with_content(self, contents):
:rtype: Pact
"""
self._insert_message_if_complete()
self._messages[0]['contents'] = contents
self._messages[0]['contents'] = from_term(contents)
return self

def expects_to_receive(self, description):
Expand Down Expand Up @@ -165,8 +166,8 @@ def write_to_pact_file(self):
json.dumps(self._messages[0]),
"--pact-dir", self.pact_dir,
f"--pact-specification-version={self.version}",
"--consumer", f"{self.consumer.name}_message",
"--provider", f"{self.provider.name}_message",
"--consumer", f"{self.consumer.name}",
"--provider", f"{self.provider.name}",
]

self._message_process = Popen(command)
Expand Down
Loading