Skip to content

Commit 280b2fd

Browse files
committed
Add unit tests for measurement grouping and condition extraction
Introduced a new test suite in `test_group_measurements.py` to validate the functionality of `_get_measurement_conditions` and `group_measurements` functions. The tests cover various scenarios including prepared and initial values, precedence rules, and grouping behavior based on identical and differing conditions. This enhances the test coverage for measurement data handling in the project.
1 parent 45ae733 commit 280b2fd

1 file changed

Lines changed: 364 additions & 0 deletions

File tree

Lines changed: 364 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,364 @@
1+
from unittest.mock import Mock
2+
3+
from pyenzyme.tools import (
4+
_conditions_to_hashable,
5+
_get_measurement_conditions,
6+
group_measurements,
7+
)
8+
9+
10+
class TestGetMeasurementConditions:
11+
"""Test suite for _get_measurement_conditions function"""
12+
13+
def test_with_prepared_values(self):
14+
"""Test extracting conditions from measurement with prepared values"""
15+
# Arrange
16+
species_data = [
17+
Mock(species_id="s1", prepared=10.0, initial=None),
18+
Mock(species_id="s2", prepared=5.0, initial=None),
19+
]
20+
measurement = Mock(species_data=species_data, ph=7.4, temperature=25.0)
21+
22+
# Act
23+
conditions = _get_measurement_conditions(measurement)
24+
25+
# Assert
26+
expected = {"s1": 10.0, "s2": 5.0, "ph": 7.4, "temperature": 25.0}
27+
assert conditions == expected
28+
29+
def test_with_initial_values(self):
30+
"""Test extracting conditions from measurement with initial values"""
31+
# Arrange
32+
species_data = [
33+
Mock(species_id="s1", prepared=None, initial=8.0),
34+
Mock(species_id="s2", prepared=None, initial=3.0),
35+
]
36+
measurement = Mock(species_data=species_data, ph=6.8, temperature=30.0)
37+
38+
# Act
39+
conditions = _get_measurement_conditions(measurement)
40+
41+
# Assert
42+
expected = {"s1": 8.0, "s2": 3.0, "ph": 6.8, "temperature": 30.0}
43+
assert conditions == expected
44+
45+
def test_prepared_takes_precedence_over_initial(self):
46+
"""Test that prepared values take precedence over initial values"""
47+
# Arrange
48+
species_data = [
49+
Mock(species_id="s1", prepared=15.0, initial=10.0),
50+
Mock(species_id="s2", prepared=None, initial=5.0),
51+
]
52+
measurement = Mock(species_data=species_data, ph=None, temperature=None)
53+
54+
# Act
55+
conditions = _get_measurement_conditions(measurement)
56+
57+
# Assert
58+
expected = {
59+
"s1": 15.0, # prepared value used
60+
"s2": 5.0, # initial value used since prepared is None
61+
}
62+
assert conditions == expected
63+
64+
def test_with_no_concentration_values(self):
65+
"""Test with species data having no prepared or initial values"""
66+
# Arrange
67+
species_data = [
68+
Mock(species_id="s1", prepared=None, initial=None),
69+
Mock(species_id="s2", prepared=12.0, initial=None),
70+
]
71+
measurement = Mock(species_data=species_data, ph=7.0, temperature=None)
72+
73+
# Act
74+
conditions = _get_measurement_conditions(measurement)
75+
76+
# Assert
77+
expected = {
78+
"s2": 12.0, # only s2 included since s1 has no values
79+
"ph": 7.0,
80+
}
81+
assert conditions == expected
82+
83+
def test_with_no_environmental_conditions(self):
84+
"""Test with measurement having no pH or temperature"""
85+
# Arrange
86+
species_data = [
87+
Mock(species_id="enzyme", prepared=1.0, initial=None),
88+
]
89+
measurement = Mock(species_data=species_data, ph=None, temperature=None)
90+
91+
# Act
92+
conditions = _get_measurement_conditions(measurement)
93+
94+
# Assert
95+
expected = {
96+
"enzyme": 1.0,
97+
}
98+
assert conditions == expected
99+
100+
def test_with_empty_species_data(self):
101+
"""Test with measurement having no species data"""
102+
# Arrange
103+
measurement = Mock(species_data=[], ph=7.5, temperature=37.0)
104+
105+
# Act
106+
conditions = _get_measurement_conditions(measurement)
107+
108+
# Assert
109+
expected = {"ph": 7.5, "temperature": 37.0}
110+
assert conditions == expected
111+
112+
113+
class TestConditionsToHashable:
114+
"""Test suite for _conditions_to_hashable function"""
115+
116+
def test_with_normal_conditions(self):
117+
"""Test converting normal conditions dict to hashable tuple"""
118+
# Arrange
119+
conditions = {"s1": 10.0, "ph": 7.4, "temperature": 25.0, "s2": 5.0}
120+
121+
# Act
122+
hashable = _conditions_to_hashable(conditions)
123+
124+
# Assert
125+
expected = (("ph", 7.4), ("s1", 10.0), ("s2", 5.0), ("temperature", 25.0))
126+
assert hashable == expected
127+
assert isinstance(hashable, tuple)
128+
129+
def test_with_empty_conditions(self):
130+
"""Test converting empty conditions dict"""
131+
# Arrange
132+
conditions = {}
133+
134+
# Act
135+
hashable = _conditions_to_hashable(conditions)
136+
137+
# Assert
138+
assert hashable == ()
139+
assert isinstance(hashable, tuple)
140+
141+
def test_order_consistency(self):
142+
"""Test that the same conditions always produce the same hashable tuple"""
143+
# Arrange
144+
conditions1 = {"b": 2, "a": 1, "c": 3}
145+
conditions2 = {"c": 3, "a": 1, "b": 2}
146+
147+
# Act
148+
hashable1 = _conditions_to_hashable(conditions1)
149+
hashable2 = _conditions_to_hashable(conditions2)
150+
151+
# Assert
152+
assert hashable1 == hashable2
153+
assert hashable1 == (("a", 1), ("b", 2), ("c", 3))
154+
155+
def test_hashable_can_be_used_as_dict_key(self):
156+
"""Test that the returned tuple can be used as a dictionary key"""
157+
# Arrange
158+
conditions = {"s1": 10.0, "ph": 7.4}
159+
hashable = _conditions_to_hashable(conditions)
160+
161+
# Act & Assert
162+
test_dict = {hashable: "group_1"}
163+
assert test_dict[hashable] == "group_1"
164+
165+
166+
class TestGroupMeasurements:
167+
"""Test suite for group_measurements function"""
168+
169+
def test_with_identical_conditions(self):
170+
"""Test grouping measurements with identical conditions"""
171+
# Arrange
172+
species_data1 = [Mock(species_id="s1", prepared=10.0, initial=None)]
173+
species_data2 = [Mock(species_id="s1", prepared=10.0, initial=None)]
174+
175+
measurement1 = Mock(
176+
species_data=species_data1, ph=7.4, temperature=25.0, group_id=None
177+
)
178+
measurement2 = Mock(
179+
species_data=species_data2, ph=7.4, temperature=25.0, group_id=None
180+
)
181+
182+
enzmldoc = Mock(measurements=[measurement1, measurement2])
183+
184+
# Act
185+
result = group_measurements(enzmldoc)
186+
187+
# Assert
188+
assert result == enzmldoc
189+
assert measurement1.group_id == measurement2.group_id
190+
assert measurement1.group_id == "group_0"
191+
192+
def test_with_different_conditions(self):
193+
"""Test grouping measurements with different conditions"""
194+
# Arrange
195+
species_data1 = [Mock(species_id="s1", prepared=10.0, initial=None)]
196+
species_data2 = [
197+
Mock(species_id="s1", prepared=15.0, initial=None)
198+
] # Different concentration
199+
200+
measurement1 = Mock(
201+
species_data=species_data1, ph=7.4, temperature=25.0, group_id=None
202+
)
203+
measurement2 = Mock(
204+
species_data=species_data2, ph=7.4, temperature=25.0, group_id=None
205+
)
206+
207+
enzmldoc = Mock(measurements=[measurement1, measurement2])
208+
209+
# Act
210+
result = group_measurements(enzmldoc)
211+
212+
# Assert
213+
assert result == enzmldoc
214+
assert measurement1.group_id != measurement2.group_id
215+
assert measurement1.group_id == "group_0"
216+
assert measurement2.group_id == "group_1"
217+
218+
def test_with_different_ph(self):
219+
"""Test grouping measurements with different pH values"""
220+
# Arrange
221+
species_data1 = [Mock(species_id="s1", prepared=10.0, initial=None)]
222+
species_data2 = [Mock(species_id="s1", prepared=10.0, initial=None)]
223+
224+
measurement1 = Mock(
225+
species_data=species_data1, ph=7.4, temperature=25.0, group_id=None
226+
)
227+
measurement2 = Mock(
228+
species_data=species_data2,
229+
ph=8.0, # Different pH
230+
temperature=25.0,
231+
group_id=None,
232+
)
233+
234+
enzmldoc = Mock(measurements=[measurement1, measurement2])
235+
236+
# Act
237+
group_measurements(enzmldoc)
238+
239+
# Assert
240+
assert measurement1.group_id != measurement2.group_id
241+
assert measurement1.group_id == "group_0"
242+
assert measurement2.group_id == "group_1"
243+
244+
def test_with_no_measurements(self):
245+
"""Test with document containing no measurements"""
246+
# Arrange
247+
enzmldoc = Mock(measurements=[])
248+
249+
# Act
250+
result = group_measurements(enzmldoc)
251+
252+
# Assert
253+
assert result == enzmldoc
254+
255+
def test_with_single_measurement(self):
256+
"""Test with document containing single measurement"""
257+
# Arrange
258+
species_data = [Mock(species_id="s1", prepared=10.0, initial=None)]
259+
measurement = Mock(
260+
species_data=species_data, ph=7.4, temperature=25.0, group_id=None
261+
)
262+
enzmldoc = Mock(measurements=[measurement])
263+
264+
# Act
265+
result = group_measurements(enzmldoc)
266+
267+
# Assert
268+
assert result == enzmldoc
269+
assert measurement.group_id == "group_0"
270+
271+
def test_with_multiple_identical_and_different_conditions(self):
272+
"""Test complex scenario with multiple measurements having various conditions"""
273+
# Arrange
274+
# Two measurements with identical conditions
275+
species_data1 = [Mock(species_id="s1", prepared=10.0, initial=None)]
276+
species_data2 = [Mock(species_id="s1", prepared=10.0, initial=None)]
277+
278+
# One measurement with different conditions
279+
species_data3 = [Mock(species_id="s1", prepared=15.0, initial=None)]
280+
281+
# Another measurement identical to the first two
282+
species_data4 = [Mock(species_id="s1", prepared=10.0, initial=None)]
283+
284+
measurement1 = Mock(
285+
species_data=species_data1, ph=7.4, temperature=25.0, group_id=None
286+
)
287+
measurement2 = Mock(
288+
species_data=species_data2, ph=7.4, temperature=25.0, group_id=None
289+
)
290+
measurement3 = Mock(
291+
species_data=species_data3, ph=7.4, temperature=25.0, group_id=None
292+
)
293+
measurement4 = Mock(
294+
species_data=species_data4, ph=7.4, temperature=25.0, group_id=None
295+
)
296+
297+
enzmldoc = Mock(
298+
measurements=[measurement1, measurement2, measurement3, measurement4]
299+
)
300+
301+
# Act
302+
group_measurements(enzmldoc)
303+
304+
# Assert
305+
# measurement1, measurement2, and measurement4 should have the same group_id
306+
assert measurement1.group_id == measurement2.group_id == measurement4.group_id
307+
assert measurement3.group_id != measurement1.group_id
308+
assert measurement1.group_id == "group_0"
309+
assert measurement3.group_id == "group_1"
310+
311+
def test_group_id_format(self):
312+
"""Test that group IDs follow the expected format"""
313+
# Arrange
314+
species_data1 = [Mock(species_id="s1", prepared=10.0, initial=None)]
315+
species_data2 = [Mock(species_id="s1", prepared=15.0, initial=None)]
316+
species_data3 = [Mock(species_id="s1", prepared=20.0, initial=None)]
317+
species_data4 = [Mock(species_id="s1", prepared=20.0, initial=None)]
318+
319+
measurement1 = Mock(
320+
species_data=species_data1, ph=7.4, temperature=25.0, group_id=None
321+
)
322+
measurement2 = Mock(
323+
species_data=species_data2, ph=7.4, temperature=25.0, group_id=None
324+
)
325+
measurement3 = Mock(
326+
species_data=species_data3, ph=7.4, temperature=25.0, group_id=None
327+
)
328+
measurement4 = Mock(
329+
species_data=species_data4, ph=7.4, temperature=25.0, group_id=None
330+
)
331+
332+
enzmldoc = Mock(
333+
measurements=[measurement1, measurement2, measurement3, measurement4]
334+
)
335+
336+
# Act
337+
group_measurements(enzmldoc)
338+
339+
# Assert
340+
assert measurement1.group_id == "group_0"
341+
assert measurement2.group_id == "group_1"
342+
assert measurement3.group_id == "group_2"
343+
assert measurement4.group_id == "group_2"
344+
345+
346+
class TestGroupMeasurementsIntegration:
347+
"""Integration tests using the existing fixtures"""
348+
349+
def test_with_valid_measurement_fixture(self, measurement_valid):
350+
"""Test group_measurements with the existing valid measurement fixture"""
351+
# Arrange
352+
original_measurements = measurement_valid.measurements.copy()
353+
354+
# Act
355+
result = group_measurements(measurement_valid)
356+
357+
# Assert
358+
assert result == measurement_valid
359+
assert len(measurement_valid.measurements) == len(original_measurements)
360+
361+
# Check that all measurements got group_ids assigned
362+
for measurement in measurement_valid.measurements:
363+
assert measurement.group_id is not None
364+
assert measurement.group_id.startswith("group_")

0 commit comments

Comments
 (0)