Some checks are pending
Test and SonarQube / quality (push) Waiting to run
_docker_get_json/_docker_post called sock.connect(sock_path) a second time after _docker_connection() had already connected the socket -- sock_path was never defined in either function, so every call raised NameError. Surfaced in the UI as every engine card reporting "Not installed" and the Active OCR Engine model dropdown reporting "Not Found" regardless of what was actually running, found while diagnosing a user report of that dropdown. Docker socket API calls (list/status/ start/stop/restart) were affected for every engine card, not just OCR. Added tests/test_docker_client.py, which did not exist before, to catch this class of regression going forward. Also records a request to make the magazine ingestion "watching it work" panel draw detected region boxes live over the page image as each is classified, instead of today's static page-image-plus-text- list (TODO.md; no code change for that yet). 254 tests passing. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
76 lines
2.8 KiB
Python
76 lines
2.8 KiB
Python
"""Regression coverage for the low-level Docker socket client.
|
|
|
|
_docker_get_json/_docker_post previously called sock.connect(sock_path) a
|
|
second time after _docker_connection() had already connected the socket --
|
|
sock_path was never defined in either function, so every container status
|
|
check, start, stop and restart raised NameError. These tests exercise both
|
|
functions against a fake connected socket so that mistake can't ship again
|
|
silently.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
from unittest import mock
|
|
|
|
from core.docker_client import _docker_get_json, _docker_post
|
|
|
|
|
|
class _FakeSocket:
|
|
"""Minimal stand-in for socket.socket, pre-"connected"."""
|
|
|
|
def __init__(self, response: bytes):
|
|
self._chunks = [response]
|
|
self.sent = b""
|
|
|
|
def settimeout(self, _timeout):
|
|
pass
|
|
|
|
def sendall(self, data: bytes):
|
|
self.sent += data
|
|
|
|
def recv(self, _bufsize: int) -> bytes:
|
|
return self._chunks.pop(0) if self._chunks else b""
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, *exc):
|
|
return False
|
|
|
|
|
|
class DockerGetJsonTests(unittest.TestCase):
|
|
def test_returns_parsed_json_body(self):
|
|
response = b'HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n\r\n{"ok": true}'
|
|
fake = _FakeSocket(response)
|
|
with mock.patch("core.docker_client._docker_available", return_value=True), \
|
|
mock.patch("core.docker_client._docker_connection", return_value=fake):
|
|
code, data = _docker_get_json("/containers/foo/json")
|
|
self.assertEqual(code, 200)
|
|
self.assertEqual(data, {"ok": True})
|
|
|
|
def test_does_not_reconnect_the_socket(self):
|
|
# A regression guard for the exact bug: a second sock.connect(sock_path)
|
|
# call would raise NameError before ever reaching sendall().
|
|
response = b"HTTP/1.1 404 Not Found\r\n\r\n"
|
|
fake = _FakeSocket(response)
|
|
with mock.patch("core.docker_client._docker_available", return_value=True), \
|
|
mock.patch("core.docker_client._docker_connection", return_value=fake):
|
|
code, _ = _docker_get_json("/containers/missing/json")
|
|
self.assertEqual(code, 404)
|
|
self.assertIn(b"GET /containers/missing/json", fake.sent)
|
|
|
|
|
|
class DockerPostTests(unittest.TestCase):
|
|
def test_returns_status_code(self):
|
|
response = b"HTTP/1.1 204 No Content\r\n\r\n"
|
|
fake = _FakeSocket(response)
|
|
with mock.patch("core.docker_client._docker_available", return_value=True), \
|
|
mock.patch("core.docker_client._docker_connection", return_value=fake):
|
|
code, _raw = _docker_post("/containers/foo/start")
|
|
self.assertEqual(code, 204)
|
|
self.assertIn(b"POST /containers/foo/start", fake.sent)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|