Files
wehub-resource-sync e904b667c6
Build/Publish Develop Docs / deploy (push) Failing after 1s
PaddleOCR Code Style Check / check-code-style (push) Failing after 1s
PaddleOCR PR Tests GPU / detect-changes (push) Failing after 1s
PaddleOCR PR Tests / detect-changes (push) Failing after 1s
PaddleOCR PR Tests GPU / test-pr-gpu (push) Has been cancelled
PaddleOCR PR Tests / test-pr (push) Has been cancelled
PaddleOCR PR Tests GPU / test-pr-gpu-impl (push) Has been cancelled
PaddleOCR PR Tests / test-pr-python (3.13) (push) Has been cancelled
PaddleOCR PR Tests / test-pr-python (3.8) (push) Has been cancelled
PaddleOCR PR Tests / test-pr-python (3.9) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 11:59:26 +08:00

265 lines
8.4 KiB
Python

# Copyright (c) 2026 PaddlePaddle Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import json
from http.server import BaseHTTPRequestHandler, HTTPServer
from threading import Thread
import pytest
from paddleocr._api_client._http import API_PATH, DEFAULT_BASE_URL, HTTPClient
from paddleocr._api_client.client import PaddleOCRClient
from paddleocr._api_client.errors import (
APIError,
AuthError,
InvalidRequestError,
NetworkError,
RateLimitError,
RequestTimeoutError,
ServiceUnavailableError,
)
from paddleocr._api_client.models import Model
class _MockHandler(BaseHTTPRequestHandler):
"""Records requests and responds with canned JSON."""
requests_log = []
response_body = {"code": 0, "data": {"jobId": "job-1"}}
response_status = 200
def do_POST(self):
content_length = int(self.headers.get("Content-Length", 0))
body = self.rfile.read(content_length) if content_length else b""
self.__class__.requests_log.append(
{
"method": "POST",
"path": self.path,
"headers": dict(self.headers),
"body": body,
}
)
self.send_response(self.__class__.response_status)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps(self.__class__.response_body).encode())
def do_GET(self):
self.__class__.requests_log.append(
{"method": "GET", "path": self.path, "headers": dict(self.headers)}
)
self.send_response(self.__class__.response_status)
self.send_header("Content-Type", "application/json")
self.end_headers()
self.wfile.write(json.dumps(self.__class__.response_body).encode())
def log_message(self, format, *args):
pass
@pytest.fixture()
def mock_server():
_MockHandler.requests_log = []
_MockHandler.response_body = {"code": 0, "data": {"jobId": "job-1"}}
_MockHandler.response_status = 200
server = HTTPServer(("127.0.0.1", 0), _MockHandler)
port = server.server_address[1]
thread = Thread(target=server.serve_forever, daemon=True)
thread.start()
yield f"http://127.0.0.1:{port}", _MockHandler
server.shutdown()
def test_base_url_appends_api_path(mock_server):
base_url, handler = mock_server
client = HTTPClient("test-token", base_url, timeout=5.0)
client.submit_url("PP-OCRv5", "https://example.test/file.pdf", {})
assert len(handler.requests_log) == 1
assert handler.requests_log[0]["path"] == API_PATH
def test_get_job_status_url(mock_server):
base_url, handler = mock_server
handler.response_body = {"code": 0, "data": {"jobId": "job-1", "state": "running"}}
client = HTTPClient("test-token", base_url, timeout=5.0)
client.get_job_status("job-123")
assert handler.requests_log[0]["path"] == f"{API_PATH}/job-123"
def test_get_batch_status_url(mock_server):
base_url, handler = mock_server
handler.response_body = {"code": 0, "data": {"batchId": "b-1", "jobs": []}}
client = HTTPClient("test-token", base_url, timeout=5.0)
client.get_batch_status("batch-abc")
assert handler.requests_log[0]["path"] == f"{API_PATH}/batch/batch-abc"
def test_base_url_trailing_slashes_normalized(mock_server):
base_url, handler = mock_server
client = HTTPClient("test-token", base_url + "///", timeout=5.0)
client.submit_url("PP-OCRv5", "https://example.test/file.pdf", {})
assert handler.requests_log[0]["path"] == API_PATH
def test_authorization_header_sent(mock_server):
base_url, handler = mock_server
client = HTTPClient("my-secret-token", base_url, timeout=5.0)
client.submit_url("PP-OCRv5", "https://example.test/file.pdf", {})
assert (
handler.requests_log[0]["headers"]["Authorization"] == "Bearer my-secret-token"
)
def test_client_platform_header(mock_server):
base_url, handler = mock_server
client = HTTPClient("token", base_url, timeout=5.0, client_platform="sdk-test")
client.submit_url("PP-OCRv5", "https://example.test/file.pdf", {})
assert handler.requests_log[0]["headers"]["Client-Platform"] == "sdk-test"
def test_submit_url_posts_correct_body(mock_server):
base_url, handler = mock_server
client = HTTPClient("token", base_url, timeout=5.0)
job_id = client.submit_url(
"PP-OCRv5",
"https://example.test/file.pdf",
{"visualize": True},
page_ranges="1-3",
batch_id="batch-1",
)
assert job_id == "job-1"
body = json.loads(handler.requests_log[0]["body"])
assert body == {
"fileUrl": "https://example.test/file.pdf",
"model": "PP-OCRv5",
"optionalPayload": {"visualize": True},
"pageRanges": "1-3",
"batchId": "batch-1",
}
def test_error_mapping_401(mock_server):
base_url, handler = mock_server
handler.response_status = 401
handler.response_body = {"code": 401, "msg": "Unauthorized"}
client = HTTPClient("bad-token", base_url, timeout=5.0)
with pytest.raises(AuthError):
client.submit_url("PP-OCRv5", "https://example.test/file.pdf", {})
def test_error_mapping_400(mock_server):
base_url, handler = mock_server
handler.response_status = 400
handler.response_body = {"code": 400, "msg": "Bad request"}
client = HTTPClient("token", base_url, timeout=5.0)
with pytest.raises(InvalidRequestError):
client.submit_url("PP-OCRv5", "https://example.test/file.pdf", {})
def test_error_mapping_429(mock_server):
base_url, handler = mock_server
handler.response_status = 429
handler.response_body = {"code": 429, "msg": "Rate limited"}
client = HTTPClient("token", base_url, timeout=5.0)
with pytest.raises(RateLimitError):
client.submit_url("PP-OCRv5", "https://example.test/file.pdf", {})
def test_error_mapping_503(mock_server):
base_url, handler = mock_server
handler.response_status = 503
handler.response_body = {"code": 503, "msg": "Service unavailable"}
client = HTTPClient("token", base_url, timeout=5.0)
with pytest.raises(ServiceUnavailableError):
client.submit_url("PP-OCRv5", "https://example.test/file.pdf", {})
def test_error_mapping_500(mock_server):
base_url, handler = mock_server
handler.response_status = 500
handler.response_body = {"code": 500, "msg": "Internal error"}
client = HTTPClient("token", base_url, timeout=5.0)
with pytest.raises(APIError):
client.submit_url("PP-OCRv5", "https://example.test/file.pdf", {})
def test_paddleocr_client_requires_token(monkeypatch):
monkeypatch.delenv("PADDLEOCR_ACCESS_TOKEN", raising=False)
with pytest.raises(AuthError):
PaddleOCRClient()
def test_paddleocr_client_reads_env_token(monkeypatch):
monkeypatch.setenv("PADDLEOCR_ACCESS_TOKEN", "env-token")
client = PaddleOCRClient()
client.close()
def test_default_base_url_is_host_only():
assert "/api/" not in DEFAULT_BASE_URL
assert DEFAULT_BASE_URL == "https://paddleocr.aistudio-app.com"
def test_paddleocr_client_client_platform_header(monkeypatch):
class FakeResponse:
status_code = 200
text = ""
def json(self):
return {"code": 0, "data": {"jobId": "job-1"}}
class FakeSession:
def __init__(self):
self.headers = {}
self.captured_headers = None
def post(self, *args, **kwargs):
self.captured_headers = dict(self.headers)
return FakeResponse()
def close(self):
return None
fake_session = FakeSession()
monkeypatch.setattr(
"paddleocr._api_client._http.requests.Session",
lambda: fake_session,
)
client = PaddleOCRClient(token="token", client_platform="my-app")
job = client.submit_ocr(file_url="https://example.test/input.pdf")
assert job.job_id == "job-1"
assert fake_session.captured_headers["Client-Platform"] == "my-app"