Skip to content

Commit ed3d5ca

Browse files
committed
Divided unit test file into smaller test files
A directory has been created for invoices and processors. `unit_test.py` has been removed. This led to a small update in `workflows/unit-test.yaml`
1 parent 0fc7c16 commit ed3d5ca

14 files changed

+1080
-1047
lines changed

.github/workflows/unit-tests.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,4 @@ jobs:
2222
2323
- name: Run unit tests
2424
run: |
25-
python -m unittest process_report/tests/unit_tests.py
25+
python -m unittest

process_report/tests/unit/__init__.py

Whitespace-only changes.

process_report/tests/unit/invoices/__init__.py

Whitespace-only changes.
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
from unittest import TestCase, mock
2+
import pandas
3+
4+
from process_report.tests import util as test_utils
5+
6+
7+
class TestBaseInvoice(TestCase):
8+
def test_filter_exported_columns(self):
9+
test_invoice = pandas.DataFrame(columns=["C1", "C2", "C3", "C4", "C5"])
10+
answer_invoice = pandas.DataFrame(columns=["C1", "C3R", "C5R"])
11+
inv = test_utils.new_base_invoice(data=test_invoice)
12+
inv.export_data = test_invoice
13+
inv.export_columns_list = ["C1", "C3", "C5"]
14+
inv.exported_columns_map = {"C3": "C3R", "C5": "C5R"}
15+
inv._filter_columns()
16+
result_invoice = inv.export_data
17+
18+
self.assertTrue(result_invoice.equals(answer_invoice))
19+
20+
21+
class TestUploadToS3(TestCase):
22+
@mock.patch("process_report.util.get_invoice_bucket")
23+
@mock.patch("process_report.util.get_iso8601_time")
24+
def test_upload_to_s3(self, mock_get_time, mock_get_bucket):
25+
mock_bucket = mock.MagicMock()
26+
mock_get_bucket.return_value = mock_bucket
27+
mock_get_time.return_value = "0"
28+
29+
invoice_month = "2024-03"
30+
filenames = ["test-test", "test2.test", "test3"]
31+
sample_base_invoice = test_utils.new_base_invoice(invoice_month=invoice_month)
32+
33+
answers = [
34+
(
35+
f"test-test {invoice_month}.csv",
36+
f"Invoices/{invoice_month}/test-test {invoice_month}.csv",
37+
),
38+
(
39+
f"test-test {invoice_month}.csv",
40+
f"Invoices/{invoice_month}/Archive/test-test {invoice_month} 0.csv",
41+
),
42+
(
43+
f"test2.test {invoice_month}.csv",
44+
f"Invoices/{invoice_month}/test2.test {invoice_month}.csv",
45+
),
46+
(
47+
f"test2.test {invoice_month}.csv",
48+
f"Invoices/{invoice_month}/Archive/test2.test {invoice_month} 0.csv",
49+
),
50+
(
51+
f"test3 {invoice_month}.csv",
52+
f"Invoices/{invoice_month}/test3 {invoice_month}.csv",
53+
),
54+
(
55+
f"test3 {invoice_month}.csv",
56+
f"Invoices/{invoice_month}/Archive/test3 {invoice_month} 0.csv",
57+
),
58+
]
59+
60+
for filename in filenames:
61+
sample_base_invoice.name = filename
62+
sample_base_invoice.export_s3(mock_bucket)
63+
64+
for i, call_args in enumerate(mock_bucket.upload_file.call_args_list):
65+
self.assertTrue(answers[i] in call_args)
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
from unittest import TestCase, mock
2+
import tempfile
3+
import pandas
4+
import os
5+
6+
from process_report.tests import util as test_utils
7+
8+
9+
class TestExportPICSV(TestCase):
10+
def setUp(self):
11+
data = {
12+
"Invoice Month": ["2023-01", "2023-01", "2023-01", "2023-01", "2023-01"],
13+
"Manager (PI)": ["PI1", "PI1", "PI1", "PI2", "PI2"],
14+
"Institution": ["BU", "BU", "BU", "HU", "HU"],
15+
"Project - Allocation": [
16+
"ProjectA",
17+
"ProjectB",
18+
"ProjectC",
19+
"ProjectD",
20+
"ProjectE",
21+
],
22+
"Untouch Data Column": ["DataA", "DataB", "DataC", "DataD", "DataE"],
23+
"Is Billable": [True, True, True, True, True],
24+
"Missing PI": [False, False, False, False, False],
25+
}
26+
self.dataframe = pandas.DataFrame(data)
27+
self.invoice_month = data["Invoice Month"][0]
28+
29+
@mock.patch("process_report.invoices.invoice.Invoice._filter_columns")
30+
def test_export_pi(self, mock_filter_cols):
31+
mock_filter_cols.return_value = self.dataframe
32+
33+
output_dir = tempfile.TemporaryDirectory()
34+
pi_inv = test_utils.new_pi_specific_invoice(
35+
output_dir.name, invoice_month=self.invoice_month, data=self.dataframe
36+
)
37+
pi_inv.process()
38+
pi_inv.export()
39+
pi_csv_1 = f'{self.dataframe["Institution"][0]}_{self.dataframe["Manager (PI)"][0]} {self.dataframe["Invoice Month"][0]}.csv'
40+
pi_csv_2 = f'{self.dataframe["Institution"][3]}_{self.dataframe["Manager (PI)"][3]} {self.dataframe["Invoice Month"][3]}.csv'
41+
self.assertIn(pi_csv_1, os.listdir(output_dir.name))
42+
self.assertIn(pi_csv_2, os.listdir(output_dir.name))
43+
self.assertEqual(
44+
len(os.listdir(output_dir.name)),
45+
len(self.dataframe["Manager (PI)"].unique()),
46+
)
47+
48+
pi_df = pandas.read_csv(output_dir.name + "/" + pi_csv_1)
49+
self.assertEqual(len(pi_df["Manager (PI)"].unique()), 1)
50+
self.assertEqual(
51+
pi_df["Manager (PI)"].unique()[0], self.dataframe["Manager (PI)"][0]
52+
)
53+
54+
self.assertIn("ProjectA", pi_df["Project - Allocation"].tolist())
55+
self.assertIn("ProjectB", pi_df["Project - Allocation"].tolist())
56+
self.assertIn("ProjectC", pi_df["Project - Allocation"].tolist())
57+
58+
pi_df = pandas.read_csv(output_dir.name + "/" + pi_csv_2)
59+
self.assertEqual(len(pi_df["Manager (PI)"].unique()), 1)
60+
self.assertEqual(
61+
pi_df["Manager (PI)"].unique()[0], self.dataframe["Manager (PI)"][3]
62+
)
63+
64+
self.assertIn("ProjectD", pi_df["Project - Allocation"].tolist())
65+
self.assertIn("ProjectE", pi_df["Project - Allocation"].tolist())
66+
self.assertNotIn("ProjectA", pi_df["Project - Allocation"].tolist())
67+
self.assertNotIn("ProjectB", pi_df["Project - Allocation"].tolist())
68+
self.assertNotIn("ProjectC", pi_df["Project - Allocation"].tolist())

process_report/tests/unit/processors/__init__.py

Whitespace-only changes.
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
from unittest import TestCase
2+
3+
from process_report.tests import util as test_utils
4+
5+
6+
class TestAddInstituteProcessor(TestCase):
7+
def test_get_pi_institution(self):
8+
institute_map = {
9+
"harvard.edu": "Harvard University",
10+
"bu.edu": "Boston University",
11+
"bentley.edu": "Bentley",
12+
"mclean.harvard.edu": "McLean Hospital",
13+
"northeastern.edu": "Northeastern University",
14+
"childrens.harvard.edu": "Boston Children's Hospital",
15+
"meei.harvard.edu": "Massachusetts Eye & Ear",
16+
"dfci.harvard.edu": "Dana-Farber Cancer Institute",
17+
"bwh.harvard.edu": "Brigham and Women's Hospital",
18+
"bidmc.harvard.edu": "Beth Israel Deaconess Medical Center",
19+
}
20+
21+
answers = {
22+
"[email protected]": "Boston University",
23+
"[email protected]": "McLean Hospital",
24+
"[email protected]": "Harvard University",
25+
"e@edu": "",
26+
"[email protected]": "Northeastern University",
27+
"[email protected]": "Harvard University",
28+
"[email protected]": "Boston Children's Hospital",
29+
"[email protected]": "Massachusetts Eye & Ear",
30+
31+
"[email protected]": "Brigham and Women's Hospital",
32+
"[email protected]": "Beth Israel Deaconess Medical Center",
33+
}
34+
35+
add_institute_proc = test_utils.new_add_institution_processor()
36+
37+
for pi_email, answer in answers.items():
38+
self.assertEqual(
39+
add_institute_proc._get_institution_from_pi(institute_map, pi_email),
40+
answer,
41+
)
Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
from unittest import TestCase
2+
import pandas
3+
4+
from process_report.tests import util as test_utils
5+
6+
7+
class TestBUSubsidyProcessor(TestCase):
8+
def _assert_result_invoice(
9+
self,
10+
subsidy_amount,
11+
test_invoice,
12+
answer_invoice,
13+
invoice_month="0000-00",
14+
):
15+
new_bu_subsidy_proc = test_utils.new_bu_subsidy_processor(
16+
invoice_month=invoice_month,
17+
data=test_invoice,
18+
subsidy_amount=subsidy_amount,
19+
)
20+
new_bu_subsidy_proc.process()
21+
output_invoice = new_bu_subsidy_proc.data
22+
answer_invoice = answer_invoice.astype(output_invoice.dtypes)
23+
24+
self.assertTrue(output_invoice.equals(answer_invoice))
25+
26+
def _get_test_invoice(
27+
self,
28+
pi,
29+
pi_balances,
30+
balances=None,
31+
project_names=None,
32+
institution=None,
33+
is_billable=None,
34+
missing_pi=None,
35+
):
36+
if not balances:
37+
balances = pi_balances
38+
39+
if not project_names:
40+
project_names = ["Project" for _ in range(len(pi))]
41+
42+
if not institution:
43+
institution = ["Boston University" for _ in range(len(pi))]
44+
45+
if not is_billable:
46+
is_billable = [True for _ in range(len(pi))]
47+
48+
if not missing_pi:
49+
missing_pi = [False for _ in range(len(pi))]
50+
51+
return pandas.DataFrame(
52+
{
53+
"Manager (PI)": pi,
54+
"Project - Allocation": project_names,
55+
"PI Balance": pi_balances,
56+
"Balance": balances,
57+
"Institution": institution,
58+
"Is Billable": is_billable,
59+
"Missing PI": missing_pi,
60+
}
61+
)
62+
63+
def test_exclude_non_BU_pi(self):
64+
"""Are only BU PIs given the subsidy?"""
65+
66+
subsidy_amount = 100
67+
test_invoice = self._get_test_invoice(
68+
[str(i) for i in range(5)],
69+
pi_balances=[subsidy_amount for _ in range(5)],
70+
institution=[
71+
"Boston University",
72+
"Boston University",
73+
"boston university",
74+
"Harvard University",
75+
"BU",
76+
],
77+
)
78+
79+
answer_invoice = test_invoice.copy()
80+
answer_invoice["Project"] = answer_invoice["Project - Allocation"]
81+
answer_invoice["Subsidy"] = [subsidy_amount, subsidy_amount, 0, 0, 0]
82+
answer_invoice["PI Balance"] = [
83+
0,
84+
0,
85+
subsidy_amount,
86+
subsidy_amount,
87+
subsidy_amount,
88+
]
89+
90+
self._assert_result_invoice(subsidy_amount, test_invoice, answer_invoice)
91+
92+
def test_exclude_nonbillables(self):
93+
"""Are nonbillables excluded from the subsidy?"""
94+
subsidy_amount = 100
95+
test_invoice = self._get_test_invoice(
96+
[str(i) for i in range(6)],
97+
pi_balances=[subsidy_amount for _ in range(6)],
98+
is_billable=[True, True, False, False, True, True],
99+
missing_pi=[True, True, False, False, False, False],
100+
)
101+
102+
answer_invoice = test_invoice.copy()
103+
answer_invoice["Project"] = answer_invoice["Project - Allocation"]
104+
answer_invoice["Subsidy"] = [0, 0, 0, 0, subsidy_amount, subsidy_amount]
105+
answer_invoice["PI Balance"] = [
106+
subsidy_amount,
107+
subsidy_amount,
108+
subsidy_amount,
109+
subsidy_amount,
110+
0,
111+
0,
112+
]
113+
114+
self._assert_result_invoice(subsidy_amount, test_invoice, answer_invoice)
115+
116+
def test_one_pi_many_allocations(self):
117+
"""Is subsidy applied properly to BU PI with many allocations?"""
118+
119+
# Two projects, one allocation each
120+
subsidy_amount = 100
121+
test_invoice = self._get_test_invoice(
122+
["PI" for i in range(2)],
123+
pi_balances=[60, 60],
124+
project_names=["P1", "P2"],
125+
)
126+
127+
answer_invoice = test_invoice.copy()
128+
answer_invoice["Project"] = answer_invoice["Project - Allocation"]
129+
answer_invoice["Subsidy"] = [60, 40]
130+
answer_invoice["PI Balance"] = [0, 20]
131+
132+
self._assert_result_invoice(subsidy_amount, test_invoice, answer_invoice)
133+
134+
# Two projects, two allocations each
135+
test_invoice = self._get_test_invoice(
136+
["PI" for i in range(4)],
137+
pi_balances=[40, 40, 40, 40],
138+
project_names=["P1-A1", "P1-A1-test", "P2", "P2-"],
139+
)
140+
141+
answer_invoice = test_invoice.copy()
142+
answer_invoice["Project"] = ["P1", "P1-A1", "P2", "P2"]
143+
answer_invoice["Subsidy"] = [40, 40, 20, 0]
144+
answer_invoice["PI Balance"] = [0, 0, 20, 40]
145+
146+
self._assert_result_invoice(subsidy_amount, test_invoice, answer_invoice)
147+
148+
# Two allocations, one where PI balance != NERC balance
149+
test_invoice = self._get_test_invoice(
150+
["PI" for i in range(2)],
151+
pi_balances=[80, 80],
152+
project_names=["P1", "P2"],
153+
balances=[100, 80],
154+
)
155+
156+
answer_invoice = test_invoice.copy()
157+
answer_invoice["Project"] = answer_invoice["Project - Allocation"]
158+
answer_invoice["Subsidy"] = [80, 20]
159+
answer_invoice["PI Balance"] = [0, 60]
160+
161+
self._assert_result_invoice(subsidy_amount, test_invoice, answer_invoice)
162+
163+
def test_two_pi(self):
164+
"""Is subsidy applied to more than one PI?"""
165+
# Each PI has two allocations
166+
subsidy_amount = 100
167+
test_invoice = self._get_test_invoice(
168+
["PI1", "PI1", "PI2", "PI2"],
169+
pi_balances=[80, 80, 40, 40],
170+
)
171+
172+
answer_invoice = test_invoice.copy()
173+
answer_invoice["Project"] = answer_invoice["Project - Allocation"]
174+
answer_invoice["Subsidy"] = [80, 20, 40, 40]
175+
answer_invoice["PI Balance"] = [0, 60, 0, 0]
176+
177+
self._assert_result_invoice(subsidy_amount, test_invoice, answer_invoice)
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
from unittest import TestCase
2+
import pandas
3+
4+
from process_report.tests import util as test_utils
5+
6+
7+
class TestLenovoProcessor(TestCase):
8+
def test_process_lenovo(self):
9+
test_invoice = pandas.DataFrame(
10+
{
11+
"SU Hours (GBhr or SUhr)": [1, 10, 100, 4, 432, 10],
12+
}
13+
)
14+
answer_invoice = test_invoice.copy()
15+
answer_invoice["SU Charge"] = 1
16+
answer_invoice["Charge"] = (
17+
answer_invoice["SU Hours (GBhr or SUhr)"] * answer_invoice["SU Charge"]
18+
)
19+
20+
lenovo_proc = test_utils.new_lenovo_processor(data=test_invoice)
21+
lenovo_proc.process()
22+
self.assertTrue(lenovo_proc.data.equals(answer_invoice))

0 commit comments

Comments
 (0)