Skip to content

Commit a74be07

Browse files
committed
redid simpler unit tests
1 parent 7e36cb8 commit a74be07

1 file changed

Lines changed: 158 additions & 0 deletions

File tree

Lines changed: 158 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,158 @@
1+
"""Unit tests for DownloadList helper methods."""
2+
3+
import csv
4+
from pathlib import Path
5+
6+
import pytest
7+
8+
from synapseclient.core.exceptions import SynapseError
9+
from synapseclient.models.download_list import DownloadList
10+
11+
12+
class TestReadManifestRows:
13+
"""Tests for DownloadList._read_manifest_rows."""
14+
15+
def _write_csv(self, tmp_path: Path, header: list[str], rows: list[dict]) -> str:
16+
path = str(tmp_path / "manifest.csv")
17+
with open(path, "w", newline="") as f:
18+
writer = csv.DictWriter(f, fieldnames=header)
19+
writer.writeheader()
20+
writer.writerows(rows)
21+
return path
22+
23+
@pytest.mark.parametrize(
24+
"csv_content, expected_columns, expected_row_count, row_checks",
25+
[
26+
pytest.param(
27+
"ID,versionNumber,name\nsyn111,1,file_a.txt\nsyn222,3,file_b.txt\n",
28+
["ID", "versionNumber", "name"],
29+
2,
30+
{0: {"ID": "syn111", "versionNumber": "1"}, 1: {"ID": "syn222"}},
31+
id="standard_manifest",
32+
),
33+
pytest.param(
34+
"ID,versionNumber\n",
35+
["ID", "versionNumber"],
36+
0,
37+
{},
38+
id="headers_only_no_rows",
39+
),
40+
pytest.param(
41+
"",
42+
None,
43+
0,
44+
{},
45+
id="empty_file",
46+
),
47+
pytest.param(
48+
"ID\nsyn999\n",
49+
["ID"],
50+
1,
51+
{0: {"ID": "syn999"}},
52+
id="single_column",
53+
),
54+
pytest.param(
55+
"ID,versionNumber\nsyn123,\n",
56+
["ID", "versionNumber"],
57+
1,
58+
{0: {"ID": "syn123", "versionNumber": ""}},
59+
id="empty_string_values_preserved",
60+
),
61+
pytest.param(
62+
'ID,name\nsyn123,"file, with comma.txt"\n',
63+
["ID", "name"],
64+
1,
65+
{0: {"name": "file, with comma.txt"}},
66+
id="quoted_field_with_comma",
67+
),
68+
],
69+
)
70+
def test_read_manifest_rows(
71+
self,
72+
tmp_path: Path,
73+
csv_content: str,
74+
expected_columns: list[str],
75+
expected_row_count: int,
76+
row_checks: dict[int, dict[str, str]],
77+
) -> None:
78+
"""_read_manifest_rows returns correct columns and rows for various CSV shapes."""
79+
# GIVEN a CSV file with the specified content
80+
path = str(tmp_path / "manifest.csv")
81+
with open(path, "w", newline="") as f:
82+
f.write(csv_content)
83+
84+
# WHEN I read the manifest
85+
columns, rows = DownloadList._read_manifest_rows(path)
86+
87+
# THEN columns and row count match expectations
88+
assert columns == expected_columns
89+
assert len(rows) == expected_row_count
90+
91+
# AND specific cell values match
92+
for row_idx, expected_values in row_checks.items():
93+
for key, value in expected_values.items():
94+
assert rows[row_idx][key] == value
95+
96+
def test_many_rows(self, tmp_path: Path) -> None:
97+
"""Reading a manifest with many rows returns all of them."""
98+
# GIVEN a CSV with 500 rows
99+
header = ["ID", "versionNumber"]
100+
data = [{"ID": f"syn{i}", "versionNumber": str(i)} for i in range(500)]
101+
path = self._write_csv(tmp_path, header, data)
102+
103+
# WHEN I read the manifest
104+
columns, rows = DownloadList._read_manifest_rows(path)
105+
106+
# THEN all 500 rows are returned
107+
assert columns == header
108+
assert len(rows) == 500
109+
assert rows[0]["ID"] == "syn0"
110+
assert rows[499]["ID"] == "syn499"
111+
112+
113+
class TestValidateAndExtendColumns:
114+
"""Tests for DownloadList._validate_and_extend_columns."""
115+
116+
@pytest.mark.parametrize(
117+
"columns, expected",
118+
[
119+
pytest.param(
120+
["ID", "versionNumber"],
121+
["ID", "versionNumber", "path", "error"],
122+
id="standard_columns",
123+
),
124+
pytest.param(
125+
["ID"],
126+
["ID", "path", "error"],
127+
id="single_column",
128+
),
129+
pytest.param(
130+
["ID", "versionNumber", "name", "createdBy"],
131+
["ID", "versionNumber", "name", "createdBy", "path", "error"],
132+
id="many_columns",
133+
),
134+
],
135+
)
136+
def test_appends_path_and_error(
137+
self, columns: list[str], expected: list[str]
138+
) -> None:
139+
"""Valid columns are returned with path and error appended."""
140+
assert DownloadList._validate_and_extend_columns(columns) == expected
141+
142+
def test_none_columns_raises(self) -> None:
143+
"""None columns (empty manifest) raises SynapseError."""
144+
with pytest.raises(SynapseError, match="no headers"):
145+
DownloadList._validate_and_extend_columns(None)
146+
147+
@pytest.mark.parametrize(
148+
"columns",
149+
[
150+
pytest.param(["ID", "path"], id="contains_path"),
151+
pytest.param(["ID", "error"], id="contains_error"),
152+
pytest.param(["path", "error"], id="contains_both"),
153+
],
154+
)
155+
def test_reserved_column_names_raise(self, columns: list[str]) -> None:
156+
"""Columns containing reserved names 'path' or 'error' raise SynapseError."""
157+
with pytest.raises(SynapseError, match="reserved column names"):
158+
DownloadList._validate_and_extend_columns(columns)

0 commit comments

Comments
 (0)