Skip to content

Commit f560983

Browse files
jophalsGirik1105jdamerow
authored
Story/gh-25 Testing for critical components (#67)
* [gh-25] Added tests for redcap components * [gh-25] Made new dir tests in track, used testcase for testing core redcap functions, added dummy url to avoid sending actual post reqs to server * [gh-25] Added tests for order func incomplete record and order success * [gh-25] added test for order failure (gbf) * [gh-25] Complete order tests with logs * [gh-25] Added logs, comments for redcap, fixed file scrture * [story/gh-25] refactored gbf to break down large methods into helpers; started building unit tests for gbf * [story/gh-25] implemented gbf unit tests, refactored some GBF methods to improve testability * [story/GH-25] added logging to test_gbf, cleaned up import statements * [gh-25] Fixed tests - error arises in 2 tests in test_gbf * [gh-25] Fixed redcap tests * [gh-25] Fixed extra spcaes, added removed comment back * Trying to get tests to run * [gh-25] make db host configurable, to be able to run tests in an action * Let's try this again --------- Co-authored-by: Girik1105 <[email protected]> Co-authored-by: Julia Damerow <[email protected]> Co-authored-by: Julia Damerow <[email protected]>
1 parent 2d6280b commit f560983

File tree

9 files changed

+652
-5
lines changed

9 files changed

+652
-5
lines changed

.github/workflows/django_test.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,26 @@ jobs:
1010
build:
1111

1212
runs-on: ubuntu-latest
13+
env:
14+
POSTGRES_USER: dbuser
15+
POSTGRES_PASSWORD: dbpassword
16+
POSTGRES_NAME: dbname
17+
POSTGRES_PORT: 5432
18+
POSTGRES_HOST: localhost
19+
services:
20+
db:
21+
image: postgres:16
22+
env:
23+
POSTGRES_USER: ${{ env.POSTGRES_USER }}
24+
POSTGRES_PASSWORD: ${{ env.POSTGRES_PASSWORD }}
25+
POSTGRES_DB: ${{ env.POSTGRES_NAME }}
26+
ports:
27+
- 5432:5432
28+
options: >-
29+
--health-cmd pg_isready
30+
--health-interval 10s
31+
--health-timeout 5s
32+
--health-retries 5
1333
strategy:
1434
max-parallel: 4
1535
matrix:

edrop/settings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@
9494
'NAME': os.environ.get('POSTGRES_NAME'),
9595
'USER': os.environ.get('POSTGRES_USER'),
9696
'PASSWORD': os.environ.get('POSTGRES_PASSWORD'),
97-
'HOST': 'db',
97+
'HOST': os.environ.get('POSTGRES_HOST', 'db'),
9898
'PORT': os.environ.get('POSTGRES_PORT'),
9999
}
100100
}

track/gbf.py

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ def create_order(order, adress_data):
3434
logger.info(message)
3535

3636
# make order with GBF
37-
return _place_order_with_GBF(order_json, order_number)
37+
order_response = _place_order_with_GBF(order_json, order_number)
38+
39+
return _check_order_response(order_response, order_number)
3840

3941
def _generate_order_number(order):
4042
"""
@@ -100,6 +102,9 @@ def _place_order_with_GBF(order_json, order_number):
100102
log_manager.append_to_gbf_log(LogManager.LEVEL_INFO, response, order_number)
101103
logger.info(response)
102104

105+
return response
106+
107+
def _check_order_response(response, order_number):
103108
response_body = response.json()
104109
log_manager.append_to_gbf_log(LogManager.LEVEL_DEBUG, response_body, order_number)
105110
logger.debug(response_body)
@@ -142,7 +147,7 @@ def get_order_confirmations(order_numbers):
142147
143148
GBF sends json like this:
144149
{
145-
"success": true,
150+
"success": True,
146151
"dataArray": [
147152
{
148153
"format": "json",
@@ -184,7 +189,7 @@ def get_order_confirmations(order_numbers):
184189
message = err
185190
log_manager.append_to_gbf_log(LogManager.LEVEL_ERROR, message)
186191
logger.error(message)
187-
return None
192+
return None
188193

189194
response_body = response.json()
190195
# if for some reason GBF does not return a success response
@@ -218,6 +223,10 @@ def get_order_confirmations(order_numbers):
218223
return None
219224

220225
confirmations = json.loads(data_object["data"])
226+
227+
return _extract_tracking_info(confirmations)
228+
229+
def _extract_tracking_info(confirmations):
221230
tracking_info = {}
222231
if "ShippingConfirmations" in confirmations:
223232
for shipping_confirmation in confirmations['ShippingConfirmations']:

track/redcap.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ def set_order_number(record_id, order_number):
124124
if r.status_code != HTTPStatus.OK:
125125
logger.error(f'HTTP Status: {r.status_code}')
126126
logger.error(r.json())
127+
raise REDCapError(f"REDCap returned {r.status_code}.")
127128
else:
128129
logger.debug("Succesfully send order number to REDCap.")
129130

@@ -156,7 +157,6 @@ def set_tracking_info(order_objects):
156157
return
157158

158159
for order in order_objects:
159-
160160
# in case an order has not been shipped yet, we don't update REDcap
161161
if not order.ship_date:
162162
continue
@@ -196,6 +196,7 @@ def set_tracking_info(order_objects):
196196
message = r.json()
197197
log_manager.append_to_redcap_log(LogManager.LEVEL_ERROR, message)
198198
logger.error(message)
199+
raise REDCapError(f"REDCap returned {r.status_code}.")
199200
else:
200201
message = f"Succesfully sent tracking information to REDCap for the following records: {[order.record_id for order in order_objects]}."
201202
log_manager.append_to_redcap_log(LogManager.LEVEL_INFO, message)

track/tests/__init__.py

Whitespace-only changes.

track/tests/test_gbf.py

Lines changed: 195 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,195 @@
1+
import logging
2+
from unittest.mock import patch, MagicMock
3+
from django.test import TestCase, override_settings
4+
import json
5+
6+
import track.gbf as gbf
7+
from track.models import *
8+
9+
logger = logging.getLogger(__name__)
10+
11+
order_json = {
12+
"test": "true",
13+
"orders": [
14+
{
15+
"orderNumber": "EDROP-00014",
16+
"shippingInfo": {
17+
"address": {
18+
"company": "John Doe",
19+
"addressLine1": "1234 Main Street",
20+
"addressLine2": "PO Box 5",
21+
"city": "Phoenix",
22+
"state": "AZ",
23+
"zipCode": "00000",
24+
"country": "United States",
25+
"phone": "1111111111",
26+
"residential": True
27+
},
28+
"shipMethod": "FedEx Ground",
29+
},
30+
"lineItems": [
31+
{
32+
"itemNumber": "111",
33+
"itemQuantity": 1.0,
34+
}
35+
]
36+
}
37+
]
38+
}
39+
40+
address_data = {
41+
"first_name": "John",
42+
"last_name": "Doe",
43+
"street1": "1234 Main Street",
44+
"street2": "PO Box 5",
45+
"city": "Phoenix",
46+
"state": "AZ",
47+
"zip": "00000",
48+
"phone": "1111111111",
49+
}
50+
51+
class OrderResponse:
52+
def __init__(self, status_code, json_data=None):
53+
self.status_code = status_code
54+
self.json_data = json_data
55+
56+
def json(self):
57+
return self.json_data
58+
59+
confirmation_response_json = {
60+
"success": True,
61+
"dataArray": [
62+
{
63+
"format": "json",
64+
"data": "{\r\n \"ShippingConfirmations\": [\r\n {\r\n \"OrderNumber\": \"EDROP-00014\",\r\n \"Shipper\": \"\",\r\n \"ShipVia\": \"FedEx Ground\",\r\n \"ShipDate\": \"2025-01-23\",\r\n \"ClientID\": \"\",\r\n \"Tracking\": [\r\n \"270000004830\"\r\n ],\r\n \"Items\": [\r\n {\r\n \"ItemNumber\": \"K-BAN-001\",\r\n \"SerialNumber\": \"EV-05FCSG\",\r\n \"ShippedQty\": 1,\r\n \"ReturnTracking\": [\r\n \"XXXXXXXXXXXX\"\r\n ],\r\n \"TubeSerial\": [\r\n \"SIHIRJT5786\"\r\n ]\r\n }\r\n ]\r\n }\r\n ]\r\n}"
65+
}
66+
]
67+
}
68+
69+
tracking_info = {
70+
'EDROP-00014': {
71+
'date_kit_shipped': '2025-01-23',
72+
'kit_tracking_n': ['270000004830'],
73+
'return_tracking_n': ['XXXXXXXXXXXX'],
74+
'tube_serial_n': ['SIHIRJT5786']
75+
}
76+
}
77+
78+
@override_settings(GBF_URL="http://host.docker.internal:3000/")
79+
class TestGBF(TestCase):
80+
def setUp(self):
81+
self.mock_order_number = "EDROP-00014"
82+
self.mock_order_json = order_json
83+
self.mock_order_response = OrderResponse(200, {'success': True, 'message': 'EXM-0000XX_RDQYD_20250115_154237.xml'})
84+
self.order_object = Order.objects.create(pk=14, project_id=1, order_number=None)
85+
self.address_data = address_data
86+
self.order_response_json = {'success': True, 'message': 'EXM-0000XX_RDQYD_20250115_154237.xml'}
87+
self.order_numbers = ["EDROP-00014", "EDROP-00015"]
88+
self.confirmation_response_json = confirmation_response_json
89+
self.tracking_info = tracking_info
90+
91+
@patch("track.gbf._place_order_with_GBF")
92+
@patch("track.gbf._generate_order_json")
93+
@patch("track.gbf._generate_order_number")
94+
def test_create_order(self, mock_generate_order_number, mock_generate_order_json, mock_place_order_with_GBF):
95+
mock_generate_order_number.return_value = self.mock_order_number
96+
mock_generate_order_json.return_value = self.mock_order_json
97+
mock_place_order_with_GBF.side_effect = lambda json, order_number: self.mock_order_response
98+
99+
result = gbf.create_order(self.order_object, self.address_data)
100+
101+
updated_order_object = Order.objects.filter(pk=self.order_object.id).first()
102+
103+
self.assertEqual(updated_order_object.order_number, "EDROP-00014")
104+
self.assertEqual(result, True)
105+
106+
logger.debug(f'Order {updated_order_object.order_number} was successfully created.')
107+
108+
def test_generate_order_number(self):
109+
result = gbf._generate_order_number(self.order_object)
110+
self.assertEqual(result, "EDROP-00014")
111+
112+
def test_generate_order_json(self):
113+
self.order_object.order_number = "EDROP-00014"
114+
115+
result = gbf._generate_order_json(self.order_object, self.address_data)
116+
result_data = json.loads(result)
117+
118+
self.assertIn("test", result_data)
119+
self.assertIn("orders", result_data)
120+
self.assertIn("orderNumber", result_data['orders'][0])
121+
self.assertIn("shippingInfo", result_data['orders'][0])
122+
self.assertIn("lineItems", result_data['orders'][0])
123+
self.assertIn("address", result_data['orders'][0]['shippingInfo'])
124+
self.assertIn("shipMethod", result_data['orders'][0]['shippingInfo'])
125+
self.assertEqual(result_data['orders'][0]['orderNumber'], "EDROP-00014")
126+
self.assertEqual(result_data['orders'][0]['shippingInfo']['address']['company'], "John Doe")
127+
self.assertEqual(result_data['orders'][0]['shippingInfo']['address']['zipCode'], "00000")
128+
129+
@patch("track.gbf.requests.post")
130+
def test_place_order_with_GBF_success(self, mock_request):
131+
mock_response = MagicMock()
132+
mock_response.status_code = 200
133+
mock_response.json.return_value = self.order_response_json
134+
mock_request.return_value = mock_response
135+
136+
result = gbf._place_order_with_GBF(self.mock_order_json, self.mock_order_number)
137+
result_body = result.json()
138+
139+
mock_request.assert_called_once()
140+
self.assertEqual(result.status_code, 200)
141+
self.assertIn("success", result_body)
142+
self.assertIn("message", result_body)
143+
self.assertEqual(result_body["success"], True)
144+
145+
logger.debug(f'Order {self.mock_order_json['orders'][0]['orderNumber']} was successfully placed.')
146+
147+
@patch("track.gbf.requests.post")
148+
def test_place_order_with_GBF_failure(self, mock_request):
149+
mock_response = MagicMock()
150+
mock_response.status_code = 400
151+
mock_response.json.return_value = {"success": False, "error": "Bad Request"}
152+
mock_request.return_value = mock_response
153+
154+
result = gbf._place_order_with_GBF(self.mock_order_json, self.mock_order_number)
155+
156+
mock_request.assert_called_once()
157+
self.assertEqual(result.status_code, 400)
158+
159+
logger.error('The order was unable to be placed due to a bad request.')
160+
161+
@patch("track.gbf._extract_tracking_info")
162+
@patch("track.gbf.requests.post")
163+
def test_get_order_confirmations_success(self, mock_request, mock_extract_tracking_info):
164+
mock_response = MagicMock()
165+
mock_response.status_code = 200
166+
mock_response.raise_for_status.return_value = None
167+
mock_response.json.return_value = self.confirmation_response_json
168+
mock_request.return_value = mock_response
169+
mock_extract_tracking_info.return_value = self.tracking_info
170+
171+
result = gbf.get_order_confirmations(self.order_numbers)
172+
173+
mock_request.assert_called_once()
174+
mock_extract_tracking_info.assert_called_once()
175+
self.assertIn("EDROP-00014", result)
176+
self.assertIn("2025-01-23", result['EDROP-00014']['date_kit_shipped'])
177+
self.assertIn("270000004830", result['EDROP-00014']['kit_tracking_n'])
178+
self.assertIn('XXXXXXXXXXXX', result['EDROP-00014']['return_tracking_n'])
179+
self.assertIn('SIHIRJT5786', result['EDROP-00014']['tube_serial_n'])
180+
181+
logger.debug(f'The following order numbers were successfully checked for order confirmation: {self.order_numbers}')
182+
183+
@patch("track.gbf.requests.post")
184+
def test_get_order_confirmations_failure(self, mock_request):
185+
mock_response = MagicMock()
186+
mock_response.status_code = 400
187+
mock_response.json.return_value = {"success": False, "error": "Bad Request"}
188+
mock_request.return_value = mock_response
189+
190+
result = gbf.get_order_confirmations(self.order_numbers)
191+
192+
mock_request.assert_called_once()
193+
self.assertEqual(result, None)
194+
195+
logger.error('The order confirmation failed due to a bad request.')

0 commit comments

Comments
 (0)