Skip to content
Closed
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/a2a/types.py
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This file shouldn't be manually edited. The alias should be added automatically from BaseModel in https://github.com/a2aproject/a2a-python/blob/main/src/a2a/_base.py

Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class APIKeySecurityScheme(A2ABaseModel):
"""
An optional description for the security scheme.
"""
in_: In
in_: In = Field(..., alias='in')
"""
The location of the API key.
"""
Expand Down
66 changes: 66 additions & 0 deletions tests/server/apps/rest/test_serialization.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
from unittest import mock

import pytest

from httpx import ASGITransport, AsyncClient

from a2a.server.apps.rest.fastapi_app import A2ARESTFastAPIApplication
from a2a.types import (
APIKeySecurityScheme,
AgentCapabilities,
AgentCard,
In,
SecurityScheme,
)


@pytest.fixture
def agent_card_with_api_key() -> AgentCard:
api_key_scheme_data = {
'type': 'apiKey',
'name': 'X-API-KEY',
'in': 'header',
}
api_key_scheme = APIKeySecurityScheme.model_validate(api_key_scheme_data)

return AgentCard(
name='APIKeyAgent',
description='An agent that uses API Key auth.',
url='http://example.com/apikey-agent',
version='1.0.0',
capabilities=AgentCapabilities(),
default_input_modes=['text/plain'],
default_output_modes=['text/plain'],
skills=[],
security_schemes={'api_key_auth': SecurityScheme(root=api_key_scheme)},
security=[{'api_key_auth': []}],
)


@pytest.mark.anyio
async def test_rest_agent_card_with_api_key_scheme_alias(
agent_card_with_api_key: AgentCard,
):
"""Ensures REST agent card serialization uses the 'in' alias."""
handler = mock.AsyncMock()
app_instance = A2ARESTFastAPIApplication(agent_card_with_api_key, handler)
app = app_instance.build(
agent_card_url='/.well-known/agent.json', rpc_url=''
)

async with AsyncClient(
transport=ASGITransport(app=app), base_url='http://test'
) as client:
response = await client.get('/.well-known/agent.json')

assert response.status_code == 200
response_data = response.json()

security_scheme_json = response_data['securitySchemes']['api_key_auth']
assert 'in' in security_scheme_json
assert security_scheme_json['in'] == 'header'
assert 'in_' not in security_scheme_json

parsed_card = AgentCard.model_validate(response_data)
parsed_scheme_wrapper = parsed_card.security_schemes['api_key_auth']
assert parsed_scheme_wrapper.root.in_ == In.header
13 changes: 13 additions & 0 deletions tests/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,19 @@ def test_security_scheme_valid():
assert scheme.root.name == 'X-API-KEY'


def test_security_scheme_accepts_in_field_name():
scheme = SecurityScheme.model_validate(
{
'type': 'apiKey',
'in_': 'header',
'name': 'X-API-KEY',
}
)
assert isinstance(scheme.root, APIKeySecurityScheme)
assert scheme.root.in_ == In.header
assert scheme.model_dump(mode='json', exclude_none=True)['in'] == 'header'
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

low

For better test coverage and maintainability, you could use a parameterized test to verify that both in and in_ are handled correctly during deserialization, and that the model always serializes to in. This would combine the logic of this test and test_security_scheme_valid into a single, more comprehensive test case.

Suggested change
def test_security_scheme_accepts_in_field_name():
scheme = SecurityScheme.model_validate(
{
'type': 'apiKey',
'in_': 'header',
'name': 'X-API-KEY',
}
)
assert isinstance(scheme.root, APIKeySecurityScheme)
assert scheme.root.in_ == In.header
assert scheme.model_dump(mode='json', exclude_none=True)['in'] == 'header'
@pytest.mark.parametrize("in_field_name", ["in", "in_"])
def test_security_scheme_in_field_handling(in_field_name: str):
scheme_data = {
'type': 'apiKey',
'name': 'X-API-KEY',
in_field_name: 'header',
}
scheme = SecurityScheme.model_validate(scheme_data)
assert isinstance(scheme.root, APIKeySecurityScheme)
assert scheme.root.in_ == In.header
serialized_data = scheme.model_dump(mode='json', exclude_none=True)
assert serialized_data.get('in') == 'header'
assert 'in_' not in serialized_data



def test_security_scheme_invalid():
with pytest.raises(ValidationError):
APIKeySecurityScheme(
Expand Down
Loading