Skip to content

Commit

Permalink
Tests: Add Tests to contains Filter Operator on PostgreSQL Backend (#…
Browse files Browse the repository at this point in the history
…6617)

This PR adds tests to validate the behavior of the `contains` operator when applied to JSON objects and arrays in the PostgreSQL backend. The tests cover a variety of scenarios, including type matching, negations, and edge cases such as empty arrays or objects. This is a test-only PR, and no features or functionality in aiida-core have been modified.
  • Loading branch information
rabbull authored Nov 26, 2024
1 parent 41a0fd9 commit c789d0a
Showing 1 changed file with 161 additions and 0 deletions.
161 changes: 161 additions & 0 deletions tests/orm/test_querybuilder.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
"""Tests for the QueryBuilder."""

import copy
import json
import uuid
import warnings
from collections import defaultdict
Expand Down Expand Up @@ -1702,3 +1703,163 @@ def test_statistics_default_class(self, aiida_localhost):
# data are correct
res = next(iter(qb.dict()[0].values()))
assert res == expected_dict


class TestJsonFilters:
@pytest.mark.parametrize(
'data,filters,is_match',
(
# contains different types of element
({'arr': [1, '2', None]}, {'attributes.arr': {'contains': [1]}}, True),
({'arr': [1, '2', None]}, {'attributes.arr': {'contains': ['2']}}, True),
({'arr': [1, '2', None]}, {'attributes.arr': {'contains': [None]}}, True),
# contains multiple elements of various types
({'arr': [1, '2', None]}, {'attributes.arr': {'contains': [1, None]}}, True),
# contains non-exist elements
({'arr': [1, '2', None]}, {'attributes.arr': {'contains': [114514]}}, False),
# contains empty set
({'arr': [1, '2', None]}, {'attributes.arr': {'contains': []}}, True),
({'arr': []}, {'attributes.arr': {'contains': []}}, True),
# nested arrays
({'arr': [[1, 0], [0, 2]]}, {'attributes.arr': {'contains': [[1, 0]]}}, True),
({'arr': [[2, 3], [0, 1], []]}, {'attributes.arr': {'contains': [[1, 0]]}}, True),
({'arr': [[2, 3], [1]]}, {'attributes.arr': {'contains': [[4]]}}, False),
({'arr': [[1, 0], [0, 2]]}, {'attributes.arr': {'contains': [[3]]}}, False),
({'arr': [[1, 0], [0, 2]]}, {'attributes.arr': {'contains': [3]}}, False),
({'arr': [[1, 0], [0, 2]]}, {'attributes.arr': {'contains': [[2]]}}, True),
({'arr': [[1, 0], [0, 2]]}, {'attributes.arr': {'contains': [2]}}, False),
({'arr': [[1, 0], [0, 2], 3]}, {'attributes.arr': {'contains': [[3]]}}, False),
({'arr': [[1, 0], [0, 2], 3]}, {'attributes.arr': {'contains': [3]}}, True),
# negations
({'arr': [1, '2', None]}, {'attributes.arr': {'!contains': [1]}}, False),
({'arr': [1, '2', None]}, {'attributes.arr': {'!contains': []}}, False),
({'arr': [1, '2', None]}, {'attributes.arr': {'!contains': [114514]}}, True),
({'arr': [1, '2', None]}, {'attributes.arr': {'!contains': [1, 114514]}}, True),
# TODO: these pass, but why? are these behaviors expected?
# non-exist `attr_key`s
({'foo': []}, {'attributes.arr': {'contains': []}}, False),
({'foo': []}, {'attributes.arr': {'!contains': []}}, False),
),
ids=json.dumps,
)
@pytest.mark.usefixtures('aiida_profile_clean')
@pytest.mark.requires_psql
def test_json_filters_contains_arrays(self, data, filters, is_match):
"""Test QueryBuilder filter `contains` for JSON array fields"""
orm.Dict(data).store()
qb = orm.QueryBuilder().append(orm.Dict, filters=filters)
assert qb.count() in {0, 1}
found = qb.count() == 1
assert found == is_match

@pytest.mark.parametrize(
'data,filters,is_match',
(
# contains different types of values
(
{
'dict': {
'k1': 1,
'k2': '2',
'k3': None,
}
},
{'attributes.dict': {'contains': {'k1': 1}}},
True,
),
(
{
'dict': {
'k1': 1,
'k2': '2',
'k3': None,
}
},
{'attributes.dict': {'contains': {'k1': 1, 'k2': '2'}}},
True,
),
(
{
'dict': {
'k1': 1,
'k2': '2',
'k3': None,
}
},
{'attributes.dict': {'contains': {'k3': None}}},
True,
),
# contains empty set
(
{
'dict': {
'k1': 1,
'k2': '2',
'k3': None,
}
},
{'attributes.dict': {'contains': {}}},
True,
),
# doesn't contain non-exist entries
(
{
'dict': {
'k1': 1,
'k2': '2',
'k3': None,
}
},
{'attributes.dict': {'contains': {'k1': 1, 'k': 'v'}}},
False,
),
# negations
(
{
'dict': {
'k1': 1,
'k2': '2',
'k3': None,
}
},
{'attributes.dict': {'!contains': {'k1': 1}}},
False,
),
(
{
'dict': {
'k1': 1,
'k2': '2',
'k3': None,
}
},
{'attributes.dict': {'!contains': {'k1': 1, 'k': 'v'}}},
True,
),
(
{
'dict': {
'k1': 1,
'k2': '2',
'k3': None,
}
},
{'attributes.dict': {'!contains': {}}},
False,
),
# TODO: these pass, but why? are these behaviors expected?
# non-exist `attr_key`s
({'map': {}}, {'attributes.dict': {'contains': {}}}, False),
({'map': {}}, {'attributes.dict': {'!contains': {}}}, False),
),
ids=json.dumps,
)
@pytest.mark.usefixtures('aiida_profile_clean')
@pytest.mark.requires_psql
def test_json_filters_contains_object(self, data, filters, is_match):
"""Test QueryBuilder filter `contains` for JSON object fields"""
orm.Dict(data).store()
qb = orm.QueryBuilder().append(orm.Dict, filters=filters)
assert qb.count() in {0, 1}
found = qb.count() == 1
assert found == is_match

0 comments on commit c789d0a

Please sign in to comment.