test: Replace pytest-asyncio plugin fixtures with anyio fixtures
This commit is contained in:
+1
-4
@@ -18,9 +18,7 @@ dependencies = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
[tool.pytest.ini_options]
|
[tool.pytest.ini_options]
|
||||||
asyncio_mode = "auto"
|
anyio_mode = "auto"
|
||||||
asyncio_default_test_loop_scope = "session"
|
|
||||||
asyncio_default_fixture_loop_scope = "session"
|
|
||||||
log_cli = 1
|
log_cli = 1
|
||||||
log_cli_level = "WARN"
|
log_cli_level = "WARN"
|
||||||
log_level = "WARN"
|
log_level = "WARN"
|
||||||
@@ -53,7 +51,6 @@ dev = [
|
|||||||
"ipython>=9.2.0",
|
"ipython>=9.2.0",
|
||||||
"playwright>=1.49.1",
|
"playwright>=1.49.1",
|
||||||
"pytest>=8.3.5",
|
"pytest>=8.3.5",
|
||||||
"pytest-asyncio>=1.0.0",
|
|
||||||
"pytest-cov>=6.1.1",
|
"pytest-cov>=6.1.1",
|
||||||
"pytest-playwright-asyncio>=0.7.1",
|
"pytest-playwright-asyncio>=0.7.1",
|
||||||
"ruff>=0.11.13",
|
"ruff>=0.11.13",
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ logger = logging.getLogger(__name__)
|
|||||||
pytestmark = pytest.mark.integration
|
pytestmark = pytest.mark.integration
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_create_and_delete_share(nc_client):
|
async def test_create_and_delete_share(nc_client):
|
||||||
"""Test creating and deleting a file share."""
|
"""Test creating and deleting a file share."""
|
||||||
# Create a test user to share with
|
# Create a test user to share with
|
||||||
@@ -68,7 +68,7 @@ async def test_create_and_delete_share(nc_client):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_update_share_permissions(nc_client):
|
async def test_update_share_permissions(nc_client):
|
||||||
"""Test updating share permissions."""
|
"""Test updating share permissions."""
|
||||||
# Create a test user to share with
|
# Create a test user to share with
|
||||||
@@ -120,7 +120,7 @@ async def test_update_share_permissions(nc_client):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_list_shares(nc_client):
|
async def test_list_shares(nc_client):
|
||||||
"""Test listing all shares."""
|
"""Test listing all shares."""
|
||||||
# Create a test user to share with
|
# Create a test user to share with
|
||||||
|
|||||||
+64
-94
@@ -15,6 +15,12 @@ from nextcloud_mcp_server.client import NextcloudClient
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture(scope="session")
|
||||||
|
def anyio_backend():
|
||||||
|
"""Configure anyio to use asyncio backend for all tests."""
|
||||||
|
return "asyncio"
|
||||||
|
|
||||||
|
|
||||||
async def wait_for_nextcloud(
|
async def wait_for_nextcloud(
|
||||||
host: str, max_attempts: int = 30, delay: float = 2.0
|
host: str, max_attempts: int = 30, delay: float = 2.0
|
||||||
) -> bool:
|
) -> bool:
|
||||||
@@ -111,7 +117,7 @@ async def create_mcp_client_session(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def nc_client() -> AsyncGenerator[NextcloudClient, Any]:
|
async def nc_client(anyio_backend) -> AsyncGenerator[NextcloudClient, Any]:
|
||||||
"""
|
"""
|
||||||
Fixture to create a NextcloudClient instance for integration tests.
|
Fixture to create a NextcloudClient instance for integration tests.
|
||||||
Uses environment variables for configuration.
|
Uses environment variables for configuration.
|
||||||
@@ -146,35 +152,21 @@ async def nc_client() -> AsyncGenerator[NextcloudClient, Any]:
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def nc_mcp_client() -> AsyncGenerator[ClientSession, Any]:
|
async def nc_mcp_client(anyio_backend) -> AsyncGenerator[ClientSession, Any]:
|
||||||
"""
|
"""
|
||||||
Fixture to create an MCP client session for integration tests using streamable-http.
|
Fixture to create an MCP client session for integration tests using streamable-http.
|
||||||
|
|
||||||
Note: This fixture uses a workaround for pytest-asyncio + anyio incompatibility.
|
Uses anyio pytest plugin for proper async fixture handling.
|
||||||
pytest-asyncio runs fixture teardown in a new asyncio task, which violates anyio's
|
|
||||||
requirement that cancel scopes must be entered/exited in the same task. We catch
|
|
||||||
and ignore these expected teardown errors while allowing real errors to propagate.
|
|
||||||
|
|
||||||
See: https://github.com/modelcontextprotocol/python-sdk/issues/577
|
|
||||||
"""
|
"""
|
||||||
try:
|
async for session in create_mcp_client_session(
|
||||||
async for session in create_mcp_client_session(
|
url="http://127.0.0.1:8000/mcp", client_name="Basic MCP"
|
||||||
url="http://127.0.0.1:8000/mcp", client_name="Basic MCP"
|
):
|
||||||
):
|
yield session
|
||||||
yield session
|
|
||||||
except RuntimeError as e:
|
|
||||||
# Expected error during pytest-asyncio fixture teardown
|
|
||||||
# pytest-asyncio creates a new task for teardown, causing:
|
|
||||||
# "Attempted to exit cancel scope in a different task than it was entered in"
|
|
||||||
if "cancel scope" in str(e) and "different task" in str(e):
|
|
||||||
logger.debug(f"Ignoring expected pytest-asyncio teardown issue: {e}")
|
|
||||||
else:
|
|
||||||
# Unexpected RuntimeError - re-raise
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def nc_mcp_oauth_client(
|
async def nc_mcp_oauth_client(
|
||||||
|
anyio_backend,
|
||||||
playwright_oauth_token: str,
|
playwright_oauth_token: str,
|
||||||
) -> AsyncGenerator[ClientSession, Any]:
|
) -> AsyncGenerator[ClientSession, Any]:
|
||||||
"""
|
"""
|
||||||
@@ -182,22 +174,14 @@ async def nc_mcp_oauth_client(
|
|||||||
Connects to the OAuth-enabled MCP server on port 8001 with OAuth authentication.
|
Connects to the OAuth-enabled MCP server on port 8001 with OAuth authentication.
|
||||||
|
|
||||||
Uses headless browser automation suitable for CI/CD.
|
Uses headless browser automation suitable for CI/CD.
|
||||||
|
Uses anyio pytest plugin for proper async fixture handling.
|
||||||
Note: Includes workaround for pytest-asyncio + anyio incompatibility.
|
|
||||||
See nc_mcp_client fixture for details.
|
|
||||||
"""
|
"""
|
||||||
try:
|
async for session in create_mcp_client_session(
|
||||||
async for session in create_mcp_client_session(
|
url="http://127.0.0.1:8001/mcp",
|
||||||
url="http://127.0.0.1:8001/mcp",
|
token=playwright_oauth_token,
|
||||||
token=playwright_oauth_token,
|
client_name="OAuth MCP (Playwright)",
|
||||||
client_name="OAuth MCP (Playwright)",
|
):
|
||||||
):
|
yield session
|
||||||
yield session
|
|
||||||
except RuntimeError as e:
|
|
||||||
if "cancel scope" in str(e) and "different task" in str(e):
|
|
||||||
logger.debug(f"Ignoring expected pytest-asyncio teardown issue: {e}")
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
@@ -519,6 +503,7 @@ async def temporary_board_with_card(
|
|||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def nc_oauth_client(
|
async def nc_oauth_client(
|
||||||
|
anyio_backend,
|
||||||
playwright_oauth_token: str,
|
playwright_oauth_token: str,
|
||||||
) -> AsyncGenerator[NextcloudClient, Any]:
|
) -> AsyncGenerator[NextcloudClient, Any]:
|
||||||
"""
|
"""
|
||||||
@@ -641,7 +626,7 @@ def oauth_callback_server():
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def shared_oauth_client_credentials(oauth_callback_server):
|
async def shared_oauth_client_credentials(anyio_backend, oauth_callback_server):
|
||||||
"""
|
"""
|
||||||
Fixture to obtain shared OAuth client credentials that will be reused for all users.
|
Fixture to obtain shared OAuth client credentials that will be reused for all users.
|
||||||
|
|
||||||
@@ -702,7 +687,7 @@ async def shared_oauth_client_credentials(oauth_callback_server):
|
|||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def playwright_oauth_token(
|
async def playwright_oauth_token(
|
||||||
browser, shared_oauth_client_credentials, oauth_callback_server
|
anyio_backend, browser, shared_oauth_client_credentials, oauth_callback_server
|
||||||
) -> str:
|
) -> str:
|
||||||
"""
|
"""
|
||||||
Fixture to obtain an OAuth access token using Playwright headless browser automation.
|
Fixture to obtain an OAuth access token using Playwright headless browser automation.
|
||||||
@@ -865,7 +850,7 @@ async def playwright_oauth_token(
|
|||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def test_users_setup(nc_client: NextcloudClient):
|
async def test_users_setup(anyio_backend, nc_client: NextcloudClient):
|
||||||
"""
|
"""
|
||||||
Create test users for multi-user OAuth testing.
|
Create test users for multi-user OAuth testing.
|
||||||
|
|
||||||
@@ -1112,7 +1097,11 @@ async def _get_oauth_token_for_user(
|
|||||||
# Parallel token retrieval fixture - fetches all OAuth tokens concurrently
|
# Parallel token retrieval fixture - fetches all OAuth tokens concurrently
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def all_oauth_tokens(
|
async def all_oauth_tokens(
|
||||||
browser, shared_oauth_client_credentials, test_users_setup, oauth_callback_server
|
anyio_backend,
|
||||||
|
browser,
|
||||||
|
shared_oauth_client_credentials,
|
||||||
|
test_users_setup,
|
||||||
|
oauth_callback_server,
|
||||||
) -> dict[str, str]:
|
) -> dict[str, str]:
|
||||||
"""
|
"""
|
||||||
Fetch OAuth tokens for all test users in parallel for speed.
|
Fetch OAuth tokens for all test users in parallel for speed.
|
||||||
@@ -1172,101 +1161,82 @@ async def all_oauth_tokens(
|
|||||||
|
|
||||||
# Session-scoped OAuth token fixtures - now use the parallel fixture
|
# Session-scoped OAuth token fixtures - now use the parallel fixture
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def alice_oauth_token(all_oauth_tokens) -> str:
|
async def alice_oauth_token(anyio_backend, all_oauth_tokens) -> str:
|
||||||
"""OAuth token for alice (cached for session). Uses shared OAuth client."""
|
"""OAuth token for alice (cached for session). Uses shared OAuth client."""
|
||||||
return all_oauth_tokens["alice"]
|
return all_oauth_tokens["alice"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def bob_oauth_token(all_oauth_tokens) -> str:
|
async def bob_oauth_token(anyio_backend, all_oauth_tokens) -> str:
|
||||||
"""OAuth token for bob (cached for session). Uses shared OAuth client."""
|
"""OAuth token for bob (cached for session). Uses shared OAuth client."""
|
||||||
return all_oauth_tokens["bob"]
|
return all_oauth_tokens["bob"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def charlie_oauth_token(all_oauth_tokens) -> str:
|
async def charlie_oauth_token(anyio_backend, all_oauth_tokens) -> str:
|
||||||
"""OAuth token for charlie (cached for session). Uses shared OAuth client."""
|
"""OAuth token for charlie (cached for session). Uses shared OAuth client."""
|
||||||
return all_oauth_tokens["charlie"]
|
return all_oauth_tokens["charlie"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def diana_oauth_token(all_oauth_tokens) -> str:
|
async def diana_oauth_token(anyio_backend, all_oauth_tokens) -> str:
|
||||||
"""OAuth token for diana (cached for session). Uses shared OAuth client."""
|
"""OAuth token for diana (cached for session). Uses shared OAuth client."""
|
||||||
return all_oauth_tokens["diana"]
|
return all_oauth_tokens["diana"]
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def alice_mcp_client(
|
async def alice_mcp_client(
|
||||||
|
anyio_backend,
|
||||||
alice_oauth_token: str,
|
alice_oauth_token: str,
|
||||||
) -> AsyncGenerator[ClientSession, Any]:
|
) -> AsyncGenerator[ClientSession, Any]:
|
||||||
"""MCP client authenticated as alice (owner role)."""
|
"""MCP client authenticated as alice (owner role)."""
|
||||||
try:
|
async for session in create_mcp_client_session(
|
||||||
async for session in create_mcp_client_session(
|
url="http://127.0.0.1:8001/mcp",
|
||||||
url="http://127.0.0.1:8001/mcp",
|
token=alice_oauth_token,
|
||||||
token=alice_oauth_token,
|
client_name="Alice MCP",
|
||||||
client_name="Alice MCP",
|
):
|
||||||
):
|
yield session
|
||||||
yield session
|
|
||||||
except RuntimeError as e:
|
|
||||||
if "cancel scope" in str(e) and "different task" in str(e):
|
|
||||||
logger.debug(f"Ignoring expected pytest-asyncio teardown issue: {e}")
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def bob_mcp_client(bob_oauth_token: str) -> AsyncGenerator[ClientSession, Any]:
|
async def bob_mcp_client(
|
||||||
|
anyio_backend, bob_oauth_token: str
|
||||||
|
) -> AsyncGenerator[ClientSession, Any]:
|
||||||
"""MCP client authenticated as bob (viewer role)."""
|
"""MCP client authenticated as bob (viewer role)."""
|
||||||
try:
|
async for session in create_mcp_client_session(
|
||||||
async for session in create_mcp_client_session(
|
url="http://127.0.0.1:8001/mcp",
|
||||||
url="http://127.0.0.1:8001/mcp",
|
token=bob_oauth_token,
|
||||||
token=bob_oauth_token,
|
client_name="Bob MCP",
|
||||||
client_name="Bob MCP",
|
):
|
||||||
):
|
yield session
|
||||||
yield session
|
|
||||||
except RuntimeError as e:
|
|
||||||
if "cancel scope" in str(e) and "different task" in str(e):
|
|
||||||
logger.debug(f"Ignoring expected pytest-asyncio teardown issue: {e}")
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def charlie_mcp_client(
|
async def charlie_mcp_client(
|
||||||
|
anyio_backend,
|
||||||
charlie_oauth_token: str,
|
charlie_oauth_token: str,
|
||||||
) -> AsyncGenerator[ClientSession, Any]:
|
) -> AsyncGenerator[ClientSession, Any]:
|
||||||
"""MCP client authenticated as charlie (editor role, in 'editors' group)."""
|
"""MCP client authenticated as charlie (editor role, in 'editors' group)."""
|
||||||
try:
|
async for session in create_mcp_client_session(
|
||||||
async for session in create_mcp_client_session(
|
url="http://127.0.0.1:8001/mcp",
|
||||||
url="http://127.0.0.1:8001/mcp",
|
token=charlie_oauth_token,
|
||||||
token=charlie_oauth_token,
|
client_name="Charlie MCP",
|
||||||
client_name="Charlie MCP",
|
):
|
||||||
):
|
yield session
|
||||||
yield session
|
|
||||||
except RuntimeError as e:
|
|
||||||
if "cancel scope" in str(e) and "different task" in str(e):
|
|
||||||
logger.debug(f"Ignoring expected pytest-asyncio teardown issue: {e}")
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def diana_mcp_client(
|
async def diana_mcp_client(
|
||||||
|
anyio_backend,
|
||||||
diana_oauth_token: str,
|
diana_oauth_token: str,
|
||||||
) -> AsyncGenerator[ClientSession, Any]:
|
) -> AsyncGenerator[ClientSession, Any]:
|
||||||
"""MCP client authenticated as diana (no-access role)."""
|
"""MCP client authenticated as diana (no-access role)."""
|
||||||
try:
|
async for session in create_mcp_client_session(
|
||||||
async for session in create_mcp_client_session(
|
url="http://127.0.0.1:8001/mcp",
|
||||||
url="http://127.0.0.1:8001/mcp",
|
token=diana_oauth_token,
|
||||||
token=diana_oauth_token,
|
client_name="Diana MCP",
|
||||||
client_name="Diana MCP",
|
):
|
||||||
):
|
yield session
|
||||||
yield session
|
|
||||||
except RuntimeError as e:
|
|
||||||
if "cancel scope" in str(e) and "different task" in str(e):
|
|
||||||
logger.debug(f"Ignoring expected pytest-asyncio teardown issue: {e}")
|
|
||||||
else:
|
|
||||||
raise
|
|
||||||
|
|
||||||
|
|
||||||
# Test user/group fixtures for clean test isolation
|
# Test user/group fixtures for clean test isolation
|
||||||
|
|||||||
@@ -46,7 +46,7 @@ async def delete_board_acl(nc_client, board_id: int, acl_id: int):
|
|||||||
logger.info(f"Deleted ACL {acl_id} from board {board_id}")
|
logger.info(f"Deleted ACL {acl_id} from board {board_id}")
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_deck_board_view_permissions(
|
async def test_deck_board_view_permissions(
|
||||||
nc_client, alice_mcp_client, bob_mcp_client, diana_mcp_client
|
nc_client, alice_mcp_client, bob_mcp_client, diana_mcp_client
|
||||||
):
|
):
|
||||||
@@ -119,7 +119,7 @@ async def test_deck_board_view_permissions(
|
|||||||
await nc_client.deck.delete_board(board_id)
|
await nc_client.deck.delete_board(board_id)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_deck_board_edit_permissions(
|
async def test_deck_board_edit_permissions(
|
||||||
nc_client, alice_mcp_client, charlie_mcp_client, bob_mcp_client
|
nc_client, alice_mcp_client, charlie_mcp_client, bob_mcp_client
|
||||||
):
|
):
|
||||||
@@ -214,7 +214,7 @@ async def test_deck_board_edit_permissions(
|
|||||||
await nc_client.deck.delete_board(board_id)
|
await nc_client.deck.delete_board(board_id)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_deck_board_manage_permissions(
|
async def test_deck_board_manage_permissions(
|
||||||
nc_client, alice_mcp_client, charlie_mcp_client
|
nc_client, alice_mcp_client, charlie_mcp_client
|
||||||
):
|
):
|
||||||
@@ -289,7 +289,7 @@ async def test_deck_board_manage_permissions(
|
|||||||
await nc_client.deck.delete_board(board_id)
|
await nc_client.deck.delete_board(board_id)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_deck_user_isolation(nc_client, alice_mcp_client, bob_mcp_client):
|
async def test_deck_user_isolation(nc_client, alice_mcp_client, bob_mcp_client):
|
||||||
"""
|
"""
|
||||||
Test that users can only see their own boards when not shared.
|
Test that users can only see their own boards when not shared.
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ logger = logging.getLogger(__name__)
|
|||||||
pytestmark = [pytest.mark.integration, pytest.mark.oauth]
|
pytestmark = [pytest.mark.integration, pytest.mark.oauth]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_file_share_read_permissions(
|
async def test_file_share_read_permissions(
|
||||||
alice_mcp_client, bob_mcp_client, diana_mcp_client
|
alice_mcp_client, bob_mcp_client, diana_mcp_client
|
||||||
):
|
):
|
||||||
@@ -104,7 +104,7 @@ async def test_file_share_read_permissions(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_file_share_write_permissions(
|
async def test_file_share_write_permissions(
|
||||||
alice_mcp_client, charlie_mcp_client, bob_mcp_client
|
alice_mcp_client, charlie_mcp_client, bob_mcp_client
|
||||||
):
|
):
|
||||||
@@ -210,7 +210,7 @@ async def test_file_share_write_permissions(
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_file_list_permissions(alice_mcp_client, bob_mcp_client):
|
async def test_file_list_permissions(alice_mcp_client, bob_mcp_client):
|
||||||
"""
|
"""
|
||||||
Test that file listing respects share permissions.
|
Test that file listing respects share permissions.
|
||||||
@@ -326,7 +326,7 @@ async def test_file_list_permissions(alice_mcp_client, bob_mcp_client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_folder_share_permissions(alice_mcp_client, bob_mcp_client):
|
async def test_folder_share_permissions(alice_mcp_client, bob_mcp_client):
|
||||||
"""
|
"""
|
||||||
Test that folder sharing works correctly.
|
Test that folder sharing works correctly.
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ logger = logging.getLogger(__name__)
|
|||||||
pytestmark = [pytest.mark.integration, pytest.mark.oauth]
|
pytestmark = [pytest.mark.integration, pytest.mark.oauth]
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_notes_share_read_permissions(
|
async def test_notes_share_read_permissions(
|
||||||
nc_client, alice_mcp_client, bob_mcp_client, diana_mcp_client
|
nc_client, alice_mcp_client, bob_mcp_client, diana_mcp_client
|
||||||
):
|
):
|
||||||
@@ -82,7 +82,7 @@ async def test_notes_share_read_permissions(
|
|||||||
await nc_client.notes.delete_note(note_id)
|
await nc_client.notes.delete_note(note_id)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_notes_share_write_permissions(
|
async def test_notes_share_write_permissions(
|
||||||
nc_client, alice_mcp_client, charlie_mcp_client, bob_mcp_client
|
nc_client, alice_mcp_client, charlie_mcp_client, bob_mcp_client
|
||||||
):
|
):
|
||||||
@@ -149,7 +149,7 @@ async def test_notes_share_write_permissions(
|
|||||||
await nc_client.notes.delete_note(note_id)
|
await nc_client.notes.delete_note(note_id)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_user_isolation_notes(nc_client, alice_mcp_client, bob_mcp_client):
|
async def test_user_isolation_notes(nc_client, alice_mcp_client, bob_mcp_client):
|
||||||
"""
|
"""
|
||||||
Test that users can only see their own notes when not shared.
|
Test that users can only see their own notes when not shared.
|
||||||
@@ -222,7 +222,7 @@ async def test_user_isolation_notes(nc_client, alice_mcp_client, bob_mcp_client)
|
|||||||
await nc_client.notes.delete_note(bob_note_id)
|
await nc_client.notes.delete_note(bob_note_id)
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_oauth_mcp_clients_initialized(
|
async def test_oauth_mcp_clients_initialized(
|
||||||
alice_mcp_client, bob_mcp_client, charlie_mcp_client, diana_mcp_client
|
alice_mcp_client, bob_mcp_client, charlie_mcp_client, diana_mcp_client
|
||||||
):
|
):
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import pytest
|
|||||||
from nextcloud_mcp_server.client import NextcloudClient
|
from nextcloud_mcp_server.client import NextcloudClient
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_create_and_delete_user(nc_client: NextcloudClient, test_user):
|
async def test_create_and_delete_user(nc_client: NextcloudClient, test_user):
|
||||||
"""Test creating a user and verifying deletion (cleanup by fixture)."""
|
"""Test creating a user and verifying deletion (cleanup by fixture)."""
|
||||||
user_config = test_user
|
user_config = test_user
|
||||||
@@ -29,7 +29,7 @@ async def test_create_and_delete_user(nc_client: NextcloudClient, test_user):
|
|||||||
# Note: Fixture cleanup will also try to delete but handle 404 gracefully
|
# Note: Fixture cleanup will also try to delete but handle 404 gracefully
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_update_user_field(nc_client: NextcloudClient, test_user):
|
async def test_update_user_field(nc_client: NextcloudClient, test_user):
|
||||||
"""Test updating user fields."""
|
"""Test updating user fields."""
|
||||||
user_config = test_user
|
user_config = test_user
|
||||||
@@ -44,7 +44,7 @@ async def test_update_user_field(nc_client: NextcloudClient, test_user):
|
|||||||
# Fixture will handle cleanup
|
# Fixture will handle cleanup
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_user_groups(nc_client: NextcloudClient, test_user_in_group):
|
async def test_user_groups(nc_client: NextcloudClient, test_user_in_group):
|
||||||
"""Test adding and removing users from groups."""
|
"""Test adding and removing users from groups."""
|
||||||
user_config, groupid = test_user_in_group
|
user_config, groupid = test_user_in_group
|
||||||
@@ -61,7 +61,7 @@ async def test_user_groups(nc_client: NextcloudClient, test_user_in_group):
|
|||||||
# Fixtures will handle cleanup
|
# Fixtures will handle cleanup
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_user_subadmins(nc_client: NextcloudClient, test_user, test_group):
|
async def test_user_subadmins(nc_client: NextcloudClient, test_user, test_group):
|
||||||
"""Test promoting and demoting subadmins."""
|
"""Test promoting and demoting subadmins."""
|
||||||
user_config = test_user
|
user_config = test_user
|
||||||
@@ -82,7 +82,7 @@ async def test_user_subadmins(nc_client: NextcloudClient, test_user, test_group)
|
|||||||
# Fixtures will handle cleanup
|
# Fixtures will handle cleanup
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_disable_enable_user(nc_client: NextcloudClient, test_user):
|
async def test_disable_enable_user(nc_client: NextcloudClient, test_user):
|
||||||
"""Test disabling and enabling users."""
|
"""Test disabling and enabling users."""
|
||||||
user_config = test_user
|
user_config = test_user
|
||||||
@@ -102,7 +102,7 @@ async def test_disable_enable_user(nc_client: NextcloudClient, test_user):
|
|||||||
# Fixture will handle cleanup
|
# Fixture will handle cleanup
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.asyncio
|
@pytest.mark.anyio
|
||||||
async def test_get_editable_user_fields(nc_client: NextcloudClient):
|
async def test_get_editable_user_fields(nc_client: NextcloudClient):
|
||||||
editable_fields = await nc_client.users.get_editable_user_fields()
|
editable_fields = await nc_client.users.get_editable_user_fields()
|
||||||
assert "displayname" in editable_fields
|
assert "displayname" in editable_fields
|
||||||
|
|||||||
@@ -648,7 +648,6 @@ dev = [
|
|||||||
{ name = "ipython" },
|
{ name = "ipython" },
|
||||||
{ name = "playwright" },
|
{ name = "playwright" },
|
||||||
{ name = "pytest" },
|
{ name = "pytest" },
|
||||||
{ name = "pytest-asyncio" },
|
|
||||||
{ name = "pytest-cov" },
|
{ name = "pytest-cov" },
|
||||||
{ name = "pytest-playwright-asyncio" },
|
{ name = "pytest-playwright-asyncio" },
|
||||||
{ name = "ruff" },
|
{ name = "ruff" },
|
||||||
@@ -671,7 +670,6 @@ dev = [
|
|||||||
{ name = "ipython", specifier = ">=9.2.0" },
|
{ name = "ipython", specifier = ">=9.2.0" },
|
||||||
{ name = "playwright", specifier = ">=1.49.1" },
|
{ name = "playwright", specifier = ">=1.49.1" },
|
||||||
{ name = "pytest", specifier = ">=8.3.5" },
|
{ name = "pytest", specifier = ">=8.3.5" },
|
||||||
{ name = "pytest-asyncio", specifier = ">=1.0.0" },
|
|
||||||
{ name = "pytest-cov", specifier = ">=6.1.1" },
|
{ name = "pytest-cov", specifier = ">=6.1.1" },
|
||||||
{ name = "pytest-playwright-asyncio", specifier = ">=0.7.1" },
|
{ name = "pytest-playwright-asyncio", specifier = ">=0.7.1" },
|
||||||
{ name = "ruff", specifier = ">=0.11.13" },
|
{ name = "ruff", specifier = ">=0.11.13" },
|
||||||
|
|||||||
Reference in New Issue
Block a user