Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 6734de8389 | |||
| 3cb31d07f1 | |||
| 16b9123af3 | |||
| 51d1f075f5 |
@@ -1,3 +1,9 @@
|
|||||||
|
## v0.14.3 (2025-10-17)
|
||||||
|
|
||||||
|
### Fix
|
||||||
|
|
||||||
|
- **deps**: update dependency mcp to >=1.18,<1.19
|
||||||
|
|
||||||
## v0.14.2 (2025-10-16)
|
## v0.14.2 (2025-10-16)
|
||||||
|
|
||||||
### Fix
|
### Fix
|
||||||
|
|||||||
@@ -132,47 +132,35 @@ Each Nextcloud app has a corresponding server module that:
|
|||||||
- **Avoid creating standalone test scripts** - use pytest with proper fixtures instead
|
- **Avoid creating standalone test scripts** - use pytest with proper fixtures instead
|
||||||
|
|
||||||
#### OAuth/OIDC Testing
|
#### OAuth/OIDC Testing
|
||||||
OAuth integration tests support both **automated** (Playwright) and **interactive** authentication flows:
|
OAuth integration tests use **automated Playwright browser automation** to complete the OAuth flow programmatically.
|
||||||
|
|
||||||
**Automated Testing (Default - Recommended for CI/CD):**
|
**OAuth Testing Setup:**
|
||||||
- **Default fixtures**: `nc_oauth_client`, `nc_mcp_oauth_client` use Playwright automation
|
- **Main fixtures**: `nc_oauth_client`, `nc_mcp_oauth_client` - Use Playwright automation
|
||||||
- Uses Playwright headless browser automation to complete OAuth flow programmatically
|
|
||||||
- **Shared OAuth Client**: All test users authenticate using a single OAuth client
|
- **Shared OAuth Client**: All test users authenticate using a single OAuth client
|
||||||
- Stored in `.nextcloud_oauth_shared_test_client.json`
|
- Stored in `.nextcloud_oauth_shared_test_client.json`
|
||||||
- Matches production MCP server behavior
|
- Matches production MCP server behavior
|
||||||
- Each user gets their own unique access token
|
- Each user gets their own unique access token
|
||||||
- Implementation: `shared_oauth_client_credentials` fixture in `tests/conftest.py:812`
|
- Implementation: `shared_oauth_client_credentials` fixture in `tests/conftest.py`
|
||||||
- All Playwright fixtures: `playwright_oauth_token`, `nc_oauth_client`, `nc_mcp_oauth_client`, `nc_oauth_client_playwright`, `nc_mcp_oauth_client_playwright`
|
- **Available fixtures**: `playwright_oauth_token`, `nc_oauth_client`, `nc_mcp_oauth_client`
|
||||||
- Multi-user fixtures: `alice_oauth_token`, `bob_oauth_token`, `charlie_oauth_token`, `diana_oauth_token`
|
- **Multi-user fixtures**: `alice_oauth_token`, `bob_oauth_token`, `charlie_oauth_token`, `diana_oauth_token`
|
||||||
- Requires: `NEXTCLOUD_HOST`, `NEXTCLOUD_USERNAME`, `NEXTCLOUD_PASSWORD` environment variables
|
- **Requirements**: `NEXTCLOUD_HOST`, `NEXTCLOUD_USERNAME`, `NEXTCLOUD_PASSWORD` environment variables
|
||||||
- Uses `pytest-playwright-asyncio` for async Playwright fixtures
|
- Uses `pytest-playwright-asyncio` for async Playwright fixtures
|
||||||
- Playwright configuration: Use pytest CLI args like `--browser firefox --headed` to customize
|
- **Playwright configuration**: Use pytest CLI args like `--browser firefox --headed` to customize
|
||||||
- Install browsers: `uv run playwright install firefox` (or `chromium`, `webkit`)
|
- **Install browsers**: `uv run playwright install firefox` (or `chromium`, `webkit`)
|
||||||
- Example:
|
|
||||||
```bash
|
|
||||||
# Run all OAuth tests with automated Playwright flow using Firefox
|
|
||||||
uv run pytest tests/server/test_oauth*.py --browser firefox -v
|
|
||||||
|
|
||||||
# Run specific Playwright tests with visible browser for debugging
|
**Example Commands:**
|
||||||
uv run pytest tests/server/test_mcp_oauth.py --browser firefox --headed -v
|
```bash
|
||||||
|
# Run all OAuth tests with Playwright automation using Firefox
|
||||||
|
uv run pytest tests/server/test_oauth*.py --browser firefox -v
|
||||||
|
|
||||||
# Run with Chromium (default)
|
# Run specific tests with visible browser for debugging
|
||||||
uv run pytest tests/server/test_oauth*.py -v
|
uv run pytest tests/server/test_mcp_oauth.py --browser firefox --headed -v
|
||||||
```
|
|
||||||
|
|
||||||
**Interactive Testing (Manual browser login):**
|
# Run with Chromium (default)
|
||||||
- Opens system browser and waits for manual login/authorization
|
uv run pytest tests/server/test_oauth*.py -v
|
||||||
- Fixtures: `interactive_oauth_token`, `nc_oauth_client_interactive`, `nc_mcp_oauth_client_interactive`
|
```
|
||||||
- Requires: User to complete browser-based login when prompted
|
|
||||||
- Useful for: Debugging OAuth flows, testing with 2FA, local development
|
|
||||||
- **Automatically skipped in GitHub Actions CI** - Interactive fixtures check for `GITHUB_ACTIONS` environment variable
|
|
||||||
- Example:
|
|
||||||
```bash
|
|
||||||
# Run OAuth tests with interactive flow (will open browser and wait for manual login)
|
|
||||||
uv run pytest tests/client/test_oauth_interactive.py -v
|
|
||||||
```
|
|
||||||
|
|
||||||
**Test Environment Setup:**
|
**Test Environment:**
|
||||||
- **Two MCP server containers are available:**
|
- **Two MCP server containers are available:**
|
||||||
- `mcp` (port 8000): Uses basic auth with admin credentials - for most testing
|
- `mcp` (port 8000): Uses basic auth with admin credentials - for most testing
|
||||||
- `mcp-oauth` (port 8001): Uses OAuth authentication - for OAuth-specific testing
|
- `mcp-oauth` (port 8001): Uses OAuth authentication - for OAuth-specific testing
|
||||||
@@ -180,9 +168,8 @@ OAuth integration tests support both **automated** (Playwright) and **interactiv
|
|||||||
- **Important**: When working on OAuth functionality, always rebuild `mcp-oauth` container, not `mcp`
|
- **Important**: When working on OAuth functionality, always rebuild `mcp-oauth` container, not `mcp`
|
||||||
- OAuth client credentials cached in `.nextcloud_oauth_shared_test_client.json`
|
- OAuth client credentials cached in `.nextcloud_oauth_shared_test_client.json`
|
||||||
|
|
||||||
**CI/CD Considerations:**
|
**CI/CD Notes:**
|
||||||
- Interactive OAuth tests are automatically skipped when `GITHUB_ACTIONS` environment variable is set
|
- Playwright tests run in CI/CD environments
|
||||||
- Automated Playwright tests will run in CI/CD environments
|
|
||||||
- Use Firefox browser in CI: `--browser firefox` (Chromium may have issues with localhost redirects)
|
- Use Firefox browser in CI: `--browser firefox` (Chromium may have issues with localhost redirects)
|
||||||
|
|
||||||
### Configuration Files
|
### Configuration Files
|
||||||
|
|||||||
+2
-2
@@ -1,6 +1,6 @@
|
|||||||
[project]
|
[project]
|
||||||
name = "nextcloud-mcp-server"
|
name = "nextcloud-mcp-server"
|
||||||
version = "0.14.2"
|
version = "0.14.3"
|
||||||
description = ""
|
description = ""
|
||||||
authors = [
|
authors = [
|
||||||
{name = "Chris Coutinho",email = "chris@coutinho.io"}
|
{name = "Chris Coutinho",email = "chris@coutinho.io"}
|
||||||
@@ -8,7 +8,7 @@ authors = [
|
|||||||
readme = "README.md"
|
readme = "README.md"
|
||||||
requires-python = ">=3.11"
|
requires-python = ">=3.11"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"mcp[cli] (>=1.17,<1.18)",
|
"mcp[cli] (>=1.18,<1.19)",
|
||||||
"httpx (>=0.28.1,<0.29.0)",
|
"httpx (>=0.28.1,<0.29.0)",
|
||||||
"pillow (>=12.0.0,<12.1.0)",
|
"pillow (>=12.0.0,<12.1.0)",
|
||||||
"icalendar (>=6.0.0,<7.0.0)",
|
"icalendar (>=6.0.0,<7.0.0)",
|
||||||
|
|||||||
@@ -1,41 +0,0 @@
|
|||||||
"""Interactive integration tests for OAuth authentication."""
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
|
|
||||||
import pytest
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
pytestmark = [pytest.mark.integration, pytest.mark.oauth]
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.skipif(
|
|
||||||
"GITHUB_ACTIONS" in os.environ,
|
|
||||||
reason="Unable to access interactive browser in GitHub Actions",
|
|
||||||
)
|
|
||||||
async def test_oauth_client_with_interactive_flow(nc_oauth_client_interactive):
|
|
||||||
"""Test that OAuth client created via interactive flow can access Nextcloud APIs."""
|
|
||||||
# Test 1: Check capabilities
|
|
||||||
capabilities = await nc_oauth_client_interactive.capabilities()
|
|
||||||
assert capabilities is not None
|
|
||||||
logger.info("OAuth client (interactive) successfully fetched capabilities")
|
|
||||||
|
|
||||||
# Test 2: List notes
|
|
||||||
notes = await nc_oauth_client_interactive.notes.get_all_notes()
|
|
||||||
assert isinstance(notes, list)
|
|
||||||
logger.info(f"OAuth client (interactive) successfully listed {len(notes)} notes")
|
|
||||||
|
|
||||||
# Test 3: Create and delete a note
|
|
||||||
test_note = await nc_oauth_client_interactive.notes.create_note(
|
|
||||||
title="OAuth Interactive Test Note",
|
|
||||||
content="This note was created during OAuth interactive testing",
|
|
||||||
)
|
|
||||||
assert test_note is not None
|
|
||||||
assert test_note.get("id") is not None
|
|
||||||
note_id = test_note["id"]
|
|
||||||
logger.info(f"OAuth client (interactive) successfully created note {note_id}")
|
|
||||||
|
|
||||||
# Clean up
|
|
||||||
await nc_oauth_client_interactive.notes.delete_note(note_id=note_id)
|
|
||||||
logger.info(f"OAuth client (interactive) successfully deleted note {note_id}")
|
|
||||||
@@ -19,14 +19,14 @@ async def test_playwright_oauth_token_acquisition(playwright_oauth_token: str):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_oauth_client_with_playwright_flow(nc_oauth_client_playwright):
|
async def test_oauth_client_with_playwright_flow(nc_oauth_client):
|
||||||
"""Test that OAuth client created via Playwright flow can access Nextcloud APIs."""
|
"""Test that OAuth client created via Playwright flow can access Nextcloud APIs."""
|
||||||
# Test 1: Check capabilities
|
# Test 1: Check capabilities
|
||||||
capabilities = await nc_oauth_client_playwright.capabilities()
|
capabilities = await nc_oauth_client.capabilities()
|
||||||
assert capabilities is not None
|
assert capabilities is not None
|
||||||
logger.info("OAuth client (Playwright) successfully fetched capabilities")
|
logger.info("OAuth client (Playwright) successfully fetched capabilities")
|
||||||
|
|
||||||
# Test 2: List notes
|
# Test 2: List notes
|
||||||
notes = await nc_oauth_client_playwright.notes.get_all_notes()
|
notes = await nc_oauth_client.notes.get_all_notes()
|
||||||
assert isinstance(notes, list)
|
assert isinstance(notes, list)
|
||||||
logger.info(f"OAuth client (Playwright) successfully listed {len(notes)} notes")
|
logger.info(f"OAuth client (Playwright) successfully listed {len(notes)} notes")
|
||||||
|
|||||||
+2
-205
@@ -167,27 +167,6 @@ async def nc_mcp_client() -> AsyncGenerator[ClientSession, Any]:
|
|||||||
yield session
|
yield session
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
async def nc_mcp_oauth_client_interactive(
|
|
||||||
interactive_oauth_token: str,
|
|
||||||
) -> AsyncGenerator[ClientSession, Any]:
|
|
||||||
"""
|
|
||||||
Fixture to create an MCP client session for OAuth integration tests using interactive authentication.
|
|
||||||
Connects to the OAuth-enabled MCP server on port 8001 with OAuth authentication.
|
|
||||||
Requires manual browser login.
|
|
||||||
|
|
||||||
For automated testing, use nc_mcp_oauth_client fixture instead.
|
|
||||||
|
|
||||||
Automatically skips when running in GitHub Actions CI.
|
|
||||||
"""
|
|
||||||
async for session in create_mcp_client_session(
|
|
||||||
url="http://127.0.0.1:8001/mcp",
|
|
||||||
token=interactive_oauth_token,
|
|
||||||
client_name="OAuth MCP (Interactive)",
|
|
||||||
):
|
|
||||||
yield session
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def nc_mcp_oauth_client(
|
async def nc_mcp_oauth_client(
|
||||||
playwright_oauth_token: str,
|
playwright_oauth_token: str,
|
||||||
@@ -196,8 +175,7 @@ async def nc_mcp_oauth_client(
|
|||||||
Fixture to create an MCP client session for OAuth integration tests using Playwright automation.
|
Fixture to create an MCP client session for OAuth integration tests using Playwright automation.
|
||||||
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.
|
||||||
|
|
||||||
This is the default OAuth MCP fixture using headless browser automation suitable for CI/CD.
|
Uses headless browser automation suitable for CI/CD.
|
||||||
For interactive testing with manual browser login, use nc_mcp_oauth_client_interactive instead.
|
|
||||||
"""
|
"""
|
||||||
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",
|
||||||
@@ -524,55 +502,13 @@ async def temporary_board_with_card(
|
|||||||
logger.error(f"Unexpected error deleting temporary card {card.id}: {e}")
|
logger.error(f"Unexpected error deleting temporary card {card.id}: {e}")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
async def nc_oauth_client_interactive(
|
|
||||||
interactive_oauth_token: str,
|
|
||||||
) -> AsyncGenerator[NextcloudClient, Any]:
|
|
||||||
"""
|
|
||||||
Fixture to create a NextcloudClient instance using interactive OAuth authentication.
|
|
||||||
Uses the interactive_oauth_token fixture which requires manual browser login.
|
|
||||||
|
|
||||||
For automated testing, use nc_oauth_client fixture instead.
|
|
||||||
|
|
||||||
Automatically skips when running in GitHub Actions CI.
|
|
||||||
"""
|
|
||||||
|
|
||||||
nextcloud_host = os.getenv("NEXTCLOUD_HOST")
|
|
||||||
username = os.getenv("NEXTCLOUD_USERNAME")
|
|
||||||
|
|
||||||
if not all([nextcloud_host, username]):
|
|
||||||
pytest.skip("OAuth client fixture requires NEXTCLOUD_HOST and USERNAME")
|
|
||||||
|
|
||||||
logger.info(f"Creating OAuth NextcloudClient (Interactive) for user: {username}")
|
|
||||||
client = NextcloudClient.from_token(
|
|
||||||
base_url=nextcloud_host,
|
|
||||||
token=interactive_oauth_token,
|
|
||||||
username=username,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify the OAuth client works
|
|
||||||
try:
|
|
||||||
await client.capabilities()
|
|
||||||
logger.info(
|
|
||||||
"OAuth NextcloudClient (Interactive) initialized and capabilities checked."
|
|
||||||
)
|
|
||||||
yield client
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to initialize OAuth NextcloudClient (Interactive): {e}")
|
|
||||||
pytest.fail(f"Failed to connect to Nextcloud with OAuth token: {e}")
|
|
||||||
finally:
|
|
||||||
await client.close()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def nc_oauth_client(
|
async def nc_oauth_client(
|
||||||
playwright_oauth_token: str,
|
playwright_oauth_token: str,
|
||||||
) -> AsyncGenerator[NextcloudClient, Any]:
|
) -> AsyncGenerator[NextcloudClient, Any]:
|
||||||
"""
|
"""
|
||||||
Fixture to create a NextcloudClient instance using automated Playwright OAuth authentication.
|
Fixture to create a NextcloudClient instance using automated Playwright OAuth authentication.
|
||||||
This is the default OAuth fixture using headless browser automation suitable for CI/CD.
|
Uses headless browser automation suitable for CI/CD.
|
||||||
|
|
||||||
For interactive testing with manual browser login, use nc_oauth_client_interactive instead.
|
|
||||||
"""
|
"""
|
||||||
nextcloud_host = os.getenv("NEXTCLOUD_HOST")
|
nextcloud_host = os.getenv("NEXTCLOUD_HOST")
|
||||||
username = os.getenv("NEXTCLOUD_USERNAME")
|
username = os.getenv("NEXTCLOUD_USERNAME")
|
||||||
@@ -689,84 +625,6 @@ def oauth_callback_server():
|
|||||||
server_thread.join(timeout=1)
|
server_thread.join(timeout=1)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
async def interactive_oauth_token(oauth_callback_server) -> str:
|
|
||||||
"""
|
|
||||||
Fixture to obtain an OAuth access token for integration tests.
|
|
||||||
|
|
||||||
This uses the interactive OAuth flow to get a token.
|
|
||||||
Depends on oauth_callback_server fixture for HTTP callback handling.
|
|
||||||
|
|
||||||
Automatically skips when running in GitHub Actions CI.
|
|
||||||
"""
|
|
||||||
|
|
||||||
import time
|
|
||||||
import webbrowser
|
|
||||||
|
|
||||||
from nextcloud_mcp_server.auth.client_registration import load_or_register_client
|
|
||||||
|
|
||||||
# Unpack the server fixture (now returns dict of auth_states)
|
|
||||||
auth_states, callback_url = oauth_callback_server
|
|
||||||
|
|
||||||
nextcloud_host = os.getenv("NEXTCLOUD_HOST")
|
|
||||||
async with httpx.AsyncClient() as http_client:
|
|
||||||
discovery_url = f"{nextcloud_host}/.well-known/openid-configuration"
|
|
||||||
discovery_response = await http_client.get(discovery_url)
|
|
||||||
oidc_config = discovery_response.json()
|
|
||||||
token_endpoint = oidc_config.get("token_endpoint")
|
|
||||||
registration_endpoint = oidc_config.get("registration_endpoint")
|
|
||||||
authorization_endpoint = oidc_config.get("authorization_endpoint")
|
|
||||||
client_info = await load_or_register_client(
|
|
||||||
nextcloud_url=nextcloud_host,
|
|
||||||
registration_endpoint=registration_endpoint,
|
|
||||||
storage_path=".nextcloud_oauth_shared_test_client.json",
|
|
||||||
redirect_uris=[callback_url],
|
|
||||||
)
|
|
||||||
|
|
||||||
# First, open Nextcloud login page to establish session
|
|
||||||
login_url = f"{nextcloud_host}/login"
|
|
||||||
logger.info(f"Please log in to Nextcloud at: {login_url}")
|
|
||||||
logger.info(
|
|
||||||
"After logging in, the OAuth authorization will proceed automatically"
|
|
||||||
)
|
|
||||||
|
|
||||||
# Construct authorization URL (no state parameter for interactive flow)
|
|
||||||
auth_url = f"{authorization_endpoint}?response_type=code&client_id={client_info.client_id}&redirect_uri={callback_url}&scope=openid%20profile%20email"
|
|
||||||
|
|
||||||
# Open authorization URL in browser
|
|
||||||
webbrowser.open(auth_url)
|
|
||||||
|
|
||||||
# Wait for auth code with timeout (uses "_default" key for flows without state)
|
|
||||||
timeout = 120 # 2 minutes
|
|
||||||
start_time = time.time()
|
|
||||||
while "_default" not in auth_states:
|
|
||||||
if time.time() - start_time > timeout:
|
|
||||||
raise TimeoutError("OAuth authorization timed out after 2 minutes")
|
|
||||||
logger.info("Waiting for OAuth authorization...")
|
|
||||||
time.sleep(1)
|
|
||||||
|
|
||||||
auth_code = auth_states["_default"]
|
|
||||||
logger.info("Received authorization code, exchanging for token...")
|
|
||||||
|
|
||||||
token_response = await http_client.post(
|
|
||||||
token_endpoint,
|
|
||||||
data={
|
|
||||||
"grant_type": "authorization_code",
|
|
||||||
"code": auth_code,
|
|
||||||
"redirect_uri": callback_url,
|
|
||||||
"client_id": client_info.client_id,
|
|
||||||
"client_secret": client_info.client_secret,
|
|
||||||
},
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.debug(f"Token response: {token_response.text}")
|
|
||||||
token_data = token_response.json()
|
|
||||||
logger.debug(f"Token data: {token_data}")
|
|
||||||
access_token = token_data.get("access_token")
|
|
||||||
|
|
||||||
return access_token
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def shared_oauth_client_credentials(oauth_callback_server):
|
async def shared_oauth_client_credentials(oauth_callback_server):
|
||||||
"""
|
"""
|
||||||
@@ -991,67 +849,6 @@ async def playwright_oauth_token(
|
|||||||
return access_token
|
return access_token
|
||||||
|
|
||||||
|
|
||||||
# Alternative fixtures using Playwright token (for automated/CI testing)
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
async def nc_oauth_client_playwright(
|
|
||||||
playwright_oauth_token: str,
|
|
||||||
) -> AsyncGenerator[NextcloudClient, Any]:
|
|
||||||
"""
|
|
||||||
Fixture to create a NextcloudClient instance using automated Playwright OAuth authentication.
|
|
||||||
This fixture uses headless browser automation and is suitable for CI/CD pipelines.
|
|
||||||
|
|
||||||
For interactive testing, use nc_oauth_client fixture instead.
|
|
||||||
"""
|
|
||||||
nextcloud_host = os.getenv("NEXTCLOUD_HOST")
|
|
||||||
username = os.getenv("NEXTCLOUD_USERNAME")
|
|
||||||
|
|
||||||
if not all([nextcloud_host, username]):
|
|
||||||
pytest.skip(
|
|
||||||
"Playwright OAuth client fixture requires NEXTCLOUD_HOST and USERNAME"
|
|
||||||
)
|
|
||||||
|
|
||||||
logger.info(f"Creating OAuth NextcloudClient (Playwright) for user: {username}")
|
|
||||||
client = NextcloudClient.from_token(
|
|
||||||
base_url=nextcloud_host,
|
|
||||||
token=playwright_oauth_token,
|
|
||||||
username=username,
|
|
||||||
)
|
|
||||||
|
|
||||||
# Verify the OAuth client works
|
|
||||||
try:
|
|
||||||
await client.capabilities()
|
|
||||||
logger.info(
|
|
||||||
"OAuth NextcloudClient (Playwright) initialized and capabilities checked."
|
|
||||||
)
|
|
||||||
yield client
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"Failed to initialize Playwright OAuth NextcloudClient: {e}")
|
|
||||||
pytest.fail(f"Failed to connect to Nextcloud with Playwright OAuth token: {e}")
|
|
||||||
finally:
|
|
||||||
await client.close()
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
|
||||||
async def nc_mcp_oauth_client_playwright(
|
|
||||||
playwright_oauth_token: str,
|
|
||||||
) -> AsyncGenerator[ClientSession, Any]:
|
|
||||||
"""
|
|
||||||
Fixture to create an MCP client session for OAuth integration tests using Playwright automation.
|
|
||||||
Connects to the OAuth-enabled MCP server on port 8001 with OAuth authentication.
|
|
||||||
|
|
||||||
This fixture uses headless browser automation and is suitable for CI/CD pipelines.
|
|
||||||
For interactive testing, use nc_mcp_oauth_client fixture instead.
|
|
||||||
"""
|
|
||||||
async for session in create_mcp_client_session(
|
|
||||||
url="http://127.0.0.1:8001/mcp",
|
|
||||||
token=playwright_oauth_token,
|
|
||||||
client_name="OAuth MCP (Playwright Alt)",
|
|
||||||
):
|
|
||||||
yield session
|
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
async def test_users_setup(nc_client: NextcloudClient):
|
async def test_users_setup(nc_client: NextcloudClient):
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -38,11 +38,11 @@ async def test_mcp_oauth_tool_execution(nc_mcp_oauth_client):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
async def test_mcp_oauth_client_with_playwright(nc_mcp_oauth_client_playwright):
|
async def test_mcp_oauth_client_with_playwright(nc_mcp_oauth_client):
|
||||||
"""Test that MCP OAuth client via Playwright can execute tools."""
|
"""Test that MCP OAuth client via Playwright can execute tools."""
|
||||||
|
|
||||||
# Test: Execute the 'nc_notes_search_notes' tool
|
# Test: Execute the 'nc_notes_search_notes' tool
|
||||||
result = await nc_mcp_oauth_client_playwright.call_tool(
|
result = await nc_mcp_oauth_client.call_tool(
|
||||||
"nc_notes_search_notes", arguments={"query": ""}
|
"nc_notes_search_notes", arguments={"query": ""}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -593,7 +593,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mcp"
|
name = "mcp"
|
||||||
version = "1.17.0"
|
version = "1.18.0"
|
||||||
source = { registry = "https://pypi.org/simple" }
|
source = { registry = "https://pypi.org/simple" }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "anyio" },
|
{ name = "anyio" },
|
||||||
@@ -608,9 +608,9 @@ dependencies = [
|
|||||||
{ name = "starlette" },
|
{ name = "starlette" },
|
||||||
{ name = "uvicorn", marker = "sys_platform != 'emscripten'" },
|
{ name = "uvicorn", marker = "sys_platform != 'emscripten'" },
|
||||||
]
|
]
|
||||||
sdist = { url = "https://files.pythonhosted.org/packages/5a/79/5724a540df19e192e8606c543cdcf162de8eb435077520cca150f7365ec0/mcp-1.17.0.tar.gz", hash = "sha256:1b57fabf3203240ccc48e39859faf3ae1ccb0b571ff798bbedae800c73c6df90", size = 477951, upload-time = "2025-10-10T12:16:44.519Z" }
|
sdist = { url = "https://files.pythonhosted.org/packages/1a/e0/fe34ce16ea2bacce489ab859abd1b47ae28b438c3ef60b9c5eee6c02592f/mcp-1.18.0.tar.gz", hash = "sha256:aa278c44b1efc0a297f53b68df865b988e52dd08182d702019edcf33a8e109f6", size = 482926, upload-time = "2025-10-16T19:19:55.125Z" }
|
||||||
wheels = [
|
wheels = [
|
||||||
{ url = "https://files.pythonhosted.org/packages/1c/72/3751feae343a5ad07959df713907b5c3fbaed269d697a14b0c449080cf2e/mcp-1.17.0-py3-none-any.whl", hash = "sha256:0660ef275cada7a545af154db3082f176cf1d2681d5e35ae63e014faf0a35d40", size = 167737, upload-time = "2025-10-10T12:16:42.863Z" },
|
{ url = "https://files.pythonhosted.org/packages/1b/44/f5970e3e899803823826283a70b6003afd46f28e082544407e24575eccd3/mcp-1.18.0-py3-none-any.whl", hash = "sha256:42f10c270de18e7892fdf9da259029120b1ea23964ff688248c69db9d72b1d0a", size = 168762, upload-time = "2025-10-16T19:19:53.2Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
[package.optional-dependencies]
|
[package.optional-dependencies]
|
||||||
@@ -630,7 +630,7 @@ wheels = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nextcloud-mcp-server"
|
name = "nextcloud-mcp-server"
|
||||||
version = "0.14.2"
|
version = "0.14.3"
|
||||||
source = { editable = "." }
|
source = { editable = "." }
|
||||||
dependencies = [
|
dependencies = [
|
||||||
{ name = "click" },
|
{ name = "click" },
|
||||||
@@ -659,7 +659,7 @@ requires-dist = [
|
|||||||
{ name = "click", specifier = ">=8.1.8" },
|
{ name = "click", specifier = ">=8.1.8" },
|
||||||
{ name = "httpx", specifier = ">=0.28.1,<0.29.0" },
|
{ name = "httpx", specifier = ">=0.28.1,<0.29.0" },
|
||||||
{ name = "icalendar", specifier = ">=6.0.0,<7.0.0" },
|
{ name = "icalendar", specifier = ">=6.0.0,<7.0.0" },
|
||||||
{ name = "mcp", extras = ["cli"], specifier = ">=1.17,<1.18" },
|
{ name = "mcp", extras = ["cli"], specifier = ">=1.18,<1.19" },
|
||||||
{ name = "pillow", specifier = ">=12.0.0,<12.1.0" },
|
{ name = "pillow", specifier = ">=12.0.0,<12.1.0" },
|
||||||
{ name = "pydantic", specifier = ">=2.11.4" },
|
{ name = "pydantic", specifier = ">=2.11.4" },
|
||||||
{ name = "pythonvcard4", specifier = ">=0.2.0" },
|
{ name = "pythonvcard4", specifier = ">=0.2.0" },
|
||||||
|
|||||||
Reference in New Issue
Block a user