docs: add Claude AI assistant rules and configuration
Add comprehensive rule documentation for AI-assisted development covering authentication surfaces, outbound-call safety invariants, and other project conventions to guide Claude's understanding of critical system behaviors.
This commit is contained in:
77
tests/test_oauth_metadata.py
Normal file
77
tests/test_oauth_metadata.py
Normal file
@@ -0,0 +1,77 @@
|
||||
"""
|
||||
OAuth discovery metadata tests (RFC 9728 / RFC 8414 / RFC 7591).
|
||||
|
||||
MCP clients that get a 401 from /mcp perform OAuth discovery. These
|
||||
endpoints are unauthenticated and served straight from main.app.
|
||||
"""
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
import main
|
||||
from config import get_settings
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
async def client():
|
||||
transport = httpx.ASGITransport(app=main.app)
|
||||
async with httpx.AsyncClient(transport=transport, base_url="http://test") as c:
|
||||
yield c
|
||||
|
||||
|
||||
class TestProtectedResourceMetadata:
|
||||
async def test_resource_advertises_mcp_path(self, client):
|
||||
resp = await client.get("/.well-known/oauth-protected-resource")
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
# mcp-remote verifies this matches the URL it connected to.
|
||||
assert body["resource"] == "http://test/mcp"
|
||||
assert body["authorization_servers"] == ["http://test"]
|
||||
|
||||
async def test_mcp_suffixed_variant(self, client):
|
||||
resp = await client.get("/.well-known/oauth-protected-resource/mcp")
|
||||
assert resp.status_code == 200
|
||||
assert resp.json()["resource"] == "http://test/mcp"
|
||||
|
||||
|
||||
class TestAuthorizationServerMetadata:
|
||||
async def test_advertises_casdoor_when_enabled(self, monkeypatch, client):
|
||||
monkeypatch.setattr(get_settings().casdoor, "enabled", True)
|
||||
monkeypatch.setattr(get_settings().casdoor, "endpoint", "https://id.example.test")
|
||||
resp = await client.get("/.well-known/oauth-authorization-server")
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
assert body["issuer"] == "https://id.example.test"
|
||||
assert body["jwks_uri"] == "https://id.example.test/.well-known/jwks"
|
||||
assert body["registration_endpoint"] == "http://test/register"
|
||||
|
||||
async def test_dev_mode_advertises_local(self, monkeypatch, client):
|
||||
monkeypatch.setattr(get_settings().casdoor, "enabled", False)
|
||||
resp = await client.get("/.well-known/oauth-authorization-server")
|
||||
assert resp.status_code == 200
|
||||
body = resp.json()
|
||||
assert body["issuer"] == "http://test"
|
||||
assert body["authorization_endpoint"] == "http://test/auth/login"
|
||||
|
||||
|
||||
class TestDynamicRegistration:
|
||||
async def test_registers_client(self, client):
|
||||
resp = await client.post(
|
||||
"/register",
|
||||
json={"redirect_uris": ["http://localhost/cb"], "client_name": "test"},
|
||||
)
|
||||
assert resp.status_code == 201
|
||||
body = resp.json()
|
||||
assert "client_id" in body
|
||||
assert body["redirect_uris"] == ["http://localhost/cb"]
|
||||
|
||||
async def test_rejects_missing_redirect_uris(self, client):
|
||||
resp = await client.post("/register", json={"client_name": "test"})
|
||||
assert resp.status_code == 400
|
||||
assert resp.json()["error"] == "invalid_redirect_uri"
|
||||
|
||||
async def test_rejects_non_json(self, client):
|
||||
resp = await client.post(
|
||||
"/register", content=b"not json", headers={"content-type": "application/json"}
|
||||
)
|
||||
assert resp.status_code == 400
|
||||
Reference in New Issue
Block a user