Skip to content

Commit f6be674

Browse files
committed
Merge branch '1.0-dev' into ishymko/test-client-server-intergation
2 parents 4b0d7b7 + b1339c8 commit f6be674

7 files changed

Lines changed: 137 additions & 27 deletions

File tree

.github/workflows/linter.yaml

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -45,9 +45,7 @@ jobs:
4545
- name: Run Pyright (Pylance equivalent)
4646
id: pyright
4747
continue-on-error: true
48-
uses: jakebailey/pyright-action@v2
49-
with:
50-
pylance-version: latest-release
48+
run: uv run pyright src
5149

5250
- name: Run JSCPD for copy-paste detection
5351
id: jscpd

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ dev = [
127127
"trio",
128128
"uvicorn>=0.35.0",
129129
"pytest-timeout>=2.4.0",
130+
"pyright",
130131
"a2a-sdk[all]",
131132
]
132133

scripts/lint.sh

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
#!/bin/bash
2+
# Local replica of .github/workflows/linter.yaml (excluding jscpd copy-paste check)
3+
4+
# ANSI color codes for premium output
5+
RED='\033[0;31m'
6+
GREEN='\033[0;32m'
7+
YELLOW='\033[1;33m'
8+
BLUE='\033[0;34m'
9+
BOLD='\033[1m'
10+
NC='\033[0m' # No Color
11+
12+
FAILED=0
13+
14+
echo -e "${BLUE}${BOLD}=== A2A Python Fixed-and-Lint Suite ===${NC}"
15+
echo -e "Fixing formatting and linting issues, then verifying types...\n"
16+
17+
# 1. Ruff Linter (with fix)
18+
echo -e "${YELLOW}${BOLD}--- [1/4] Running Ruff Linter (fix) ---${NC}"
19+
if uv run ruff check --fix; then
20+
echo -e "${GREEN}✓ Ruff Linter passed (and fixed what it could)${NC}"
21+
else
22+
echo -e "${RED}✗ Ruff Linter failed${NC}"
23+
FAILED=1
24+
fi
25+
26+
# 2. Ruff Formatter
27+
echo -e "\n${YELLOW}${BOLD}--- [2/4] Running Ruff Formatter (apply) ---${NC}"
28+
if uv run ruff format; then
29+
echo -e "${GREEN}✓ Ruff Formatter applied${NC}"
30+
else
31+
echo -e "${RED}✗ Ruff Formatter failed${NC}"
32+
FAILED=1
33+
fi
34+
35+
# 3. MyPy Type Checker
36+
echo -e "\n${YELLOW}${BOLD}--- [3/4] Running MyPy Type Checker ---${NC}"
37+
if uv run mypy src; then
38+
echo -e "${GREEN}✓ MyPy passed${NC}"
39+
else
40+
echo -e "${RED}✗ MyPy failed${NC}"
41+
FAILED=1
42+
fi
43+
44+
# 4. Pyright Type Checker
45+
echo -e "\n${YELLOW}${BOLD}--- [4/4] Running Pyright ---${NC}"
46+
if uv run pyright; then
47+
echo -e "${GREEN}✓ Pyright passed${NC}"
48+
else
49+
echo -e "${RED}✗ Pyright failed${NC}"
50+
FAILED=1
51+
fi
52+
53+
echo -e "\n${BLUE}${BOLD}=========================================${NC}"
54+
if [ $FAILED -eq 0 ]; then
55+
echo -e "${GREEN}${BOLD}SUCCESS: All linting and formatting tasks complete!${NC}"
56+
exit 0
57+
else
58+
echo -e "${RED}${BOLD}FAILURE: One or more steps failed.${NC}"
59+
exit 1
60+
fi

src/a2a/server/apps/jsonrpc/jsonrpc_app.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,13 @@ async def _handle_requests(self, request: Request) -> Response: # noqa: PLR0911
365365
message='Batch requests are not supported'
366366
),
367367
)
368+
if body.get('jsonrpc') != '2.0':
369+
return self._generate_error_response(
370+
request_id,
371+
InvalidRequestError(
372+
message="Invalid request: 'jsonrpc' must be exactly '2.0'"
373+
),
374+
)
368375
except Exception as e:
369376
logger.exception('Failed to validate base JSON-RPC request')
370377
return self._generate_error_response(

tests/integration/test_client_server_integration.py

Lines changed: 29 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,33 @@ async def test_client_get_signed_base_and_extended_cards(
789789

790790

791791
@pytest.mark.asyncio
792-
async def test_jsonrpc_malformed_payload(jsonrpc_setup: TransportSetup) -> None:
792+
@pytest.mark.parametrize(
793+
'request_kwargs, expected_error_code',
794+
[
795+
pytest.param(
796+
{'content': 'not a json'},
797+
-32700, # Parse error
798+
id='invalid-json',
799+
),
800+
pytest.param(
801+
{
802+
'json': {
803+
'jsonrpc': '2.0',
804+
'method': 'SendMessage',
805+
'params': {'message': 'should be an object'},
806+
'id': 1,
807+
}
808+
},
809+
-32602, # Invalid params
810+
id='wrong-params-type',
811+
),
812+
],
813+
)
814+
async def test_jsonrpc_malformed_payload(
815+
jsonrpc_setup: TransportSetup,
816+
request_kwargs: dict[str, Any],
817+
expected_error_code: int,
818+
) -> None:
793819
"""Integration test to verify that JSON-RPC malformed payloads don't return 500."""
794820
client_obj = jsonrpc_setup.client
795821
assert isinstance(client_obj, BaseClient)
@@ -798,23 +824,9 @@ async def test_jsonrpc_malformed_payload(jsonrpc_setup: TransportSetup) -> None:
798824
client = transport.httpx_client
799825
url = transport.url
800826

801-
# 1. Invalid JSON
802-
response = await client.post(url, content='not a json')
803-
assert response.status_code == 200
804-
assert response.json()['error']['code'] == -32700 # Parse error
805-
806-
# 2. Wrong types in params
807-
response = await client.post(
808-
url,
809-
json={
810-
'jsonrpc': '2.0',
811-
'method': 'SendMessage',
812-
'params': {'message': 'should be an object'},
813-
'id': 1,
814-
},
815-
)
827+
response = await client.post(url, **request_kwargs)
816828
assert response.status_code == 200
817-
assert response.json()['error']['code'] == -32602 # Invalid params
829+
assert response.json()['error']['code'] == expected_error_code
818830

819831
await transport.close()
820832

tests/server/test_integration.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -703,7 +703,24 @@ def test_invalid_request_structure(client: TestClient):
703703
assert response.status_code == 200
704704
data = response.json()
705705
assert 'error' in data
706-
# The jsonrpc library returns MethodNotFoundError for unknown methods
706+
# The jsonrpc library returns InvalidRequestError for invalid requests format
707+
assert data['error']['code'] == InvalidRequestError().code
708+
709+
710+
def test_invalid_request_method(client: TestClient):
711+
"""Test handling an invalid request method."""
712+
response = client.post(
713+
'/',
714+
json={
715+
'jsonrpc': '2.0', # Missing or wrong required fields
716+
'id': '123',
717+
'method': 'foo/bar',
718+
},
719+
)
720+
assert response.status_code == 200
721+
data = response.json()
722+
assert 'error' in data
723+
# The jsonrpc library returns MethodNotFoundError for invalid request method
707724
assert data['error']['code'] == MethodNotFoundError().code
708725

709726

uv.lock

Lines changed: 21 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)