chore: import upstream snapshot with attribution
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
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
This commit is contained in:
@@ -0,0 +1,263 @@
|
||||
"""Extra coverage tests for socket_service.py — private event handlers and logging."""
|
||||
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
|
||||
MODULE = "local_deep_research.web.services.socket_service"
|
||||
|
||||
|
||||
def _get_service_with_patched_init():
|
||||
"""Get a SocketIOService instance bypassing singleton + Flask requirement."""
|
||||
from local_deep_research.web.services.socket_service import SocketIOService
|
||||
|
||||
with patch.object(
|
||||
SocketIOService, "__new__", lambda cls, *a, **kw: object.__new__(cls)
|
||||
):
|
||||
svc = object.__new__(SocketIOService)
|
||||
|
||||
svc._SocketIOService__socket_subscriptions = {}
|
||||
svc._SocketIOService__lock = __import__("threading").Lock()
|
||||
svc._SocketIOService__socketio = MagicMock()
|
||||
svc._SocketIOService__logging_enabled = True
|
||||
return svc
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# __handle_connect
|
||||
# ===========================================================================
|
||||
|
||||
|
||||
class TestHandleConnect:
|
||||
def test_rejects_unauthenticated(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
mock_request = MagicMock()
|
||||
mock_request.sid = "client-123"
|
||||
|
||||
with patch(f"{MODULE}.session", {}):
|
||||
assert svc._SocketIOService__handle_connect(mock_request) is False
|
||||
|
||||
def test_rejects_when_no_db_session(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
mock_request = MagicMock()
|
||||
mock_request.sid = "client-123"
|
||||
|
||||
with (
|
||||
patch(f"{MODULE}.session", {"username": "alice"}),
|
||||
patch(f"{MODULE}.db_manager") as mock_db,
|
||||
):
|
||||
mock_db.is_user_connected.return_value = False
|
||||
assert svc._SocketIOService__handle_connect(mock_request) is False
|
||||
|
||||
def test_accepts_authenticated(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
mock_request = MagicMock()
|
||||
mock_request.sid = "client-123"
|
||||
|
||||
with (
|
||||
patch(f"{MODULE}.session", {"username": "alice"}),
|
||||
patch(f"{MODULE}.db_manager") as mock_db,
|
||||
patch(f"{MODULE}.join_room"),
|
||||
):
|
||||
mock_db.is_user_connected.return_value = True
|
||||
assert svc._SocketIOService__handle_connect(mock_request) is True
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# __handle_disconnect
|
||||
# ===========================================================================
|
||||
|
||||
|
||||
class TestHandleDisconnect:
|
||||
def test_removes_subscription(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
svc._SocketIOService__socket_subscriptions = {
|
||||
"res-1": {"client-1", "client-2"},
|
||||
"res-2": {"client-1"},
|
||||
}
|
||||
|
||||
mock_request = MagicMock()
|
||||
mock_request.sid = "client-1"
|
||||
|
||||
with patch(f"{MODULE}.cleanup_current_thread", create=True):
|
||||
svc._SocketIOService__handle_disconnect(
|
||||
mock_request, "transport close"
|
||||
)
|
||||
|
||||
assert "client-1" not in svc._SocketIOService__socket_subscriptions.get(
|
||||
"res-1", set()
|
||||
)
|
||||
# res-2 should be cleaned up entirely since no clients left
|
||||
assert "res-2" not in svc._SocketIOService__socket_subscriptions
|
||||
|
||||
def test_empty_subscriptions_handled(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
mock_request = MagicMock()
|
||||
mock_request.sid = "unknown-client"
|
||||
|
||||
with patch(f"{MODULE}.cleanup_current_thread", create=True):
|
||||
svc._SocketIOService__handle_disconnect(mock_request, "timeout")
|
||||
|
||||
def test_cleanup_import_error_swallowed(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
mock_request = MagicMock()
|
||||
mock_request.sid = "client-1"
|
||||
|
||||
with patch.dict(
|
||||
"sys.modules",
|
||||
{"local_deep_research.database.thread_local_session": None},
|
||||
):
|
||||
svc._SocketIOService__handle_disconnect(mock_request, "close")
|
||||
|
||||
def test_cleanup_exception_swallowed(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
mock_request = MagicMock()
|
||||
mock_request.sid = "client-1"
|
||||
|
||||
mock_mod = MagicMock()
|
||||
mock_mod.cleanup_current_thread.side_effect = RuntimeError(
|
||||
"cleanup fail"
|
||||
)
|
||||
|
||||
with patch.dict(
|
||||
"sys.modules",
|
||||
{"local_deep_research.database.thread_local_session": mock_mod},
|
||||
):
|
||||
svc._SocketIOService__handle_disconnect(mock_request, "close")
|
||||
|
||||
def test_outer_exception_swallowed(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
# Make the lock acquisition fail
|
||||
svc._SocketIOService__lock = MagicMock()
|
||||
svc._SocketIOService__lock.__enter__ = MagicMock(
|
||||
side_effect=RuntimeError("lock fail")
|
||||
)
|
||||
svc._SocketIOService__lock.__exit__ = MagicMock(return_value=False)
|
||||
|
||||
mock_request = MagicMock()
|
||||
mock_request.sid = "client-1"
|
||||
|
||||
# Should not raise
|
||||
svc._SocketIOService__handle_disconnect(mock_request, "error")
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# __handle_subscribe
|
||||
# ===========================================================================
|
||||
|
||||
|
||||
class TestHandleSubscribe:
|
||||
def test_adds_subscriber(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
mock_request = MagicMock()
|
||||
mock_request.sid = "client-1"
|
||||
|
||||
with patch(f"{MODULE}.get_active_research_snapshot", return_value=None):
|
||||
svc._SocketIOService__handle_subscribe(
|
||||
{"research_id": "res-1"}, mock_request
|
||||
)
|
||||
|
||||
assert "client-1" in svc._SocketIOService__socket_subscriptions["res-1"]
|
||||
|
||||
def test_sends_current_status_when_available(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
mock_request = MagicMock()
|
||||
mock_request.sid = "client-1"
|
||||
|
||||
snapshot = {
|
||||
"progress": 50,
|
||||
"log": [{"message": "Searching...", "phase": "search"}],
|
||||
}
|
||||
|
||||
with patch(
|
||||
f"{MODULE}.get_active_research_snapshot", return_value=snapshot
|
||||
):
|
||||
svc._SocketIOService__handle_subscribe(
|
||||
{"research_id": "res-1"}, mock_request
|
||||
)
|
||||
|
||||
svc._SocketIOService__socketio.emit.assert_called()
|
||||
|
||||
def test_no_status_when_snapshot_none(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
mock_request = MagicMock()
|
||||
mock_request.sid = "client-1"
|
||||
|
||||
with patch(f"{MODULE}.get_active_research_snapshot", return_value=None):
|
||||
svc._SocketIOService__handle_subscribe(
|
||||
{"research_id": "res-1"}, mock_request
|
||||
)
|
||||
|
||||
svc._SocketIOService__socketio.emit.assert_not_called()
|
||||
|
||||
def test_empty_log_no_emit(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
mock_request = MagicMock()
|
||||
mock_request.sid = "client-1"
|
||||
|
||||
snapshot = {"progress": 0, "log": []}
|
||||
|
||||
with patch(
|
||||
f"{MODULE}.get_active_research_snapshot", return_value=snapshot
|
||||
):
|
||||
svc._SocketIOService__handle_subscribe(
|
||||
{"research_id": "res-1"}, mock_request
|
||||
)
|
||||
|
||||
svc._SocketIOService__socketio.emit.assert_not_called()
|
||||
|
||||
def test_no_research_id_ignored(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
mock_request = MagicMock()
|
||||
|
||||
svc._SocketIOService__handle_subscribe({}, mock_request)
|
||||
|
||||
assert len(svc._SocketIOService__socket_subscriptions) == 0
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# __handle_socket_error / __handle_default_error
|
||||
# ===========================================================================
|
||||
|
||||
|
||||
class TestErrorHandlers:
|
||||
def test_socket_error_returns_false(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
result = svc._SocketIOService__handle_socket_error(RuntimeError("test"))
|
||||
assert result is False
|
||||
|
||||
def test_default_error_returns_false(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
result = svc._SocketIOService__handle_default_error(ValueError("test"))
|
||||
assert result is False
|
||||
|
||||
|
||||
# ===========================================================================
|
||||
# __log_info / __log_error / __log_exception — logging control
|
||||
# ===========================================================================
|
||||
|
||||
|
||||
class TestLoggingControl:
|
||||
def test_log_info_when_enabled(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
svc._SocketIOService__logging_enabled = True
|
||||
svc._SocketIOService__log_info("test message")
|
||||
|
||||
def test_log_info_when_disabled(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
svc._SocketIOService__logging_enabled = False
|
||||
svc._SocketIOService__log_info("should not log")
|
||||
|
||||
def test_log_error_when_enabled(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
svc._SocketIOService__logging_enabled = True
|
||||
svc._SocketIOService__log_error("error message")
|
||||
|
||||
def test_log_exception_when_enabled(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
svc._SocketIOService__logging_enabled = True
|
||||
svc._SocketIOService__log_exception("exception message")
|
||||
|
||||
def test_log_exception_when_disabled(self):
|
||||
svc = _get_service_with_patched_init()
|
||||
svc._SocketIOService__logging_enabled = False
|
||||
svc._SocketIOService__log_exception("should not log")
|
||||
Reference in New Issue
Block a user