Skip to content

Commit 2437d68

Browse files
committed
Handler Tests
- Add tests for all handlers
1 parent e5ba343 commit 2437d68

File tree

8 files changed

+949
-0
lines changed

8 files changed

+949
-0
lines changed

tests/unit/handlers/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# -*- coding: utf-8 -*-
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import pytest
4+
from unittest.mock import Mock, MagicMock
5+
6+
from pyard.handlers.allele_handler import AlleleHandler
7+
8+
9+
class TestAlleleHandler:
10+
"""Test cases for AlleleHandler class"""
11+
12+
@pytest.fixture
13+
def mock_ard(self):
14+
"""Create mock ARD instance"""
15+
ard = Mock()
16+
ard._config = {"ARS_as_lg": False}
17+
return ard
18+
19+
@pytest.fixture
20+
def mock_strategy_factory(self):
21+
"""Create mock strategy factory"""
22+
factory = Mock()
23+
mock_strategy = Mock()
24+
mock_strategy.reduce.return_value = "A*01:01G"
25+
factory.get_strategy.return_value = mock_strategy
26+
return factory
27+
28+
@pytest.fixture
29+
def allele_handler(self, mock_ard, mock_strategy_factory):
30+
"""Create AlleleHandler instance with mocked dependencies"""
31+
handler = AlleleHandler(mock_ard)
32+
handler.strategy_factory = mock_strategy_factory
33+
return handler
34+
35+
def test_init(self, mock_ard):
36+
"""Test AlleleHandler initialization"""
37+
handler = AlleleHandler(mock_ard)
38+
assert handler.ard == mock_ard
39+
assert handler.strategy_factory is not None
40+
41+
def test_reduce_allele(self, allele_handler, mock_strategy_factory):
42+
"""Test reduce_allele method"""
43+
result = allele_handler.reduce_allele("A*01:01:01", "G")
44+
45+
mock_strategy_factory.get_strategy.assert_called_once_with("G")
46+
mock_strategy_factory.get_strategy.return_value.reduce.assert_called_once_with(
47+
"A*01:01:01"
48+
)
49+
assert result == "A*01:01G"
50+
51+
def test_add_lg_suffix_single_allele_default(self, allele_handler):
52+
"""Test add_lg_suffix with single allele using default 'g' suffix"""
53+
result = allele_handler.add_lg_suffix("A*01:01")
54+
assert result == "A*01:01g"
55+
56+
def test_add_lg_suffix_single_allele_ars(self, mock_ard):
57+
"""Test add_lg_suffix with single allele using ARS suffix"""
58+
mock_ard._config = {"ARS_as_lg": True}
59+
handler = AlleleHandler(mock_ard)
60+
result = handler.add_lg_suffix("A*01:01")
61+
assert result == "A*01:01ARS"
62+
63+
def test_add_lg_suffix_multiple_alleles(self, allele_handler):
64+
"""Test add_lg_suffix with multiple alleles separated by '/'"""
65+
result = allele_handler.add_lg_suffix("A*01:01/A*01:02")
66+
assert result == "A*01:01g/A*01:02g"
67+
68+
def test_add_lg_suffix_multiple_alleles_ars(self, mock_ard):
69+
"""Test add_lg_suffix with multiple alleles using ARS suffix"""
70+
mock_ard._config = {"ARS_as_lg": True}
71+
handler = AlleleHandler(mock_ard)
72+
result = handler.add_lg_suffix("A*01:01/A*01:02")
73+
assert result == "A*01:01ARS/A*01:02ARS"
Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
# -*- coding: utf-8 -*-
2+
3+
import pytest
4+
from unittest.mock import Mock
5+
6+
from pyard.handlers.gl_string_processor import GLStringHandler
7+
from pyard.exceptions import InvalidAlleleError
8+
9+
10+
class TestGLStringHandler:
11+
"""Test cases for GLStringHandler class"""
12+
13+
@pytest.fixture
14+
def mock_ard(self):
15+
"""Create mock ARD instance"""
16+
17+
def redux_side_effect(x, y):
18+
# Mock the redux method to return the input string
19+
return x
20+
21+
def sort_comparator(a, b, ignore_suffixes=()):
22+
# Simple lexicographic comparison for consistent sorting
23+
if a < b:
24+
return -1
25+
elif a > b:
26+
return 1
27+
else:
28+
return 0
29+
30+
ard = Mock()
31+
ard._config = {"strict": False, "ignore_allele_with_suffixes": ()}
32+
ard.redux.side_effect = redux_side_effect
33+
ard.smart_sort_comparator.side_effect = sort_comparator
34+
ard._is_valid.return_value = True
35+
return ard
36+
37+
@pytest.fixture
38+
def gl_handler(self, mock_ard):
39+
"""Create GLStringHandler instance"""
40+
return GLStringHandler(mock_ard)
41+
42+
def test_init(self, mock_ard):
43+
"""Test GLStringHandler initialization"""
44+
handler = GLStringHandler(mock_ard)
45+
assert handler.ard == mock_ard
46+
47+
def test_process_gl_string_single_allele(self, gl_handler):
48+
"""Test processing single allele (no delimiters)"""
49+
result = gl_handler.process_gl_string("A*01:01", "G")
50+
assert result == "A*01:01"
51+
52+
def test_process_gl_string_caret_delimiter(self, gl_handler):
53+
"""Test processing GL string with ^ delimiter"""
54+
result = gl_handler.process_gl_string("A*01:01^B*07:02", "G")
55+
# Results are sorted - check that both components are present
56+
assert "A*01:01" in result and "B*07:02" in result and "^" in result
57+
58+
def test_process_gl_string_pipe_delimiter(self, gl_handler):
59+
"""Test processing GL string with | delimiter"""
60+
result = gl_handler.process_gl_string("A*01:01|B*07:02", "G")
61+
# Results are sorted - check that both components are present
62+
assert "A*01:01" in result and "B*07:02" in result and "|" in result
63+
64+
def test_process_gl_string_plus_delimiter(self, gl_handler):
65+
"""Test processing GL string with + delimiter"""
66+
result = gl_handler.process_gl_string("A*01:01+A*02:01", "G")
67+
assert result == "A*01:01+A*02:01"
68+
69+
def test_process_gl_string_tilde_delimiter(self, gl_handler):
70+
"""Test processing GL string with ~ delimiter"""
71+
result = gl_handler.process_gl_string("A*01:01~A*02:01", "G")
72+
assert result == "A*01:01~A*02:01"
73+
74+
def test_process_gl_string_slash_delimiter(self, gl_handler):
75+
"""Test processing GL string with / delimiter"""
76+
result = gl_handler.process_gl_string("A*01:01/A*02:01", "lgx")
77+
# Results are sorted - the actual sorting puts A*01:01 before A*02:01
78+
assert result == "A*01:01/A*02:01"
79+
80+
def test_process_gl_string_strict_mode_valid(self, mock_ard):
81+
"""Test processing with strict mode enabled and valid alleles"""
82+
mock_ard._config["strict"] = True
83+
handler = GLStringHandler(mock_ard)
84+
result = handler.process_gl_string("A*01:01", "G")
85+
assert result == "A*01:01"
86+
mock_ard._is_valid.assert_called_once_with("A*01:01")
87+
88+
def test_process_gl_string_strict_mode_invalid(self, mock_ard):
89+
"""Test processing with strict mode enabled and invalid alleles"""
90+
mock_ard._config["strict"] = True
91+
mock_ard._is_valid.return_value = False
92+
handler = GLStringHandler(mock_ard)
93+
94+
with pytest.raises(InvalidAlleleError):
95+
handler.process_gl_string("INVALID", "G")
96+
97+
def test_sorted_unique_gl_tilde_preserves_order(self, gl_handler):
98+
"""Test that ~ delimiter preserves original order"""
99+
result = gl_handler._sorted_unique_gl(["B*07:02", "A*01:01"], "~")
100+
assert result == "B*07:02~A*01:01"
101+
102+
def test_sorted_unique_gl_plus_sorts(self, gl_handler):
103+
"""Test that + delimiter sorts components"""
104+
result = gl_handler._sorted_unique_gl(["B*07:02", "A*01:01"], "+")
105+
assert "+" in result
106+
107+
def test_sorted_unique_gl_other_delimiters_flatten(self, gl_handler):
108+
"""Test that other delimiters flatten and deduplicate"""
109+
result = gl_handler._sorted_unique_gl(["A*01:01/A*02:01", "A*01:01"], "/")
110+
# Should flatten and deduplicate
111+
assert "/" in result
112+
113+
def test_validate_gl_string_single_valid_allele(self, gl_handler):
114+
"""Test validation of single valid allele"""
115+
result = gl_handler.validate_gl_string("A*01:01")
116+
assert result is True
117+
118+
def test_validate_gl_string_single_invalid_allele(self, mock_ard):
119+
"""Test validation of single invalid allele"""
120+
mock_ard._is_valid.return_value = False
121+
handler = GLStringHandler(mock_ard)
122+
123+
with pytest.raises(InvalidAlleleError):
124+
handler.validate_gl_string("INVALID")
125+
126+
def test_validate_gl_string_with_delimiters(self, gl_handler):
127+
"""Test validation of GL string with delimiters"""
128+
result = gl_handler.validate_gl_string("A*01:01^B*07:02")
129+
assert result is True
130+
131+
def test_validate_gl_string_mixed_valid_invalid(self, mock_ard):
132+
"""Test validation with mix of valid and invalid alleles"""
133+
mock_ard._is_valid.side_effect = lambda x: x != "INVALID"
134+
handler = GLStringHandler(mock_ard)
135+
136+
with pytest.raises(InvalidAlleleError):
137+
handler.validate_gl_string("A*01:01^INVALID")
138+
139+
@pytest.mark.parametrize(
140+
"redux_type", ["G", "P", "lg", "lgx", "W", "exon", "U2", "S"]
141+
)
142+
def test_process_gl_string_valid_redux_types(self, gl_handler, redux_type):
143+
"""Test processing with all valid reduction types"""
144+
result = gl_handler.process_gl_string("A*01:01", redux_type)
145+
assert result == "A*01:01"
146+
147+
def test_process_gl_string_invalid_redux_type(self, gl_handler):
148+
"""Test processing with invalid reduction type"""
149+
with pytest.raises(ValueError):
150+
gl_handler.process_gl_string("A*01:01", "INVALID")

0 commit comments

Comments
 (0)