/ tests / sam-test-infrastructure / src / sam_test_infrastructure / a2a_validator / test_validator.py
test_validator.py
1 """ 2 Unit tests for the A2AMessageValidator. 3 """ 4 5 import pytest 6 from sam_test_infrastructure.a2a_validator.validator import A2AMessageValidator 7 8 9 @pytest.fixture 10 def validator() -> A2AMessageValidator: 11 """Provides an instance of the A2AMessageValidator.""" 12 return A2AMessageValidator() 13 14 15 def test_validator_initialization(validator: A2AMessageValidator): 16 """Tests that the validator initializes correctly and loads the schema.""" 17 assert validator.active is False 18 assert validator.schema is not None 19 assert "definitions" in validator.schema 20 assert validator.validator is not None 21 22 23 def test_valid_send_message_request(validator: A2AMessageValidator): 24 """Tests validation of a valid SendMessageRequest.""" 25 payload = { 26 "jsonrpc": "2.0", 27 "id": "req-1", 28 "method": "message/send", 29 "params": { 30 "message": { 31 "role": "user", 32 "messageId": "msg-1", 33 "kind": "message", 34 "parts": [{"kind": "text", "text": "Hello"}], 35 } 36 }, 37 } 38 validator.validate_message(payload, "a2a/v1/agent/request/TestAgent") 39 40 41 def test_valid_task_response(validator: A2AMessageValidator): 42 """Tests validation of a valid response containing a Task object.""" 43 payload = { 44 "jsonrpc": "2.0", 45 "id": "req-1", 46 "result": { 47 "id": "task-1", 48 "contextId": "session-1", 49 "kind": "task", 50 "status": { 51 "state": "completed", 52 "message": { 53 "role": "agent", 54 "messageId": "msg-2", 55 "kind": "message", 56 "parts": [{"kind": "text", "text": "Done"}], 57 }, 58 }, 59 }, 60 } 61 validator.validate_message(payload, "a2a/v1/gateway/response/gw-1/task-1") 62 63 64 def test_valid_status_update_response(validator: A2AMessageValidator): 65 """Tests validation of a valid response containing a TaskStatusUpdateEvent.""" 66 payload = { 67 "jsonrpc": "2.0", 68 "id": "req-1", 69 "result": { 70 "kind": "status-update", 71 "taskId": "task-1", 72 "contextId": "session-1", 73 "final": False, 74 "status": {"state": "working"}, 75 }, 76 } 77 validator.validate_message(payload, "a2a/v1/gateway/status/gw-1/task-1") 78 79 80 def test_invalid_request_missing_jsonrpc(validator: A2AMessageValidator): 81 """Tests that a request missing 'jsonrpc' fails validation.""" 82 payload = { 83 "id": "req-1", 84 "method": "message/send", 85 "params": { 86 "message": { 87 "role": "user", 88 "messageId": "msg-1", 89 "kind": "message", 90 "parts": [{"kind": "text", "text": "Hello"}], 91 } 92 }, 93 } 94 with pytest.raises(pytest.fail.Exception, match="'jsonrpc' is a required property"): 95 validator.validate_message(payload, "a2a/v1/agent/request/TestAgent") 96 97 98 def test_invalid_request_bad_method(validator: A2AMessageValidator): 99 """Tests that a request with an unknown method falls back to generic validation.""" 100 payload = { 101 "jsonrpc": "2.0", 102 "id": "req-1", 103 "method": "non/existent/method", 104 "params": {}, 105 } 106 # This should not fail because it validates against the generic request schema. 107 validator.validate_message(payload, "a2a/v1/agent/request/TestAgent") 108 109 110 def test_invalid_response_both_result_and_error(validator: A2AMessageValidator): 111 """Tests that a response with both 'result' and 'error' fails validation.""" 112 payload = { 113 "jsonrpc": "2.0", 114 "id": "req-1", 115 "result": { 116 "id": "task-1", 117 "contextId": "session-1", 118 "kind": "task", 119 "status": {"state": "completed"}, 120 }, 121 "error": {"code": -32000, "message": "An error"}, 122 } 123 with pytest.raises( 124 pytest.fail.Exception, match="'result' and 'error' are mutually exclusive" 125 ): 126 validator.validate_message(payload, "a2a/v1/gateway/response/gw-1/task-1") 127 128 129 def test_invalid_task_missing_id(validator: A2AMessageValidator): 130 """Tests that a Task object in a result missing a required field fails validation.""" 131 payload = { 132 "jsonrpc": "2.0", 133 "id": "req-1", 134 "result": { 135 # "id": "task-1", <-- Missing required field 136 "contextId": "session-1", 137 "kind": "task", 138 "status": {"state": "completed"}, 139 }, 140 } 141 with pytest.raises(pytest.fail.Exception, match="'id' is a required property"): 142 validator.validate_message(payload, "a2a/v1/gateway/response/gw-1/task-1")