fix: subset selection validation and remove unnecessary hash computation
Two bugs fixed in animal selection with checkboxes: 1. Confirmation dialog showed wrong count (e.g., "35 animals" instead of "2 animals" when only 2 were selected). Fixed by using valid_selected count in diff.server_count instead of full filter resolution count. 2. Spurious "Selection Changed" dialogs due to race condition in async hash computation. Fixed by removing client-side hash computation entirely - it was unnecessary since the server validates selected_ids directly against the filter resolution. Changes: - validation.py: Remove hash comparison in _validate_subset(), validate IDs directly, fix server_count in diff - animal_select.py: Remove computeSelectionHash(), hidden roster_hash field, and related async fetch code - test_selection_validation.py: Add tests for subset mode validation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -456,3 +456,86 @@ class TestSelectionMismatchError:
|
||||
|
||||
assert error.result is result
|
||||
assert error.result.diff is diff
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# Tests for validate_selection - subset mode
|
||||
# ============================================================================
|
||||
|
||||
|
||||
class TestValidateSelectionSubsetMode:
|
||||
"""Tests for validate_selection with subset_mode=True."""
|
||||
|
||||
def test_subset_mode_returns_valid_when_all_selected_match(
|
||||
self, seeded_db, animal_service, strip1_location_id
|
||||
):
|
||||
"""validate_selection returns valid=True when all selected IDs are in filter."""
|
||||
# Create cohort of 5 animals
|
||||
ts_utc = int(time.time() * 1000)
|
||||
payload = make_cohort_payload(strip1_location_id, count=5)
|
||||
event = animal_service.create_cohort(payload, ts_utc, "test_user")
|
||||
all_ids = event.entity_refs["animal_ids"]
|
||||
|
||||
# User selects only 2 of them
|
||||
selected_ids = all_ids[:2]
|
||||
subset_hash = compute_roster_hash(selected_ids, None)
|
||||
|
||||
ctx = SelectionContext(
|
||||
filter="species:duck",
|
||||
resolved_ids=all_ids, # Full filter resolution
|
||||
roster_hash=subset_hash, # Hash of selected subset
|
||||
ts_utc=ts_utc,
|
||||
from_location_id=None,
|
||||
subset_mode=True,
|
||||
selected_ids=selected_ids,
|
||||
)
|
||||
|
||||
result = validate_selection(seeded_db, ctx)
|
||||
|
||||
assert result.valid is True
|
||||
assert result.resolved_ids == selected_ids
|
||||
assert result.diff is None
|
||||
|
||||
def test_subset_mode_diff_server_count_is_valid_selected_count(
|
||||
self, seeded_db, animal_service, strip1_location_id, strip2_location_id
|
||||
):
|
||||
"""In subset mode, diff.server_count should be count of valid selected IDs, not full filter."""
|
||||
# Create cohort of 5 animals
|
||||
ts_create = int(time.time() * 1000)
|
||||
payload = make_cohort_payload(strip1_location_id, count=5)
|
||||
event = animal_service.create_cohort(payload, ts_create, "test_user")
|
||||
all_ids = event.entity_refs["animal_ids"]
|
||||
|
||||
# User selects 2 animals
|
||||
selected_ids = all_ids[:2]
|
||||
subset_hash = compute_roster_hash(selected_ids, None)
|
||||
|
||||
# Move one selected animal away (makes it invalid for the filter)
|
||||
ts_move = ts_create + 1000
|
||||
move_payload = AnimalMovedPayload(
|
||||
resolved_ids=[selected_ids[0]],
|
||||
from_location_id=strip1_location_id,
|
||||
to_location_id=strip2_location_id,
|
||||
)
|
||||
animal_service.move_animals(move_payload, ts_move, "test_user")
|
||||
|
||||
# Now validate at ts_move - one of the selected animals is no longer at Strip 1
|
||||
ctx = SelectionContext(
|
||||
filter="location:'Strip 1'",
|
||||
resolved_ids=all_ids, # Full filter resolution at creation time
|
||||
roster_hash=subset_hash,
|
||||
ts_utc=ts_move, # Validate at move time
|
||||
from_location_id=None,
|
||||
subset_mode=True,
|
||||
selected_ids=selected_ids,
|
||||
)
|
||||
|
||||
result = validate_selection(seeded_db, ctx)
|
||||
|
||||
assert result.valid is False
|
||||
assert result.diff is not None
|
||||
# BUG: diff.server_count is currently len(resolved_ids) = 4 (5 minus moved)
|
||||
# SHOULD BE: len(valid_selected) = 1 (2 selected minus 1 moved)
|
||||
assert result.diff.server_count == 1 # Only 1 valid selected animal remains
|
||||
assert result.diff.client_count == 2 # User selected 2
|
||||
assert selected_ids[0] in result.diff.removed # The moved animal is invalid
|
||||
|
||||
Reference in New Issue
Block a user