feat: initial commit from workspace-mcp
Some checks failed
Check Maintainer Edits Enabled / check-maintainer-edits (pull_request) Has been cancelled
Check Maintainer Edits Enabled / check-maintainer-edits-internal (pull_request) Has been cancelled
Docker Build and Push to GHCR / build-and-push (pull_request) Has been cancelled
Ruff / ruff (pull_request) Has been cancelled
Some checks failed
Check Maintainer Edits Enabled / check-maintainer-edits (pull_request) Has been cancelled
Check Maintainer Edits Enabled / check-maintainer-edits-internal (pull_request) Has been cancelled
Docker Build and Push to GHCR / build-and-push (pull_request) Has been cancelled
Ruff / ruff (pull_request) Has been cancelled
This commit is contained in:
0
tests/core/__init__.py
Normal file
0
tests/core/__init__.py
Normal file
69
tests/core/test_attachment_route.py
Normal file
69
tests/core/test_attachment_route.py
Normal file
@@ -0,0 +1,69 @@
|
||||
import pytest
|
||||
from starlette.requests import Request
|
||||
from starlette.responses import FileResponse, JSONResponse
|
||||
|
||||
from core.server import serve_attachment
|
||||
|
||||
|
||||
def _build_request(file_id: str) -> Request:
|
||||
scope = {
|
||||
"type": "http",
|
||||
"asgi": {"version": "3.0"},
|
||||
"http_version": "1.1",
|
||||
"method": "GET",
|
||||
"scheme": "http",
|
||||
"path": f"/attachments/{file_id}",
|
||||
"raw_path": f"/attachments/{file_id}".encode(),
|
||||
"query_string": b"",
|
||||
"headers": [],
|
||||
"client": ("127.0.0.1", 12345),
|
||||
"server": ("localhost", 8000),
|
||||
"path_params": {"file_id": file_id},
|
||||
}
|
||||
|
||||
async def receive():
|
||||
return {"type": "http.request", "body": b"", "more_body": False}
|
||||
|
||||
return Request(scope, receive)
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_serve_attachment_uses_path_param_file_id(monkeypatch, tmp_path):
|
||||
file_path = tmp_path / "sample.pdf"
|
||||
file_path.write_bytes(b"%PDF-1.3\n")
|
||||
captured = {}
|
||||
|
||||
class DummyStorage:
|
||||
def get_attachment_metadata(self, file_id):
|
||||
captured["file_id"] = file_id
|
||||
return {"filename": "sample.pdf", "mime_type": "application/pdf"}
|
||||
|
||||
def get_attachment_path(self, _file_id):
|
||||
return file_path
|
||||
|
||||
monkeypatch.setattr(
|
||||
"core.attachment_storage.get_attachment_storage", lambda: DummyStorage()
|
||||
)
|
||||
|
||||
response = await serve_attachment(_build_request("abc123"))
|
||||
|
||||
assert captured["file_id"] == "abc123"
|
||||
assert isinstance(response, FileResponse)
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_serve_attachment_404_when_metadata_missing(monkeypatch):
|
||||
class DummyStorage:
|
||||
def get_attachment_metadata(self, _file_id):
|
||||
return None
|
||||
|
||||
monkeypatch.setattr(
|
||||
"core.attachment_storage.get_attachment_storage", lambda: DummyStorage()
|
||||
)
|
||||
|
||||
response = await serve_attachment(_build_request("missing"))
|
||||
|
||||
assert isinstance(response, JSONResponse)
|
||||
assert response.status_code == 404
|
||||
assert b"Attachment not found or expired" in response.body
|
||||
112
tests/core/test_comments.py
Normal file
112
tests/core/test_comments.py
Normal file
@@ -0,0 +1,112 @@
|
||||
"""Tests for core comments module."""
|
||||
|
||||
import sys
|
||||
import os
|
||||
import pytest
|
||||
from unittest.mock import Mock
|
||||
|
||||
sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), "../..")))
|
||||
|
||||
from core.comments import _read_comments_impl
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_comments_includes_quoted_text():
|
||||
"""Verify that quotedFileContent.value is surfaced in the output."""
|
||||
mock_service = Mock()
|
||||
mock_service.comments.return_value.list.return_value.execute = Mock(
|
||||
return_value={
|
||||
"comments": [
|
||||
{
|
||||
"id": "c1",
|
||||
"content": "Needs a citation here.",
|
||||
"author": {"displayName": "Alice"},
|
||||
"createdTime": "2025-01-15T10:00:00Z",
|
||||
"modifiedTime": "2025-01-15T10:00:00Z",
|
||||
"resolved": False,
|
||||
"quotedFileContent": {
|
||||
"mimeType": "text/html",
|
||||
"value": "the specific text that was highlighted",
|
||||
},
|
||||
"replies": [],
|
||||
},
|
||||
{
|
||||
"id": "c2",
|
||||
"content": "General comment without anchor.",
|
||||
"author": {"displayName": "Bob"},
|
||||
"createdTime": "2025-01-16T09:00:00Z",
|
||||
"modifiedTime": "2025-01-16T09:00:00Z",
|
||||
"resolved": False,
|
||||
"replies": [],
|
||||
},
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
result = await _read_comments_impl(mock_service, "document", "doc123")
|
||||
|
||||
# Comment with anchor text should show the quoted text
|
||||
assert "Quoted text: the specific text that was highlighted" in result
|
||||
assert "Needs a citation here." in result
|
||||
|
||||
# Comment without anchor text should not have a "Quoted text" line between Bob's author and content
|
||||
# The output uses literal \n joins, so split on that
|
||||
parts = result.split("\\n")
|
||||
bob_section_started = False
|
||||
for part in parts:
|
||||
if "Author: Bob" in part:
|
||||
bob_section_started = True
|
||||
if bob_section_started and "Quoted text:" in part:
|
||||
pytest.fail(
|
||||
"Comment without quotedFileContent should not show 'Quoted text'"
|
||||
)
|
||||
if bob_section_started and "Content: General comment" in part:
|
||||
break
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_comments_empty():
|
||||
"""Verify empty comments returns appropriate message."""
|
||||
mock_service = Mock()
|
||||
mock_service.comments.return_value.list.return_value.execute = Mock(
|
||||
return_value={"comments": []}
|
||||
)
|
||||
|
||||
result = await _read_comments_impl(mock_service, "document", "doc123")
|
||||
assert "No comments found" in result
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_read_comments_with_replies():
|
||||
"""Verify replies are included in output."""
|
||||
mock_service = Mock()
|
||||
mock_service.comments.return_value.list.return_value.execute = Mock(
|
||||
return_value={
|
||||
"comments": [
|
||||
{
|
||||
"id": "c1",
|
||||
"content": "Question?",
|
||||
"author": {"displayName": "Alice"},
|
||||
"createdTime": "2025-01-15T10:00:00Z",
|
||||
"modifiedTime": "2025-01-15T10:00:00Z",
|
||||
"resolved": False,
|
||||
"quotedFileContent": {"value": "some text"},
|
||||
"replies": [
|
||||
{
|
||||
"id": "r1",
|
||||
"content": "Answer.",
|
||||
"author": {"displayName": "Bob"},
|
||||
"createdTime": "2025-01-15T11:00:00Z",
|
||||
"modifiedTime": "2025-01-15T11:00:00Z",
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
}
|
||||
)
|
||||
|
||||
result = await _read_comments_impl(mock_service, "document", "doc123")
|
||||
assert "Question?" in result
|
||||
assert "Answer." in result
|
||||
assert "Bob" in result
|
||||
assert "Quoted text: some text" in result
|
||||
90
tests/core/test_well_known_cache_control_middleware.py
Normal file
90
tests/core/test_well_known_cache_control_middleware.py
Normal file
@@ -0,0 +1,90 @@
|
||||
import importlib
|
||||
|
||||
from starlette.applications import Starlette
|
||||
from starlette.middleware import Middleware
|
||||
from starlette.responses import Response
|
||||
from starlette.routing import Route
|
||||
from starlette.testclient import TestClient
|
||||
|
||||
|
||||
def test_well_known_cache_control_middleware_rewrites_headers():
|
||||
from core.server import WellKnownCacheControlMiddleware, _compute_scope_fingerprint
|
||||
|
||||
async def well_known_endpoint(request):
|
||||
response = Response("ok")
|
||||
response.headers["Cache-Control"] = "public, max-age=3600"
|
||||
response.set_cookie("a", "1")
|
||||
response.set_cookie("b", "2")
|
||||
return response
|
||||
|
||||
async def regular_endpoint(request):
|
||||
response = Response("ok")
|
||||
response.headers["Cache-Control"] = "public, max-age=3600"
|
||||
return response
|
||||
|
||||
app = Starlette(
|
||||
routes=[
|
||||
Route("/.well-known/oauth-authorization-server", well_known_endpoint),
|
||||
Route("/.well-known/oauth-authorization-server-extra", regular_endpoint),
|
||||
Route("/health", regular_endpoint),
|
||||
],
|
||||
middleware=[Middleware(WellKnownCacheControlMiddleware)],
|
||||
)
|
||||
client = TestClient(app)
|
||||
|
||||
well_known = client.get("/.well-known/oauth-authorization-server")
|
||||
assert well_known.status_code == 200
|
||||
assert well_known.headers["cache-control"] == "no-store, must-revalidate"
|
||||
assert well_known.headers["etag"] == f'"{_compute_scope_fingerprint()}"'
|
||||
assert sorted(well_known.headers.get_list("set-cookie")) == sorted(
|
||||
["a=1; Path=/; SameSite=lax", "b=2; Path=/; SameSite=lax"]
|
||||
)
|
||||
|
||||
regular = client.get("/health")
|
||||
assert regular.status_code == 200
|
||||
assert regular.headers["cache-control"] == "public, max-age=3600"
|
||||
assert "etag" not in regular.headers
|
||||
|
||||
extra = client.get("/.well-known/oauth-authorization-server-extra")
|
||||
assert extra.status_code == 200
|
||||
assert extra.headers["cache-control"] == "public, max-age=3600"
|
||||
assert "etag" not in extra.headers
|
||||
|
||||
|
||||
def test_configured_server_applies_no_cache_to_served_oauth_discovery_routes(
|
||||
monkeypatch,
|
||||
):
|
||||
monkeypatch.setenv("MCP_ENABLE_OAUTH21", "true")
|
||||
monkeypatch.setenv("GOOGLE_OAUTH_CLIENT_ID", "dummy-client")
|
||||
monkeypatch.setenv("GOOGLE_OAUTH_CLIENT_SECRET", "dummy-secret")
|
||||
monkeypatch.setenv("WORKSPACE_MCP_BASE_URI", "http://localhost")
|
||||
monkeypatch.setenv("WORKSPACE_MCP_PORT", "8000")
|
||||
monkeypatch.delenv("WORKSPACE_EXTERNAL_URL", raising=False)
|
||||
monkeypatch.setenv("EXTERNAL_OAUTH21_PROVIDER", "false")
|
||||
|
||||
import core.server as core_server
|
||||
from auth.oauth_config import reload_oauth_config
|
||||
|
||||
reload_oauth_config()
|
||||
core_server = importlib.reload(core_server)
|
||||
core_server.set_transport_mode("streamable-http")
|
||||
core_server.configure_server_for_http()
|
||||
|
||||
app = core_server.server.http_app(transport="streamable-http", path="/mcp")
|
||||
client = TestClient(app)
|
||||
|
||||
authorization_server = client.get("/.well-known/oauth-authorization-server")
|
||||
assert authorization_server.status_code == 200
|
||||
assert authorization_server.headers["cache-control"] == "no-store, must-revalidate"
|
||||
assert authorization_server.headers["etag"].startswith('"')
|
||||
assert authorization_server.headers["etag"].endswith('"')
|
||||
|
||||
protected_resource = client.get("/.well-known/oauth-protected-resource/mcp")
|
||||
assert protected_resource.status_code == 200
|
||||
assert protected_resource.headers["cache-control"] == "no-store, must-revalidate"
|
||||
assert protected_resource.headers["etag"].startswith('"')
|
||||
assert protected_resource.headers["etag"].endswith('"')
|
||||
|
||||
# Ensure we did not create a shadow route at the wrong path.
|
||||
wrong_path = client.get("/.well-known/oauth-protected-resource")
|
||||
assert wrong_path.status_code == 404
|
||||
Reference in New Issue
Block a user