"""Unit tests for format_response util.""" from types import SimpleNamespace from abacus_contract.utils.format_response import ( prepare_dataload_grouped_response, prepare_dataload_response, prepare_dataload_with_data_as_list_response, ) def test_prepare_dataload_response(): """Test prepare_dataload_response function.""" mock_data_1 = {'test_id': 1} mock_data_2 = {'test_id': 2} mock_list = [mock_data_1, mock_data_2] entity_ids = [1, 2, 3] res = prepare_dataload_response(entity_ids, mock_list, 'test_id') assert res == [{'data': mock_data_1}, {'data': mock_data_2}, {'data': None}] def test_prepare_dataload_with_data_as_list_response(): """Test prepare_dataload_with_data_as_list_response function.""" mock_data_1 = {'test_id': 1, 'other': 1} mock_data_2 = {'test_id': 1, 'other': 1} mock_data_3 = {'test_id': 2, 'other': 1} mock_list = [mock_data_1, mock_data_2, mock_data_3] entity_ids = [1, 2, 3] res = prepare_dataload_with_data_as_list_response(entity_ids, mock_list, 'test_id') assert res == [ {'data': [mock_data_1, mock_data_2]}, {'data': [mock_data_3]}, {'data': None}, ] def test_prepare_dataload_with_data_as_list_response_preserves_order_when_keys_interleave(): """Records are grouped per id and kept in first-seen order, not record order.""" a1 = {'test_id': 1, 'seq': 'a1'} b1 = {'test_id': 2, 'seq': 'b1'} a2 = {'test_id': 1, 'seq': 'a2'} # records arrive interleaved (1, 2, 1); each id's list must keep encounter order. mock_list = [a1, b1, a2] res = prepare_dataload_with_data_as_list_response([2, 1], mock_list, 'test_id') # response order follows entity_ids ([2, 1]), not record order. assert res == [{'data': [b1]}, {'data': [a1, a2]}] def test_prepare_dataload_with_data_as_list_response_repeats_group_for_duplicate_ids(): """A duplicated requested id yields its group once per occurrence, in order.""" r1 = {'test_id': 1, 'seq': 'r1'} r2 = {'test_id': 1, 'seq': 'r2'} res = prepare_dataload_with_data_as_list_response([1, 1, 2], [r1, r2], 'test_id') assert res == [{'data': [r1, r2]}, {'data': [r1, r2]}, {'data': None}] def test_prepare_dataload_with_data_as_list_response_ignores_missing_or_none_keys(): """Records with an absent or None key_field attach to no requested id.""" good = {'test_id': 1, 'seq': 'good'} none_key = {'test_id': None, 'seq': 'none'} missing_key = {'seq': 'missing'} mock_list = [good, none_key, missing_key] res = prepare_dataload_with_data_as_list_response([1, 2], mock_list, 'test_id') # id 1 gets only the real record; id 2 has none; the None/missing-keyed # records never surface against a requested id. assert res == [{'data': [good]}, {'data': None}] def _pair(gid, cid, name): return SimpleNamespace(gid=gid, child=SimpleNamespace(cid=cid, name=name)) def test_prepare_dataload_grouped_response_groups_dedups_sorts_and_serializes(): """Groups pairs by parent id, dedups + sorts children, serializes per id.""" pairs = [ _pair(10, 2, 'Beta'), _pair(10, 1, 'Alpha'), _pair(10, 1, 'Alpha'), # duplicate child in the same group _pair(20, 3, 'Gamma'), ] res = prepare_dataload_grouped_response( [10, 20, 30, 10], # 30 missing; duplicate 10 repeats its group pairs, group_key=lambda p: p.gid, child=lambda p: p.child, serialize_group=lambda children: [c.name for c in children], dedup_key=lambda c: c.cid, sort_key=lambda c: c.name, ) assert res == [ {'data': ['Alpha', 'Beta']}, # deduped and name-sorted {'data': ['Gamma']}, {'data': None}, # missing group {'data': ['Alpha', 'Beta']}, # duplicate requested id repeats the group ] def test_prepare_dataload_grouped_response_without_dedup_or_sort_keeps_encounter_order(): """Without dedup_key/sort_key, children keep encounter order and duplicates.""" pairs = [ SimpleNamespace(gid=1, child='b'), SimpleNamespace(gid=1, child='a'), SimpleNamespace(gid=2, child='c'), ] res = prepare_dataload_grouped_response( [1, 2], pairs, group_key=lambda p: p.gid, child=lambda p: p.child, serialize_group=list, ) assert res == [{'data': ['b', 'a']}, {'data': ['c']}] def test_prepare_dataload_grouped_response_dedups_per_group_not_globally(): """The same dedup identity in two different groups survives in each group.""" # child cid=1 appears in group 10 and group 20; a second cid=1 in group 20 # is the only one that should be dropped. pairs = [ SimpleNamespace(gid=10, child=SimpleNamespace(cid=1, name='A')), SimpleNamespace(gid=20, child=SimpleNamespace(cid=1, name='A')), SimpleNamespace(gid=20, child=SimpleNamespace(cid=1, name='A')), ] res = prepare_dataload_grouped_response( [10, 20], pairs, group_key=lambda p: p.gid, child=lambda p: p.child, serialize_group=lambda children: [c.cid for c in children], dedup_key=lambda c: c.cid, ) assert res == [{'data': [1]}, {'data': [1]}] def test_prepare_dataload_grouped_response_dedup_without_sort_keeps_first_occurrence(): """Dedup keeps the first occurrence and, with no sort_key, first-seen order.""" pairs = [ SimpleNamespace(gid=1, child=SimpleNamespace(cid=2, name='second')), SimpleNamespace(gid=1, child=SimpleNamespace(cid=1, name='first')), SimpleNamespace(gid=1, child=SimpleNamespace(cid=2, name='second-dup')), ] res = prepare_dataload_grouped_response( [1], pairs, group_key=lambda p: p.gid, child=lambda p: p.child, serialize_group=lambda children: [c.name for c in children], dedup_key=lambda c: c.cid, ) # cid 2 kept as its first occurrence ('second'); the later 'second-dup' drops. assert res == [{'data': ['second', 'first']}] def test_prepare_dataload_grouped_response_ignores_unrequested_groups(): """Pairs whose parent id was not requested never surface.""" pairs = [ SimpleNamespace(gid=1, child='a'), SimpleNamespace(gid=99, child='z'), ] res = prepare_dataload_grouped_response( [1], pairs, group_key=lambda p: p.gid, child=lambda p: p.child, serialize_group=list, ) assert res == [{'data': ['a']}] def test_prepare_dataload_grouped_response_empty_pairs_yields_all_none(): """No pairs means every requested id shapes to None.""" res = prepare_dataload_grouped_response( [1, 2], [], group_key=lambda p: p.gid, child=lambda p: p.child, serialize_group=list, ) assert res == [{'data': None}, {'data': None}] def test_prepare_dataload_grouped_response_empty_entity_ids_yields_empty(): """No requested ids means an empty response, regardless of pairs.""" res = prepare_dataload_grouped_response( [], [SimpleNamespace(gid=1, child='a')], group_key=lambda p: p.gid, child=lambda p: p.child, serialize_group=list, ) assert res == [] def test_prepare_dataload_grouped_response_does_not_serialize_missing_groups(): """serialize_group is invoked only for ids that actually have children.""" serialized = [] def serialize(children): serialized.append(list(children)) return list(children) res = prepare_dataload_grouped_response( [1, 2], # id 2 has no children [SimpleNamespace(gid=1, child='x')], group_key=lambda p: p.gid, child=lambda p: p.child, serialize_group=serialize, ) assert res == [{'data': ['x']}, {'data': None}] # Called exactly once (for id 1), never for the missing id 2 with an empty list. assert serialized == [['x']] def test_prepare_dataload_grouped_response_serialize_receives_deduped_sorted_list(): """serialize_group receives the already deduped-and-sorted children.""" received = [] pairs = [ SimpleNamespace(gid=1, child=SimpleNamespace(cid=2, name='B')), SimpleNamespace(gid=1, child=SimpleNamespace(cid=1, name='A')), SimpleNamespace(gid=1, child=SimpleNamespace(cid=1, name='A')), ] def serialize(children): received.append([c.name for c in children]) return [c.name for c in children] prepare_dataload_grouped_response( [1], pairs, group_key=lambda p: p.gid, child=lambda p: p.child, serialize_group=serialize, dedup_key=lambda c: c.cid, sort_key=lambda c: c.name, ) # Deduped to {A, B} then name-sorted to [A, B] before serialization. assert received == [['A', 'B']] def test_prepare_dataload_grouped_response_duplicate_id_serializes_per_occurrence(): """A duplicated requested id yields its group once per occurrence, in order.""" serialized = [] def serialize(children): serialized.append(list(children)) return list(children) res = prepare_dataload_grouped_response( [1, 1], [SimpleNamespace(gid=1, child='a')], group_key=lambda p: p.gid, child=lambda p: p.child, serialize_group=serialize, ) assert res == [{'data': ['a']}, {'data': ['a']}] # Serialized once per requested occurrence. assert serialized == [['a'], ['a']]