|
5 | 5 | errors instead of crashing or leaking data. |
6 | 6 | """ |
7 | 7 |
|
| 8 | +import asyncio |
8 | 9 | import json |
9 | 10 | import os |
10 | 11 | from collections.abc import AsyncGenerator |
11 | 12 |
|
12 | 13 | import pytest |
13 | 14 | from mcp.server.fastmcp.exceptions import ToolError |
| 15 | +from urllib3.exceptions import MaxRetryError |
14 | 16 |
|
15 | 17 | from nc_mcp_server.client import NextcloudClient, NextcloudError |
16 | 18 | from nc_mcp_server.config import Config |
|
28 | 30 |
|
29 | 31 | @pytest.fixture(scope="module") |
30 | 32 | async def _ensure_test_user() -> None: |
31 | | - """Create the test user via admin client if it doesn't exist.""" |
| 33 | + """Create the test user via admin client if it doesn't exist. |
| 34 | +
|
| 35 | + Retries the entire operation to handle slow container startup in CI. |
| 36 | + The niquests/urllib3 stack may raise TimeoutError, OSError, or |
| 37 | + urllib3.exceptions.MaxRetryError (which is none of the above), |
| 38 | + so we catch Exception and retry on any connection-level failure. |
| 39 | + """ |
32 | 40 | admin_config = Config( |
33 | 41 | nextcloud_url=os.environ.get("NEXTCLOUD_URL", "http://nextcloud.ncmcp"), |
34 | 42 | user=os.environ.get("NEXTCLOUD_USER", "admin"), |
35 | 43 | password=os.environ.get("NEXTCLOUD_PASSWORD", "admin"), |
36 | 44 | ) |
37 | 45 | client = NextcloudClient(admin_config) |
38 | | - try: |
39 | | - await client.ocs_get(f"cloud/users/{TEST_USER}") |
40 | | - except NextcloudError: |
41 | | - await client.ocs_post("cloud/users", data={"userid": TEST_USER, "password": TEST_PASS}) |
| 46 | + last_err: Exception | None = None |
| 47 | + for attempt in range(10): |
| 48 | + try: |
| 49 | + await client.ocs_get(f"cloud/users/{TEST_USER}") |
| 50 | + break |
| 51 | + except NextcloudError: |
| 52 | + await client.ocs_post("cloud/users", data={"userid": TEST_USER, "password": TEST_PASS}) |
| 53 | + break |
| 54 | + except (OSError, TimeoutError, MaxRetryError) as exc: |
| 55 | + last_err = exc |
| 56 | + if attempt < 9: |
| 57 | + await asyncio.sleep(3) |
| 58 | + continue |
| 59 | + raise last_err from exc |
42 | 60 | await client.close() |
43 | 61 |
|
44 | 62 |
|
|
0 commit comments