• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

SEED-platform / seed / #8932

10 Apr 2025 06:24PM UTC coverage: 86.881%. Remained the same
#8932

push

coveralls-python

web-flow
Merge 4ed62e124 into 8857f084b

25 of 43 new or added lines in 28 files covered. (58.14%)

41992 of 48333 relevant lines covered (86.88%)

0.87 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

40.48
/seed/tests/test_string_expression_parser.py
1
"""
2
SEED Platform (TM), Copyright (c) Alliance for Sustainable Energy, LLC, and other contributors.
3
See also https://github.com/SEED-platform/seed/blob/main/LICENSE.md
4

5
:author 'Piper Merriam <pmerriam@quickleft.com>'
6
"""
7

8
import itertools
1✔
9

10
from django.test import TestCase
11

12
from seed.utils.search import STRING_EXPRESSION_REGEX, is_string_expression, parse_expression
1✔
13

14

15
# Metaclass to create individual test methods per test case.
16
class TestCaseFactory(type):
1✔
17
    def __new__(cls, name, bases, attrs):
1✔
18
        cases = attrs["cases"]
×
19
        method_maker = attrs["method_maker"]
×
20
        prefix = attrs["prefix"]
×
21

22
        for doc, value, expected in cases:
×
23
            test = method_maker(value, expected)
×
NEW
24
            test_name = f"{prefix}_{doc.lower().replace(' ', '_')}"
×
25
            if test_name in attrs:
×
26
                raise KeyError(f"Test name {test_name} duplicated")
×
27
            test.__name__ = test_name
×
28
            test.__doc__ = doc
×
29
            attrs[test_name] = test
×
30
        return super().__new__(cls, name, bases, attrs)
×
31

32

33
def make_is_string_expression_method(value, expected):
1✔
34
    def run(self):
×
35
        result = is_string_expression(value)
×
36
        self.assertEqual(expected, bool(result), (expected, result, value))
×
37

38
    return run
×
39

40

41
class IsStringExpressionTests(TestCase):
1✔
42
    __metaclass__ = TestCaseFactory
1✔
43
    method_maker = make_is_string_expression_method
1✔
44
    prefix = "test_is_string_expression"
1✔
45

46
    # test name, input, expected output
47
    cases = [
1✔
48
        # Non expressions
49
        ("not_expression_1", "abcd", False),
50
        ("not_expression_2", "", False),
51
        ("not_expression_3", None, False),
52
        # Invalid operators
53
        ("not_expression_9", "<abc", False),
54
        ("not_expression_10", "<=abc", False),
55
        ("not_expression_11", ">abc", False),
56
        ("not_expression_12", ">=abc", False),
57
        # Incomplete expressions
58
        ("not_expression_4", "=", False),
59
        ("not_expression_5", "==", False),
60
        ("not_expression_6", "!=", False),
61
        ("not_expression_7", "!", False),
62
        ("not_expression_8", "<>", False),
63
        # Basic expressions
64
        ("equality_1", "=abcd", True),
65
        ("equality_2", "==abcd", True),
66
        ("inequality_1", "!=abcd", True),
67
        ("inequality_2", "!abcd", True),
68
        ("inequality_3", "<>abcd", True),
69
        # Empty string expressions
70
        ("empty_string_expression_1", "==''", True),
71
        ("empty_string_expression_2", '=""', True),
72
        # Whitespace
73
        ("whitespace_1", "=  abcd", True),
74
        ("whitespace_2", " == abcd ", True),
75
        # Internal whitespace
76
        ("internal_whitespace_1", "=  ab cd", True),
77
        ("internal_whitespace_2", "=  123 abcd", True),
78
        # Nulls checks
79
        ("is_null_1", "=null", True),
80
        ("is_null_2", "==null", True),
81
        ("is_not_null_1", "!=null", True),
82
        ("is_not_null_2", "!null", True),
83
        ("is_not_null_3", "<>null", True),
84
        # Complex Expressions
85
        ("complex_1", "!=abc,<>xyz", True),
86
        ("complex_2", "!abc, !xyz", True),
87
        ("complex_3", "!=abc , !=xyz", True),
88
        ("complex_4", "!abc,!xyz,!null", True),
89
        ("complex_5", "!abc,==", True),
90
    ]
91

92

93
def query_to_child_tuples(query):
1✔
94
    """
95
    Takes a Q object and extracts the underlying queries.  Returns an iterable
96
    of 3-tuples who's values are (negated, field_lookup, value)
97
    """
98
    if isinstance(query, tuple):
×
99
        return query
×
100
    return list(
×
101
        itertools.chain.from_iterable(
102
            ([tuple(itertools.chain.from_iterable(([query.negated], c)))] if isinstance(c, tuple) else query_to_child_tuples(c))
103
            for c in query.children
104
        )
105
    )
106

107

108
def make_parse_expression_method(value, expected):
1✔
109
    def run(self):
×
110
        parts = STRING_EXPRESSION_REGEX.findall(value)
×
111
        result = parse_expression("field", parts)
×
112
        query_children = query_to_child_tuples(result)
×
113
        self.assertEqual(expected, query_children)
×
114

115
    return run
×
116

117

118
class ExpressionParserTests(TestCase):
1✔
119
    __metaclass__ = TestCaseFactory
1✔
120
    method_maker = make_parse_expression_method
1✔
121
    prefix = "test_string_expression_parser"
1✔
122

123
    # test name, input, expected output
124
    cases = [
1✔
125
        ("equality_1", "=abcd", [(False, "field", "abcd")]),
126
        ("equality_2", "==abcd", [(False, "field", "abcd")]),
127
        ("inequality_1", "!=abcd", [(True, "field", "abcd")]),
128
        ("inequality_2", "!abcd", [(True, "field", "abcd")]),
129
        ("inequality_3", "<>abcd", [(True, "field", "abcd")]),
130
        # null
131
        ("is_null_1", "=null", [(False, "field__isnull", True)]),
132
        ("is_null_2", "==null", [(False, "field__isnull", True)]),
133
        ("is_not_null_1", "!null", [(False, "field__isnull", False)]),
134
        ("is_not_null_2", "!=null", [(False, "field__isnull", False)]),
135
        ("is_not_null_3", "<>null", [(False, "field__isnull", False)]),
136
        # complex expressions
137
        ("complex_1", "!=abcd,<>wxyz", [(True, "field", "abcd"), (True, "field", "wxyz")]),
138
        ("complex_2", "!null,!=wxyz", [(False, "field__isnull", False), (True, "field", "wxyz")]),
139
        # invalid
140
        ("invalid_null_1", ">null", []),
141
        ("invalid_null_2", ">=null", []),
142
        ("invalid_null_3", "<null", []),
143
        ("invalid_null_4", "<=null", []),
144
    ]
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc