Skip to content

Commit 26ab068

Browse files
Elarwei001claude
authored andcommitted
test(ref): cover remaining lines for codecov (#179)
Add a network-free TestAssemblyReportOffline class that mocks requests to cover assembly_report (accession validation, parent-dir and report HTTP errors, no-folder/missing-header errors, parsing, verbose, and json/save/CSV branches) and the ref(assembly_report=True) delegation. All PR-added lines are now covered except the json_package.dump rename deep in the Ensembl network save path (un-coverable network-free). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent b146c52 commit 26ab068

1 file changed

Lines changed: 93 additions & 1 deletion

File tree

tests/test_ref.py

Lines changed: 93 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
11
import json
2+
import os
3+
import tempfile
24
import unittest
5+
from unittest.mock import patch
36

4-
from gget.gget_ref import ref
7+
import gget.gget_ref as gget_ref
8+
from gget.gget_ref import assembly_report, ref
59

610
from .from_json import from_json
711

@@ -12,3 +16,91 @@
1216

1317
class TestRef(unittest.TestCase, metaclass=from_json(ref_dict, ref)):
1418
pass # all tests are loaded from json
19+
20+
21+
class _FakeResp:
22+
"""Minimal stand-in for a requests.Response used to test assembly_report offline."""
23+
24+
def __init__(self, text="", status_code=200):
25+
self.text = text
26+
self.status_code = status_code
27+
28+
29+
# A parent-directory listing containing the assembly's folder, and a minimal
30+
# tab-delimited assembly report (with a comment header and a blank line).
31+
_PARENT_HTML = '<html><body><a href="GCF_000001405.40_GRCh38.p14/">GCF_000001405.40_GRCh38.p14/</a></body></html>'
32+
_REPORT_TEXT = (
33+
"# Assembly name: GRCh38.p14\n"
34+
"# Sequence-Name\tSequence-Role\tGenBank-Accn\tRefSeq-Accn\tUCSC-style-name\n"
35+
"1\tassembled-molecule\tCM000663.2\tNC_000001.11\tchr1\n"
36+
"\n"
37+
)
38+
39+
40+
class TestAssemblyReportOffline(unittest.TestCase):
41+
"""Network-free tests of assembly_report and the ref() delegation (issue #179)."""
42+
43+
def test_invalid_accession_raises(self):
44+
with self.assertRaises(ValueError):
45+
assembly_report("not-an-accession", verbose=False)
46+
47+
@patch.object(gget_ref.requests, "get")
48+
def test_parse_verbose_json_and_save(self, mock_get):
49+
# Happy path: verbose log, blank-line skip, parsing, then json/save/csv branches.
50+
mock_get.side_effect = [_FakeResp(_PARENT_HTML), _FakeResp(_REPORT_TEXT)]
51+
df = assembly_report("GCF_000001405.40", verbose=True)
52+
self.assertEqual(list(df["Sequence-Name"]), ["1"])
53+
self.assertEqual(df.iloc[0]["UCSC-style-name"], "chr1")
54+
55+
mock_get.side_effect = [_FakeResp(_PARENT_HTML), _FakeResp(_REPORT_TEXT)]
56+
result = assembly_report("GCF_000001405.40", json=True, verbose=False)
57+
self.assertIsInstance(result, list)
58+
self.assertEqual(result[0]["RefSeq-Accn"], "NC_000001.11")
59+
60+
with tempfile.TemporaryDirectory() as tmp:
61+
cwd = os.getcwd()
62+
os.chdir(tmp)
63+
try:
64+
mock_get.side_effect = [_FakeResp(_PARENT_HTML), _FakeResp(_REPORT_TEXT)]
65+
assembly_report("GCF_000001405.40", save=True, verbose=False)
66+
self.assertTrue(os.path.exists("GCF_000001405.40_assembly_report.csv"))
67+
mock_get.side_effect = [_FakeResp(_PARENT_HTML), _FakeResp(_REPORT_TEXT)]
68+
assembly_report("GCF_000001405.40", json=True, save=True, verbose=False)
69+
self.assertTrue(os.path.exists("GCF_000001405.40_assembly_report.json"))
70+
finally:
71+
os.chdir(cwd)
72+
73+
@patch.object(gget_ref.requests, "get")
74+
def test_parent_dir_non_200_raises(self, mock_get):
75+
mock_get.return_value = _FakeResp("", status_code=404)
76+
with self.assertRaises(RuntimeError):
77+
assembly_report("GCF_000001405.40", verbose=False)
78+
79+
@patch.object(gget_ref.requests, "get")
80+
def test_no_folder_found_raises(self, mock_get):
81+
mock_get.return_value = _FakeResp("<html>no matching links</html>", status_code=200)
82+
with self.assertRaises(RuntimeError):
83+
assembly_report("GCF_000001405.40", verbose=False)
84+
85+
@patch.object(gget_ref.requests, "get")
86+
def test_report_non_200_raises(self, mock_get):
87+
mock_get.side_effect = [_FakeResp(_PARENT_HTML), _FakeResp("", status_code=500)]
88+
with self.assertRaises(RuntimeError):
89+
assembly_report("GCF_000001405.40", verbose=False)
90+
91+
@patch.object(gget_ref.requests, "get")
92+
def test_report_missing_header_raises(self, mock_get):
93+
# No "# Sequence-Name" header line -> columns stay None -> RuntimeError.
94+
mock_get.side_effect = [_FakeResp(_PARENT_HTML), _FakeResp("# only comments\nfoo\tbar\n")]
95+
with self.assertRaises(RuntimeError):
96+
assembly_report("GCF_000001405.40", verbose=False)
97+
98+
def test_ref_assembly_report_requires_species(self):
99+
with self.assertRaises(ValueError):
100+
ref(None, assembly_report=True, verbose=False)
101+
102+
@patch.object(gget_ref, "_assembly_report_fn")
103+
def test_ref_delegates_to_assembly_report(self, mock_fn):
104+
mock_fn.return_value = "DELEGATED"
105+
self.assertEqual(ref("GCF_000001405.40", assembly_report=True, verbose=False), "DELEGATED")
106+
mock_fn.assert_called_once()

0 commit comments

Comments
 (0)