feat(reject-history): fix silent data loss by propagating partial failure metadata to frontend
Chunk failures in BatchQueryEngine were silently discarded — `has_partial_failure` was tracked in Redis but never surfaced to the API response or frontend. Users could see incomplete data without any warning. This commit closes the gap end-to-end: Backend: - Track failed chunk time ranges (`failed_ranges`) in batch engine progress metadata - Add single retry for transient Oracle errors (timeout, connection) in `_execute_single_chunk` - Read `get_batch_progress()` after merge but before `redis_clear_batch()` cleanup - Inject `has_partial_failure`, `failed_chunk_count`, `failed_ranges` into API response meta - Persist partial failure flag to independent Redis key with TTL aligned to data storage layer - Add shared container-resolution policy module with wildcard/expansion guardrails - Refactor reason filter from single-value to multi-select (`reason` → `reasons`) Frontend: - Add client-side date range validation (730-day limit) before API submission - Display amber warning banner on partial failure with specific failed date ranges - Support generic fallback message for container-mode queries without date ranges - Update FilterPanel to support multi-select reason chips Specs & tests: - Create batch-query-resilience spec; update reject-history-api and reject-history-page specs - Add 7 new tests for retry, memory guard, failed ranges, partial failure propagation, TTL - Cross-service regression verified (hold, resource, job, msd — 411 tests pass) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@ from unittest.mock import patch, MagicMock
|
||||
from mes_dashboard import create_app
|
||||
from mes_dashboard.core.cache import NoOpCache
|
||||
from mes_dashboard.core.rate_limit import reset_rate_limits_for_tests
|
||||
from mes_dashboard.services.query_tool_service import MAX_DATE_RANGE_DAYS, MAX_LOT_IDS
|
||||
from mes_dashboard.services.query_tool_service import MAX_DATE_RANGE_DAYS
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -118,20 +118,19 @@ class TestResolveEndpoint:
|
||||
data = json.loads(response.data)
|
||||
assert 'error' in data
|
||||
|
||||
def test_values_over_limit(self, client):
|
||||
"""Should reject values exceeding limit."""
|
||||
values = [f'GA{i:09d}' for i in range(MAX_LOT_IDS + 1)]
|
||||
def test_rejects_too_broad_wildcard(self, client):
|
||||
"""Should reject wildcard patterns that are too broad."""
|
||||
response = client.post(
|
||||
'/api/query-tool/resolve',
|
||||
json={
|
||||
'input_type': 'lot_id',
|
||||
'values': values
|
||||
}
|
||||
)
|
||||
'values': ['%']
|
||||
}
|
||||
)
|
||||
assert response.status_code == 400
|
||||
data = json.loads(response.data)
|
||||
assert 'error' in data
|
||||
assert '超過上限' in data['error'] or str(MAX_LOT_IDS) in data['error']
|
||||
assert '萬用字元條件過於寬鬆' in data['error']
|
||||
|
||||
@patch('mes_dashboard.routes.query_tool_routes.resolve_lots')
|
||||
def test_resolve_success(self, mock_resolve, client):
|
||||
|
||||
Reference in New Issue
Block a user