Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

176 lines
7.5 KiB
Python

"""Tests for DB-backed context warning checks.
Only mock needed: SQLAlchemy session (no Flask dependency).
"""
from unittest.mock import Mock
from local_deep_research.web.warning_checks.context import (
check_context_below_history,
check_context_truncation_history,
)
def _mock_db_with_context_records(records, truncation_count=0):
"""Build a mock db_session that returns *records* for the context query
and *truncation_count* for the truncation count query.
"""
db_session = Mock()
context_query = Mock()
context_query.filter.return_value.order_by.return_value.limit.return_value.all.return_value = records
truncation_query = Mock()
truncation_query.filter.return_value.filter.return_value.scalar.return_value = truncation_count
db_session.query.side_effect = [context_query, truncation_query]
return db_session
class TestCheckContextBelowHistory:
"""Tests for check_context_below_history."""
def test_warns_when_below_historical_min(self):
db = _mock_db_with_context_records([(8192,)] * 10)
result = check_context_below_history(db, local_context=2048)
assert result is not None
assert result["type"] == "context_below_history"
assert "2,048" in result["message"]
def test_no_warning_when_at_or_above_historical(self):
db = _mock_db_with_context_records([(4096,)] * 10)
result = check_context_below_history(db, local_context=4096)
assert result is None
def test_no_warning_fewer_than_5_records(self):
db = _mock_db_with_context_records([(8192,)] * 3)
result = check_context_below_history(db, local_context=2048)
assert result is None
def test_no_warning_empty_records(self):
db = _mock_db_with_context_records([])
result = check_context_below_history(db, local_context=2048)
assert result is None
def test_exactly_5_records_is_enough(self):
"""Boundary: exactly 5 records should be sufficient."""
db = _mock_db_with_context_records([(8192,)] * 5)
result = check_context_below_history(db, local_context=2048)
assert result is not None
assert result["type"] == "context_below_history"
def test_exactly_4_records_not_enough(self):
"""Boundary: 4 records is below the threshold of 5."""
db = _mock_db_with_context_records([(8192,)] * 4)
result = check_context_below_history(db, local_context=2048)
assert result is None
def test_none_values_filtered_out(self):
"""Records with None context_limit are filtered from percentile calc."""
# 6 records total but 4 are None → only 2 valid → below threshold
records = [(None,)] * 4 + [(8192,)] * 2
db = _mock_db_with_context_records(records)
result = check_context_below_history(db, local_context=2048)
# Only 2 non-None records < 5 threshold, but the query returned 6 rows
# so len(recent_contexts) >= 5 passes, but after filtering context_values
# has only 2 entries. Still, percentile calc should work on 2 values.
# With sorted [8192, 8192], idx=0, min_safe=8192, 2048 < 8192 → warn
assert result is not None
def test_all_none_values_after_filtering(self):
"""If all records have None/0 context_limit after filtering, no warning."""
records = [(None,)] * 5 + [(0,)] * 5
db = _mock_db_with_context_records(records)
result = check_context_below_history(db, local_context=2048)
assert result is None
def test_mixed_context_values_percentile(self):
"""With varied values, percentile should pick the 1st percentile."""
# 100 records: sorted values [1000, 2000, 2000, ..., 2000, 8000, 8000, ...]
records = [(1000,)] * 1 + [(2000,)] * 49 + [(8000,)] * 50
db = _mock_db_with_context_records(records)
# idx = max(0, int(100 * 0.01)) = 1 → sorted[1] = 2000
result = check_context_below_history(db, local_context=1500)
assert result is not None
assert "1,500" in result["message"]
def test_mixed_context_values_no_warning_above_percentile(self):
"""No warning when above the 1st percentile threshold."""
records = [(1000,)] * 1 + [(2000,)] * 49 + [(8000,)] * 50
db = _mock_db_with_context_records(records)
result = check_context_below_history(db, local_context=2000)
assert result is None
def test_min_safe_context_in_message(self):
db = _mock_db_with_context_records([(16384,)] * 10)
result = check_context_below_history(db, local_context=4096)
assert "16,384" in result["message"]
def test_dismiss_key(self):
db = _mock_db_with_context_records([(8192,)] * 10)
result = check_context_below_history(db, local_context=2048)
assert result["dismissKey"] == "app.warnings.dismiss_context_reduced"
class TestCheckContextTruncationHistory:
"""Tests for check_context_truncation_history."""
def test_warns_when_truncation_exists(self):
db = Mock()
db.query.return_value.filter.return_value.filter.return_value.scalar.return_value = 3
result = check_context_truncation_history(db, local_context=4096)
assert result is not None
assert result["type"] == "context_truncation_history"
assert "3 time(s)" in result["message"]
def test_no_warning_zero_truncations(self):
db = Mock()
db.query.return_value.filter.return_value.filter.return_value.scalar.return_value = 0
result = check_context_truncation_history(db, local_context=4096)
assert result is None
def test_no_warning_none_truncations(self):
db = Mock()
db.query.return_value.filter.return_value.filter.return_value.scalar.return_value = None
result = check_context_truncation_history(db, local_context=4096)
assert result is None
def test_single_truncation_message(self):
db = Mock()
db.query.return_value.filter.return_value.filter.return_value.scalar.return_value = 1
result = check_context_truncation_history(db, local_context=4096)
assert "1 time(s)" in result["message"]
def test_large_truncation_count(self):
db = Mock()
db.query.return_value.filter.return_value.filter.return_value.scalar.return_value = 42
result = check_context_truncation_history(db, local_context=4096)
assert "42 time(s)" in result["message"]
def test_warning_dict_has_all_required_keys(self):
db = Mock()
db.query.return_value.filter.return_value.filter.return_value.scalar.return_value = 1
result = check_context_truncation_history(db, local_context=4096)
required = {
"type",
"icon",
"title",
"message",
"dismissKey",
}
assert required.issubset(set(result.keys()))
def test_warning_dict_has_action_link(self):
"""Truncation warnings carry actionUrl/actionLabel pointing to the diagnostic page."""
db = Mock()
db.query.return_value.filter.return_value.filter.return_value.scalar.return_value = 1
result = check_context_truncation_history(db, local_context=4096)
assert result["actionUrl"] == "/metrics/context-overflow"
assert result["actionLabel"]
def test_dismiss_key(self):
db = Mock()
db.query.return_value.filter.return_value.filter.return_value.scalar.return_value = 1
result = check_context_truncation_history(db, local_context=4096)
assert result["dismissKey"] == "app.warnings.dismiss_context_reduced"