Skip to content

Commit b629e90

Browse files
committed
integration tests
1 parent e456259 commit b629e90

6 files changed

Lines changed: 119 additions & 54 deletions

File tree

cargo_api.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ def sync(self, shipment: Shipment) -> None:
1919

2020

2121
class RealCargoAPI:
22-
API_URL = 'https://example.org'
22+
def __init__(self, api_url='https://example.org'):
23+
self.api_url = api_url
2324

2425

2526
def get_latest_eta(self, reference: str) -> date:
@@ -31,37 +32,40 @@ def get_latest_eta(self, reference: str) -> date:
3132
)
3233
return None
3334

34-
[journey] = requests.get(f"{self.API_URL}/shipments/{external_shipment_id}/journeys").json()['items']
35+
[journey] = requests.get(f"{self.api_url}/shipments/{external_shipment_id}/journeys").json()['items']
3536
return date.fromisoformat(journey['eta'])
3637

3738

3839

3940
def sync(self, shipment: Shipment) -> None:
4041
external_shipment_id = self._get_shipment_id(shipment.reference)
4142
if external_shipment_id is None:
42-
requests.post(f'{self.API_URL}/shipments/', json={
43+
r = requests.post(f'{self.api_url}/shipments/', json={
4344
'client_reference': shipment.reference,
4445
'arrival_date': shipment.eta.isoformat()[:10] if shipment.eta else None,
4546
'products': [
4647
{'sku': ol.sku, 'quantity': ol.qty}
4748
for ol in shipment.lines
4849
]
4950
})
51+
assert r.ok, f'{r.status_code}: {r.text[:20]}'
5052

5153
else:
52-
requests.put(f'{self.API_URL}/shipments/{external_shipment_id}/', json={
54+
r = requests.put(f'{self.api_url}/shipments/{external_shipment_id}/', json={
55+
'id': external_shipment_id,
5356
'client_reference': shipment.reference,
5457
'arrival_date': shipment.eta.isoformat()[:10] if shipment.eta else None,
5558
'products': [
5659
{'sku': ol.sku, 'quantity': ol.qty}
5760
for ol in shipment.lines
5861
]
5962
})
63+
assert r.ok, f'{r.status_code}: {r.text[:20]}'
6064

6165

6266
def _get_shipment_id(self, our_reference) -> Optional[str]:
6367
try:
64-
their_shipments = requests.get(f"{self.API_URL}/shipments/").json()['items']
68+
their_shipments = requests.get(f"{self.api_url}/shipments/").json()['items']
6569
return next(
6670
(s['id'] for s in their_shipments if s['client_reference'] == our_reference),
6771
None

docker-compose.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
version: "3"
2+
3+
services:
4+
5+
fake_cargo_api:
6+
build:
7+
context: ./fake_cargo_api
8+
volumes:
9+
- ./fake_cargo_api/fake_cargo_api.py:/fake_cargo_api.py
10+
environment:
11+
- FLASK_APP=/fake_cargo_api.py
12+
- FLASK_ENV=development
13+
- FLASK_DEBUG=1
14+
- PYTHONUNBUFFERED=1
15+
entrypoint:
16+
- flask
17+
- run
18+
- --port=8543
19+
- --reload
20+
ports:
21+
- "8543:8543"

fake_cargo_api/Dockerfile

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
FROM python:3.8
2+
RUN python3.8 -m pip install flask

fake_cargo_api/fake_cargo_api.py

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import uuid
2+
from typing import Dict
3+
from flask import Flask, request
4+
5+
app = Flask('fake-cargo-api')
6+
7+
SHIPMENTS = {} # type: Dict[str, Dict]
8+
9+
@app.route('/shipments/', methods=["GET"])
10+
def list_shipments():
11+
print('returning', SHIPMENTS)
12+
return {'items': list(SHIPMENTS.values())}
13+
14+
15+
@app.route('/shipments/', methods=["POST"])
16+
def create_shipment():
17+
new_id = uuid.uuid4().hex
18+
refs = {s['client_reference'] for s in SHIPMENTS.values()}
19+
if request.json['client_reference'] in refs:
20+
return 'already exists', 400
21+
SHIPMENTS[new_id] = {'id': new_id, **request.json}
22+
print('saved', SHIPMENTS)
23+
return 'ok', 201
24+
25+
26+
@app.route('/shipments/<shipment_id>/', methods=["PUT"])
27+
def update_shipment(shipment_id):
28+
existing = SHIPMENTS[shipment_id]
29+
SHIPMENTS[shipment_id] = {**existing, **request.json}
30+
print('updated', SHIPMENTS)
31+
return 'ok', 200
32+
33+
34+
if __name__ == '__main__':
35+
app.run(host='0.0.0.0', port=8543, debug=True)

test_integration.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import subprocess
2+
import time
3+
import uuid
4+
import pytest
5+
import requests
6+
from cargo_api import RealCargoAPI
7+
from domain import Shipment, OrderLine
8+
9+
@pytest.fixture(autouse=True)
10+
def restart_fake_webapp():
11+
subprocess.run('docker-compose exec -T fake_cargo_api touch /fake_cargo_api.py'.split())
12+
time.sleep(0.5)
13+
14+
def random_reference():
15+
return uuid.uuid4().hex[:6]
16+
17+
18+
def test_can_create_new_shipment():
19+
api = RealCargoAPI('http://localhost:8543')
20+
line = OrderLine('sku1', 10)
21+
ref = random_reference()
22+
shipment = Shipment(reference=ref, lines=[line], eta=None, incoterm='foo')
23+
24+
api.sync(shipment)
25+
26+
shipments = requests.get(api.api_url + '/shipments/').json()['items']
27+
new_shipment = next(s for s in shipments if s['client_reference'] == ref)
28+
assert new_shipment['arrival_date'] is None
29+
assert new_shipment['products'] == [{'sku': 'sku1', 'quantity': 10}]
30+
31+
32+
def test_can_update_a_shipment():
33+
api = RealCargoAPI('http://localhost:8543')
34+
line = OrderLine('sku1', 10)
35+
ref = random_reference()
36+
shipment = Shipment(reference=ref, lines=[line], eta=None, incoterm='foo')
37+
38+
api.sync(shipment)
39+
40+
shipment.lines[0].qty = 20
41+
42+
api.sync(shipment)
43+
44+
shipments = requests.get(api.api_url + '/shipments/').json()['items']
45+
new_shipment = next(s for s in shipments if s['client_reference'] == ref)
46+
assert new_shipment['products'] == [{'sku': 'sku1', 'quantity': 20}]
47+

test_sync.py

Lines changed: 5 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,10 @@
11
from unittest import mock
22

33
from use_cases import create_shipment, API_URL
4+
from domain import Shipment, OrderLine
5+
from cargo_api import RealCargoAPI
46

5-
def test_create_shipment_does_post_to_external_api():
6-
with mock.patch('cargo_api.requests') as mock_requests:
7+
def test_create_shipment_syncs_to_api():
8+
with mock.patch('use_cases.cargo_api') as mock_cargo_api:
79
shipment = create_shipment({'sku1': 10}, incoterm='EXW')
8-
expected_data = {
9-
'client_reference': shipment.reference,
10-
'arrival_date': None,
11-
'products': [{'sku': 'sku1', 'quantity': 10}],
12-
}
13-
assert mock_requests.post.call_args == mock.call(
14-
API_URL + '/shipments/', json=expected_data
15-
)
16-
17-
18-
def test_does_PUT_if_shipment_already_exists():
19-
with mock.patch('use_cases.uuid') as mock_uuid, mock.patch('cargo_api.requests') as mock_requests:
20-
mock_uuid.uuid4.return_value.hex = 'our-id'
21-
mock_requests.get.return_value.json.return_value = {
22-
'items': [{'id': 'their-id', 'client_reference': 'our-id'}]
23-
}
24-
25-
shipment = create_shipment({'sku1': 10}, incoterm='EXW')
26-
assert mock_requests.post.called is False
27-
expected_data = {
28-
'client_reference': 'our-id',
29-
'arrival_date': None,
30-
'products': [{'sku': 'sku1', 'quantity': 10}],
31-
}
32-
assert mock_requests.put.call_args == mock.call(
33-
API_URL + '/shipments/their-id/', json=expected_data
34-
)
35-
36-
37-
def test_does_PUT_if_shipment_already_exists2():
38-
with mock.patch('use_cases.cargo_api._get_shipment_id') as mock_get_shipment_id, mock.patch('cargo_api.requests') as mock_requests:
39-
mock_get_shipment_id.return_value = 'their-id'
40-
mock_requests.get.return_value.json.return_value = {
41-
'items': [{'id': 'their-id', 'client_reference': 'our-id'}]
42-
}
43-
44-
shipment = create_shipment({'sku1': 10}, incoterm='EXW')
45-
assert mock_requests.post.called is False
46-
expected_data = {
47-
'client_reference': shipment.reference,
48-
'arrival_date': None,
49-
'products': [{'sku': 'sku1', 'quantity': 10}],
50-
}
51-
assert mock_requests.put.call_args == mock.call(
52-
API_URL + '/shipments/their-id/', json=expected_data
53-
)
54-
10+
assert mock_cargo_api.sync.call_args == mock.call(shipment)

0 commit comments

Comments
 (0)