diff --git a/tests/unit/test_gpu_encoder.py b/tests/unit/test_gpu_encoder.py index 1c72190e1..f3895a009 100644 --- a/tests/unit/test_gpu_encoder.py +++ b/tests/unit/test_gpu_encoder.py @@ -3,7 +3,6 @@ from __future__ import annotations import json -import os import tempfile import urllib.error import urllib.request @@ -34,6 +33,7 @@ def client(): return GpuEncoderClient( endpoint="http://gpu.example.com:8900", relay_base_url="http://api.example.com", + relay_internal_base_url="http://api-internal:8000", sync_timeout=60, health_timeout=2, relay_secret="test-secret", @@ -41,15 +41,20 @@ def client(): def _fake_response(status: int = 200, body: dict | bytes | None = None, headers=None): + """Fake HTTPResponse that supports chunked read(size) used by _download_to_file.""" if isinstance(body, dict): data = json.dumps(body).encode("utf-8") elif body is None: data = b"" else: data = body + + # Use a real BytesIO so read(size) works for chunked downloads + bio = BytesIO(data) + resp = mock.MagicMock(spec=HTTPResponse) resp.status = status - resp.read.return_value = data + resp.read.side_effect = lambda n=-1: bio.read(n) resp.__enter__ = mock.MagicMock(return_value=resp) resp.__exit__ = mock.MagicMock(return_value=False) return resp @@ -111,36 +116,65 @@ class TestPostSync: ) assert res["status"] == "completed" assert res["ffmpeg_rc"] == 0 - # verify request sent to sync endpoint req = m.call_args[0][0] assert req.full_url == "http://gpu.example.com:8900/api/render/sync" def test_ffmpeg_failure_raises(self, client): body = {"status": "failed", "ffmpeg_rc": 1, "message": "Invalid data found"} with mock.patch("urllib.request.urlopen", return_value=_fake_response(body=body)): - with pytest.raises(GpuEncodeError, match="ffmpeg_rc=1"): + with pytest.raises(GpuEncodeError, match="rc=1"): client._post_sync( - {"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10}, mezzanine_path=Path("/tmp/x") + {"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10}, + mezzanine_path=Path("/tmp/x"), ) def test_http_4xx_raises(self, client): err = urllib.error.HTTPError( - url="http://gpu/render/sync", code=422, msg="Unprocessable", hdrs={}, fp=BytesIO(b"bad request") + url="http://gpu/render/sync", + code=422, + msg="Unprocessable", + hdrs={}, + fp=BytesIO(b"bad request"), ) with mock.patch("urllib.request.urlopen", side_effect=err): with pytest.raises(GpuEncodeError, match="HTTP 422"): client._post_sync( - {"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10}, mezzanine_path=Path("/tmp/x") + {"inputs": {}, "ffmpeg_args": [], "output_url": "", "timeout": 10}, + mezzanine_path=Path("/tmp/x"), ) class TestRelayUrl: - def test_url_contains_token_and_key(self, client): - url = client._relay_url("abc123", "secret!") + def test_put_url_uses_external_base(self, client): + url = client._relay_put_url("abc123", "secret!") assert "abc123" in url assert "token=secret%21" in url # urlencoded assert url.startswith("http://api.example.com/api/v1/internal/gpu-relay/") + def test_internal_url_uses_internal_base(self, client): + url = client._relay_internal_url("abc123", "s") + assert url.startswith("http://api-internal:8000/api/v1/internal/gpu-relay/abc123") + + def test_internal_url_falls_back_to_external_when_not_set(self): + c = GpuEncoderClient( + endpoint="http://gpu", + relay_base_url="http://api.example.com", + relay_secret="s", + ) + put = c._relay_put_url("k", "s") + internal = c._relay_internal_url("k", "s") + assert put.startswith("http://api.example.com/") + # When internal not set, internal_url falls back to external base + assert internal == put + + def test_encode_uses_different_put_and_get_urls(self, client): + """encode_mezzanine_to_output should use external URL for PUT and internal for GET/DELETE.""" + put_url = client._relay_put_url("k", "test-secret") + get_url = client._relay_internal_url("k", "test-secret") + assert "api.example.com" in put_url + assert "api-internal:8000" in get_url + assert put_url != get_url + class TestGetRelaySecret: def test_explicit_secret_used(self, client): @@ -175,9 +209,3 @@ class TestDownloadToFile: with pytest.raises(GpuEncodeError, match="empty file"): client._download_to_file("http://relay/k", out) assert not out.exists() - - -class TestFfmpegOutputToMezzanineIntegration: - """_ffmpeg_output_to_mezzanine is on UnifiedRenderService; unit-tested there via mocks.""" - - pass