Skip to content

Commit 8374872

Browse files
authored
Fix test_targetsmart_smartmatch on windows + code coverage improvement (#1271)
* improve coverage of test_targetsmart_smartmatch by testing behavior of args * fix file access issues for targetsmart_smartmatch on windows * enable testing on windows
1 parent 3df7c2e commit 8374872

File tree

2 files changed

+139
-71
lines changed

2 files changed

+139
-71
lines changed

parsons/targetsmart/targetsmart_smartmatch.py

Lines changed: 51 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import tempfile
1212
import time
1313
import uuid
14+
from pathlib import Path
1415

1516
import petl
1617
import requests
@@ -100,7 +101,6 @@ def __init__(self):
100101
self.connection = None
101102

102103
def _smartmatch_poll(self, poll_url, submit_filename):
103-
download_url = None
104104
while True:
105105
poll_response = requests.get(
106106
poll_url,
@@ -242,18 +242,21 @@ def smartmatch(
242242
)
243243

244244
# Write Petl table to CSV and upload for SmartMatch to process
245-
with tempfile.NamedTemporaryFile(
245+
tmp = tempfile.NamedTemporaryFile(
246246
mode="w+",
247247
encoding="utf8",
248248
newline="\n",
249249
prefix="smartmatch_input",
250250
suffix=".csv",
251251
dir=tmp_location,
252-
delete=not keep_smartmatch_input_file,
253-
) as tmp:
254-
dataprep_table.tocsv(tmp.name, encoding="utf8")
255-
tmp.flush()
256-
_smartmatch_upload(response_1_info["url"], tmp.name)
252+
delete=False,
253+
)
254+
dataprep_table.tocsv(tmp.name, encoding="utf8")
255+
_smartmatch_upload(response_1_info["url"], tmp.name)
256+
257+
tmp.close()
258+
if not keep_smartmatch_input_file:
259+
Path(tmp.name).unlink()
257260

258261
logger.info(
259262
"The SmartMatch workflow execution has been submitted using file"
@@ -266,46 +269,47 @@ def smartmatch(
266269

267270
# Download SmartMatch .csv.gz results, decompress, and Petl table wrap.
268271
# The final tmp file cannot be deleted due to Petl tables being lazy.
269-
with tempfile.NamedTemporaryFile(
272+
tmp_gz = tempfile.NamedTemporaryFile(
270273
prefix="smartmatch_output",
271274
suffix=".csv.gz",
272275
dir=tmp_location,
273-
delete=not keep_smartmatch_output_gz_file,
274-
) as tmp_gz:
275-
with tempfile.NamedTemporaryFile(
276-
prefix="smartmatch_output",
277-
suffix=".csv",
278-
dir=tmp_location,
279-
delete=False,
280-
) as tmp_csv:
281-
logger.info(
282-
f"Downloading the '{submit_filename}' SmartMatch results to {tmp_gz.name}."
283-
)
284-
_smartmatch_download(download_url, tmp_gz)
285-
tmp_gz.flush()
286-
287-
logger.info("Decompressing results")
288-
with gzip.open(tmp_gz.name, "rb") as gz_reader:
289-
shutil.copyfileobj(gz_reader, tmp_csv)
290-
tmp_csv.flush()
291-
292-
raw_outtable = petl.fromcsv(tmp_csv.name, encoding="utf8").convert(
293-
INTERNAL_JOIN_ID, int
294-
)
295-
logger.info(
296-
"SmartMatch remote execution successful. Joining results to input table."
297-
)
298-
outtable = (
299-
petl.leftjoin(
300-
input_table,
301-
raw_outtable,
302-
key=INTERNAL_JOIN_ID,
303-
tempdir=tmp_location,
304-
)
305-
.sort(key=INTERNAL_JOIN_ID)
306-
.cutout(INTERNAL_JOIN_ID)
307-
)
308-
if INTERNAL_JOIN_ID_CONFLICT in input_table.fieldnames():
309-
input_table = input_table.rename(INTERNAL_JOIN_ID_CONFLICT, INTERNAL_JOIN_ID)
310-
311-
return Table(outtable)
276+
delete=False,
277+
)
278+
279+
tmp_csv = tempfile.NamedTemporaryFile(
280+
prefix="smartmatch_output",
281+
suffix=".csv",
282+
dir=tmp_location,
283+
delete=False,
284+
)
285+
286+
logger.info(f"Downloading the '{submit_filename}' SmartMatch results to {tmp_gz.name}.")
287+
_smartmatch_download(download_url, tmp_gz)
288+
tmp_gz.flush()
289+
290+
logger.info("Decompressing results")
291+
with gzip.open(tmp_gz.name, "rb") as gz_reader:
292+
shutil.copyfileobj(gz_reader, tmp_csv)
293+
tmp_csv.flush()
294+
295+
tmp_gz.close()
296+
if not keep_smartmatch_output_gz_file:
297+
Path(tmp_gz.name).unlink()
298+
tmp_csv.close()
299+
300+
raw_outtable = petl.fromcsv(tmp_csv.name, encoding="utf8").convert(INTERNAL_JOIN_ID, int)
301+
logger.info("SmartMatch remote execution successful. Joining results to input table.")
302+
outtable = (
303+
petl.leftjoin(
304+
input_table,
305+
raw_outtable,
306+
key=INTERNAL_JOIN_ID,
307+
tempdir=tmp_location,
308+
)
309+
.sort(key=INTERNAL_JOIN_ID)
310+
.cutout(INTERNAL_JOIN_ID)
311+
)
312+
if INTERNAL_JOIN_ID_CONFLICT in input_table.fieldnames():
313+
input_table.rename(INTERNAL_JOIN_ID_CONFLICT, INTERNAL_JOIN_ID)
314+
315+
return Table(outtable)
Lines changed: 88 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,19 @@
11
import csv
22
import gzip
33
import io
4-
import sys
4+
import tempfile
5+
from pathlib import Path
6+
from unittest.mock import MagicMock
57

68
import petl
79
import pytest
10+
from petl.util.base import TableWrapper
811

912
from parsons.targetsmart.targetsmart_api import TargetSmartAPI
1013

1114

1215
@pytest.fixture
13-
def intable():
16+
def intable() -> TableWrapper:
1417
return petl.wrap(
1518
[
1619
[
@@ -29,7 +32,7 @@ def intable():
2932

3033

3134
@pytest.fixture
32-
def raw_outtable(intable):
35+
def raw_outtable(intable: TableWrapper) -> tuple:
3336
return (
3437
intable.addrownumbers(field="ts__input_row")
3538
.addrownumbers(field="ts__row")
@@ -42,53 +45,114 @@ def raw_outtable(intable):
4245

4346

4447
@pytest.fixture
45-
def prep_intable(intable):
48+
def prep_intable(intable: TableWrapper) -> TableWrapper:
4649
return intable.addrownumbers(field="matchback_id")
4750

4851

4952
@pytest.fixture
50-
def raw_outcsv(raw_outtable):
53+
def raw_outcsv(raw_outtable: tuple) -> str:
5154
buf = io.StringIO()
5255
writer = csv.writer(buf)
5356
writer.writerows(list(raw_outtable))
5457
return buf.getvalue()
5558

5659

5760
@pytest.fixture
58-
def raw_outgz(raw_outcsv):
61+
def raw_outgz(raw_outcsv: str) -> bytes:
5962
buf = io.BytesIO()
6063
with gzip.GzipFile(fileobj=buf, mode="w") as gz:
6164
gz.write(raw_outcsv.encode("utf8"))
6265
return buf.getvalue()
6366

6467

6568
@pytest.fixture
66-
def final_outtable(prep_intable, raw_outtable):
69+
def final_outtable(prep_intable: TableWrapper, raw_outtable: tuple) -> TableWrapper:
6770
return petl.leftjoin(prep_intable, raw_outtable, key="matchback_id").cutout("matchback_id")
6871

6972

70-
@pytest.fixture
71-
def submit_filename():
72-
return "parsons_test.csv"
73-
74-
75-
@pytest.mark.skipif(sys.platform == "win32", reason="need to fix this test on windows")
76-
def test_smartmatch(
77-
intable,
78-
submit_filename,
79-
raw_outgz,
80-
raw_outcsv,
81-
raw_outtable,
82-
final_outtable,
83-
requests_mock,
84-
):
85-
ts = TargetSmartAPI("mockkey")
73+
def smartmatch_requests_mock(requests_mock: MagicMock, raw_outgz: bytes):
8674
resp1 = {"url": "https://mock_smartmatch_upload_endpoint", "error": None}
87-
poll_resp = {"url": "https://mock_smartmatch_download_endpoint", "error": None}
8875
requests_mock.get("https://api.targetsmart.com/service/smartmatch", json=resp1)
8976
requests_mock.put(resp1["url"])
77+
poll_resp = {"url": "https://mock_smartmatch_download_endpoint", "error": None}
9078
requests_mock.get("https://api.targetsmart.com/service/smartmatch/poll", json=poll_resp)
9179
requests_mock.get(poll_resp["url"], content=raw_outgz)
80+
return requests_mock
81+
82+
83+
def test_smartmatch_returned_petl(
84+
intable: TableWrapper,
85+
raw_outgz: bytes,
86+
final_outtable: TableWrapper,
87+
requests_mock: MagicMock,
88+
):
89+
ts = TargetSmartAPI("mockkey")
90+
smartmatch_requests_mock(requests_mock, raw_outgz)
9291

9392
results = ts.smartmatch(intable).to_petl()
9493
assert list(final_outtable) == list(results)
94+
95+
96+
def test_smartmatch_output_csv_exists(
97+
intable: TableWrapper,
98+
raw_outgz: bytes,
99+
requests_mock: MagicMock,
100+
):
101+
ts = TargetSmartAPI("mockkey")
102+
smartmatch_requests_mock(requests_mock, raw_outgz)
103+
104+
temp_dir = tempfile.mkdtemp()
105+
ts.smartmatch(intable, tmp_location=temp_dir)
106+
assert sorted(Path(temp_dir).glob("smartmatch_output*.csv")) != []
107+
108+
109+
def test_smartmatch_keep_smartmatch_input_csv(
110+
intable: TableWrapper,
111+
raw_outgz: bytes,
112+
requests_mock: MagicMock,
113+
):
114+
ts = TargetSmartAPI("mockkey")
115+
smartmatch_requests_mock(requests_mock, raw_outgz)
116+
117+
temp_dir = tempfile.mkdtemp()
118+
ts.smartmatch(intable, tmp_location=temp_dir, keep_smartmatch_input_file=True)
119+
assert sorted(Path(temp_dir).glob("smartmatch_input*.csv")) != []
120+
121+
122+
def test_smartmatch_keep_smartmatch_input_csv_false(
123+
intable: TableWrapper,
124+
raw_outgz: bytes,
125+
requests_mock: MagicMock,
126+
):
127+
ts = TargetSmartAPI("mockkey")
128+
smartmatch_requests_mock(requests_mock, raw_outgz)
129+
130+
temp_dir = tempfile.mkdtemp()
131+
ts.smartmatch(intable, tmp_location=temp_dir, keep_smartmatch_input_file=False)
132+
assert sorted(Path(temp_dir).glob("smartmatch_input*.csv")) == []
133+
134+
135+
def test_smartmatch_keep_smartmatch_output_gz(
136+
intable: TableWrapper,
137+
raw_outgz: bytes,
138+
requests_mock: MagicMock,
139+
):
140+
ts = TargetSmartAPI("mockkey")
141+
smartmatch_requests_mock(requests_mock, raw_outgz)
142+
143+
temp_dir = tempfile.mkdtemp()
144+
ts.smartmatch(intable, tmp_location=temp_dir, keep_smartmatch_output_gz_file=True)
145+
assert sorted(Path(temp_dir).glob("smartmatch_output*.csv.gz")) != []
146+
147+
148+
def test_smartmatch_keep_smartmatch_output_gz_false(
149+
intable: TableWrapper,
150+
raw_outgz: bytes,
151+
requests_mock: MagicMock,
152+
):
153+
ts = TargetSmartAPI("mockkey")
154+
smartmatch_requests_mock(requests_mock, raw_outgz)
155+
156+
temp_dir = tempfile.mkdtemp()
157+
ts.smartmatch(intable, tmp_location=temp_dir, keep_smartmatch_output_gz_file=False)
158+
assert sorted(Path(temp_dir).glob("smartmatch_output*.csv.gz")) == []

0 commit comments

Comments
 (0)