Skip to content
Open
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
17 changes: 16 additions & 1 deletion src/country_workspace/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,18 @@ def update_checksum(self, update_fields: Iterable[str] | None) -> Iterable[str]
def checker(self) -> "DataChecker":
raise NotImplementedError

@staticmethod
def _normalize_invalid_value_for_field(field: Any, value: Any) -> Any:
if not getattr(getattr(field, "widget", None), "allow_multiple_selected", False):
return value
if value is None:
return []
if isinstance(value, list):
return value
if isinstance(value, tuple | set):
return list(value)
return [value]

def validate_with_checker(self, fail_if_alien: bool = False) -> bool:
update_fields = []
errors = self.checker.validate([self.flex_fields], fail_if_alien=fail_if_alien)
Expand All @@ -138,7 +150,10 @@ def validate_with_checker(self, fail_if_alien: bool = False) -> bool:
# keep invalid values
for field_name in new_errors:
if field_name in flex_fields:
self.flex_fields[field_name] = flex_fields[field_name]
field = self.checker.form.fields.get(field_name)
self.flex_fields[field_name] = self._normalize_invalid_value_for_field(
field, flex_fields[field_name]
)
update_fields.append("flex_fields")

self.last_checked = timezone.now()
Expand Down
70 changes: 70 additions & 0 deletions tests/models/test_m_household.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

import pytest

from country_workspace.models.base import Validable

if TYPE_CHECKING:
from country_workspace.models import Household
from country_workspace.workspaces.models import CountryHousehold, CountryIndividual
Expand Down Expand Up @@ -35,3 +37,71 @@ def test_validate_with_checker(individual: "CountryHousehold"):
with mock.patch.object(household.program.beneficiary_validator, "validate", Mock(return_value=["Error"])):
assert not household.validate_with_checker()
assert household.errors == {"dct": ["Error"]}


def test_validate_with_checker_preserves_multiselect_invalid_values_as_list(household):
checker = Mock()
checker.validate.return_value = {household.pk: {"reasons_oos": ["Invalid value"]}}
checker.form = Mock(
cleaned_data={},
fields={
"reasons_oos": Mock(widget=Mock(allow_multiple_selected=True)),
},
)
household.flex_fields = {"reasons_oos": "invalid-choice"}

with mock.patch.object(type(household), "checker", mock.PropertyMock(return_value=checker)):
household.validate_with_checker()

household.refresh_from_db()
assert household.flex_fields["reasons_oos"] == ["invalid-choice"]


def test_normalize_invalid_value_for_field_passthrough_for_non_multiselect():
field = Mock(widget=Mock(allow_multiple_selected=False))

value = "raw-value"
result = Validable._normalize_invalid_value_for_field(field, value)

assert result == value


def test_normalize_invalid_value_for_field_none_for_multiselect():
field = Mock(widget=Mock(allow_multiple_selected=True))

result = Validable._normalize_invalid_value_for_field(field, None)

assert result == []


def test_normalize_invalid_value_for_field_list_for_multiselect():
field = Mock(widget=Mock(allow_multiple_selected=True))

value = ["a", "b"]
result = Validable._normalize_invalid_value_for_field(field, value)

assert result == value


def test_normalize_invalid_value_for_field_tuple_for_multiselect():
field = Mock(widget=Mock(allow_multiple_selected=True))

result = Validable._normalize_invalid_value_for_field(field, ("a", "b"))

assert result == ["a", "b"]


def test_normalize_invalid_value_for_field_set_for_multiselect():
field = Mock(widget=Mock(allow_multiple_selected=True))

result = Validable._normalize_invalid_value_for_field(field, {"a", "b"})

assert set(result) == {"a", "b"}


def test_normalize_invalid_value_for_field_scalar_for_multiselect():
field = Mock(widget=Mock(allow_multiple_selected=True))

result = Validable._normalize_invalid_value_for_field(field, "single")

assert result == ["single"]