Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/server-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ jobs:
run: npm run install-python-deps
- name: Install NPM packages
run: npm ci
- name: Run sequence-doc Python tests
working-directory: server/scripts/sequence-doc
run: python3 -m pytest -c pytest.toml
- name: Run tests
run: |
npm run start &
Expand Down
5 changes: 5 additions & 0 deletions server/scripts/sequence-doc/src/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ def reduce_to_boolean(boolList: list[bool]) -> bool:

def common_member(l1, l2) -> bool:
'''Is any member of l1 in l2

`l2` is materialized first so that one-shot iterables (e.g. `map`,
generators) can be passed safely: membership is tested once per element
of `l1`, which would otherwise exhaust the iterator after the first check.
'''
l2 = list(l2)
return [i for i in l1 if i in l2]

def remove_empty_strings(string_list: list[str]) -> list[str]:
Expand Down
53 changes: 53 additions & 0 deletions server/scripts/sequence-doc/tests/test_mogrifier.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
'''
Regression tests for issue #619:
`[ANY ...]` toggle dropped its section when the resolved selection list had
more than one entry and a non-matching entry was iterated before a matching one,
because `common_member` was handed a one-shot `map` iterator.
'''
import utils
from mogrifier import evaluate_annotation


def test_common_member_accepts_one_shot_iterable():
''' A `map`/generator must be usable even when the first few probes miss.
'''
haystack = map(str.upper, ['other', 'match'])
assert utils.common_member(['NOPE', 'MATCH'], haystack) == ['MATCH']


def test_any_toggle_kept_when_match_is_not_first_selection():
''' [ANY ...] keeps the section (returns False) when any compare value is
selected, regardless of ordering / list length.
'''
name_map = {
'buiPreCon': 'path.buiPreCon',
'ReturnFanMeasuredAir': 'ReturnFanMeasuredAir',
'ReturnFanCalculatedAir': 'ReturnFanCalculatedAir',
}
selections = {
# non-matching entry first, matching entry second
'path.buiPreCon': ['ReliefFan', 'ReturnFanCalculatedAir'],
}
op = {
'op': 'ANY',
'text': '[ANY buiPreCon ReturnFanMeasuredAir ReturnFanCalculatedAir]',
}

assert evaluate_annotation(op, name_map, selections) is False


def test_any_toggle_deleted_when_no_compare_value_selected():
''' [ANY ...] still deletes (returns True) when nothing matches.
'''
name_map = {
'buiPreCon': 'path.buiPreCon',
'ReturnFanMeasuredAir': 'ReturnFanMeasuredAir',
'ReturnFanCalculatedAir': 'ReturnFanCalculatedAir',
}
selections = {'path.buiPreCon': ['ReliefFan', 'ReliefDamper']}
op = {
'op': 'ANY',
'text': '[ANY buiPreCon ReturnFanMeasuredAir ReturnFanCalculatedAir]',
}

assert evaluate_annotation(op, name_map, selections) is True