Skip to content

Commit 654cbd4

Browse files
committed
Test kind does not appear
1 parent c8dfcc9 commit 654cbd4

File tree

3 files changed

+263
-2
lines changed

3 files changed

+263
-2
lines changed

spec/src/main/java/io/a2a/json/JsonUtil.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -517,8 +517,8 @@ public void write(JsonWriter out, Message.Role value) throws java.io.IOException
517517
/**
518518
* Gson TypeAdapter for serializing and deserializing {@link Part} and its subclasses.
519519
* <p>
520-
* This adapter handles polymorphic deserialization based on the "kind" field, creating the
521-
* appropriate subclass instance (TextPart, FilePart, or DataPart).
520+
* This adapter handles polymorphic deserialization, creating the
521+
* appropriate subclass instance (TextPart, FilePart, or DataPart) based on available fields.
522522
* <p>
523523
* The adapter uses a two-pass approach: first reads the JSON as a tree to inspect the "kind"
524524
* field, then deserializes to the appropriate concrete type.
Lines changed: 257 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,257 @@
1+
package io.a2a.spec;
2+
3+
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
5+
import static org.junit.jupiter.api.Assertions.assertInstanceOf;
6+
import static org.junit.jupiter.api.Assertions.assertNotNull;
7+
import static org.junit.jupiter.api.Assertions.assertTrue;
8+
9+
import java.util.List;
10+
11+
import io.a2a.json.JsonProcessingException;
12+
import io.a2a.json.JsonUtil;
13+
import org.junit.jupiter.api.Test;
14+
15+
/**
16+
* Tests for StreamingEventKind serialization and deserialization.
17+
* <p>
18+
* Verifies that StreamingEventKind types (Task, Message, TaskStatusUpdateEvent, TaskArtifactUpdateEvent)
19+
* serialize using wrapper member names (e.g., {"task": {...}}) and do not contain "kind" fields.
20+
*/
21+
class StreamingEventKindSerializationTest {
22+
23+
@Test
24+
void testTaskSerialization() throws JsonProcessingException {
25+
// Create a Task
26+
Task task = Task.builder()
27+
.id("task-123")
28+
.contextId("context-456")
29+
.status(new TaskStatus(TaskState.SUBMITTED))
30+
.build();
31+
32+
// Serialize as StreamingEventKind
33+
String json = JsonUtil.toJson((StreamingEventKind) task);
34+
35+
// Verify JSON contains task wrapper, not "kind" field
36+
assertNotNull(json);
37+
assertTrue(json.contains("\"task\""));
38+
assertTrue(json.contains("\"id\":\"task-123\""));
39+
assertTrue(json.contains("\"state\":\"submitted\""));
40+
assertFalse(json.contains("\"kind\""));
41+
42+
// Deserialize back to StreamingEventKind
43+
StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class);
44+
45+
// Verify it's a Task
46+
assertInstanceOf(Task.class, deserialized);
47+
Task deserializedTask = (Task) deserialized;
48+
assertEquals(task.id(), deserializedTask.id());
49+
assertEquals(task.contextId(), deserializedTask.contextId());
50+
assertEquals(task.status().state(), deserializedTask.status().state());
51+
}
52+
53+
@Test
54+
void testMessageSerialization() throws JsonProcessingException {
55+
// Create a Message
56+
Message message = Message.builder()
57+
.role(Message.Role.USER)
58+
.parts(List.of(new TextPart("Hello, agent!")))
59+
.taskId("task-789")
60+
.messageId("msg-123")
61+
.contextId("context-456")
62+
.build();
63+
64+
// Serialize as StreamingEventKind
65+
String json = JsonUtil.toJson((StreamingEventKind) message);
66+
67+
// Verify JSON contains message wrapper, not "kind" field
68+
assertNotNull(json);
69+
assertTrue(json.contains("\"message\""));
70+
assertTrue(json.contains("\"taskId\":\"task-789\""));
71+
assertTrue(json.contains("\"role\":\"user\""));
72+
assertTrue(json.contains("Hello, agent!"));
73+
assertFalse(json.contains("\"kind\""));
74+
75+
// Deserialize back to StreamingEventKind
76+
StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class);
77+
78+
// Verify it's a Message
79+
assertInstanceOf(Message.class, deserialized);
80+
Message deserializedMessage = (Message) deserialized;
81+
assertEquals(message.taskId(), deserializedMessage.taskId());
82+
assertEquals(message.role(), deserializedMessage.role());
83+
assertEquals(message.parts().size(), deserializedMessage.parts().size());
84+
}
85+
86+
@Test
87+
void testTaskStatusUpdateEventSerialization() throws JsonProcessingException {
88+
// Create a TaskStatusUpdateEvent
89+
TaskStatusUpdateEvent statusEvent = TaskStatusUpdateEvent.builder()
90+
.taskId("task-abc")
91+
.contextId("context-def")
92+
.status(new TaskStatus(TaskState.WORKING))
93+
.isFinal(false)
94+
.build();
95+
96+
// Serialize as StreamingEventKind
97+
String json = JsonUtil.toJson((StreamingEventKind) statusEvent);
98+
99+
// Verify JSON contains statusUpdate wrapper, not "kind" field
100+
assertNotNull(json);
101+
assertTrue(json.contains("\"statusUpdate\""));
102+
assertTrue(json.contains("\"taskId\":\"task-abc\""));
103+
assertTrue(json.contains("\"state\":\"working\""));
104+
assertTrue(json.contains("\"final\":false"));
105+
assertFalse(json.contains("\"kind\""));
106+
107+
// Deserialize back to StreamingEventKind
108+
StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class);
109+
110+
// Verify it's a TaskStatusUpdateEvent
111+
assertInstanceOf(TaskStatusUpdateEvent.class, deserialized);
112+
TaskStatusUpdateEvent deserializedEvent = (TaskStatusUpdateEvent) deserialized;
113+
assertEquals(statusEvent.taskId(), deserializedEvent.taskId());
114+
assertEquals(statusEvent.status().state(), deserializedEvent.status().state());
115+
assertEquals(statusEvent.isFinal(), deserializedEvent.isFinal());
116+
}
117+
118+
@Test
119+
void testTaskArtifactUpdateEventSerialization() throws JsonProcessingException {
120+
// Create a TaskArtifactUpdateEvent
121+
Artifact artifact = Artifact.builder()
122+
.artifactId("artifact-xyz")
123+
.name("Test Artifact")
124+
.parts(List.of(new TextPart("Artifact content")))
125+
.build();
126+
127+
TaskArtifactUpdateEvent artifactEvent = TaskArtifactUpdateEvent.builder()
128+
.taskId("task-123")
129+
.contextId("context-456")
130+
.artifact(artifact)
131+
.build();
132+
133+
// Serialize as StreamingEventKind
134+
String json = JsonUtil.toJson((StreamingEventKind) artifactEvent);
135+
136+
// Verify JSON contains artifactUpdate wrapper, not "kind" field
137+
assertNotNull(json);
138+
assertTrue(json.contains("\"artifactUpdate\""));
139+
assertTrue(json.contains("\"taskId\":\"task-123\""));
140+
assertTrue(json.contains("\"artifactId\":\"artifact-xyz\""));
141+
assertTrue(json.contains("Artifact content"));
142+
assertFalse(json.contains("\"kind\""));
143+
144+
// Deserialize back to StreamingEventKind
145+
StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class);
146+
147+
// Verify it's a TaskArtifactUpdateEvent
148+
assertInstanceOf(TaskArtifactUpdateEvent.class, deserialized);
149+
TaskArtifactUpdateEvent deserializedEvent = (TaskArtifactUpdateEvent) deserialized;
150+
assertEquals(artifactEvent.taskId(), deserializedEvent.taskId());
151+
assertEquals(artifactEvent.artifact().artifactId(), deserializedEvent.artifact().artifactId());
152+
}
153+
154+
@Test
155+
void testUnwrappedTaskDeserialization() throws JsonProcessingException {
156+
// Test that unwrapped Task format (direct deserialization) still works
157+
String json = """
158+
{
159+
"id": "task-unwrapped",
160+
"contextId": "context-999",
161+
"status": {
162+
"state": "completed"
163+
}
164+
}
165+
""";
166+
167+
// Deserialize as StreamingEventKind
168+
StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class);
169+
170+
// Should successfully deserialize as Task
171+
assertInstanceOf(Task.class, deserialized);
172+
Task task = (Task) deserialized;
173+
assertEquals("task-unwrapped", task.id());
174+
assertEquals("context-999", task.contextId());
175+
assertEquals(TaskState.COMPLETED, task.status().state());
176+
}
177+
178+
@Test
179+
void testUnwrappedMessageDeserialization() throws JsonProcessingException {
180+
// Test that unwrapped Message format (direct deserialization) still works
181+
String json = """
182+
{
183+
"role": "agent",
184+
"parts": [
185+
{
186+
"text": "Unwrapped message"
187+
}
188+
],
189+
"messageId": "msg-unwrapped",
190+
"taskId": "task-999"
191+
}
192+
""";
193+
194+
// Deserialize as StreamingEventKind
195+
StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class);
196+
197+
// Should successfully deserialize as Message
198+
assertInstanceOf(Message.class, deserialized);
199+
Message message = (Message) deserialized;
200+
assertEquals("msg-unwrapped", message.messageId());
201+
assertEquals("task-999", message.taskId());
202+
assertEquals(Message.Role.AGENT, message.role());
203+
}
204+
205+
@Test
206+
void testUnwrappedTaskStatusUpdateEventDeserialization() throws JsonProcessingException {
207+
// Test that unwrapped TaskStatusUpdateEvent format still works
208+
String json = """
209+
{
210+
"taskId": "task-status-unwrapped",
211+
"contextId": "context-999",
212+
"status": {
213+
"state": "working"
214+
},
215+
"final": false
216+
}
217+
""";
218+
219+
// Deserialize as StreamingEventKind
220+
StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class);
221+
222+
// Should successfully deserialize as TaskStatusUpdateEvent
223+
assertInstanceOf(TaskStatusUpdateEvent.class, deserialized);
224+
TaskStatusUpdateEvent event = (TaskStatusUpdateEvent) deserialized;
225+
assertEquals("task-status-unwrapped", event.taskId());
226+
assertEquals(TaskState.WORKING, event.status().state());
227+
assertFalse(event.isFinal());
228+
}
229+
230+
@Test
231+
void testUnwrappedTaskArtifactUpdateEventDeserialization() throws JsonProcessingException {
232+
// Test that unwrapped TaskArtifactUpdateEvent format still works
233+
String json = """
234+
{
235+
"taskId": "task-artifact-unwrapped",
236+
"contextId": "context-999",
237+
"artifact": {
238+
"artifactId": "artifact-unwrapped",
239+
"parts": [
240+
{
241+
"text": "Unwrapped artifact"
242+
}
243+
]
244+
}
245+
}
246+
""";
247+
248+
// Deserialize as StreamingEventKind
249+
StreamingEventKind deserialized = JsonUtil.fromJson(json, StreamingEventKind.class);
250+
251+
// Should successfully deserialize as TaskArtifactUpdateEvent
252+
assertInstanceOf(TaskArtifactUpdateEvent.class, deserialized);
253+
TaskArtifactUpdateEvent event = (TaskArtifactUpdateEvent) deserialized;
254+
assertEquals("task-artifact-unwrapped", event.taskId());
255+
assertEquals("artifact-unwrapped", event.artifact().artifactId());
256+
}
257+
}

spec/src/test/java/io/a2a/spec/TaskSerializationTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package io.a2a.spec;
22

33
import static org.junit.jupiter.api.Assertions.assertEquals;
4+
import static org.junit.jupiter.api.Assertions.assertFalse;
45
import static org.junit.jupiter.api.Assertions.assertNotNull;
56
import static org.junit.jupiter.api.Assertions.assertNull;
67
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -247,6 +248,7 @@ void testTaskWithFilePartBytes() throws JsonProcessingException {
247248

248249
// Verify JSON contains file part data (v1.0 format uses member name "file", not "kind")
249250
assertTrue(json.contains("\"file\""));
251+
assertFalse(json.contains("\"kind\""));
250252
assertTrue(json.contains("document.pdf"));
251253
assertTrue(json.contains("application/pdf"));
252254

@@ -284,6 +286,7 @@ void testTaskWithFilePartUri() throws JsonProcessingException {
284286

285287
// Verify JSON contains URI
286288
assertTrue(json.contains("https://example.com/photo.png"));
289+
assertFalse(json.contains("\"kind\"")); // Removed in spec 1.0
287290

288291
// Deserialize
289292
Task deserialized = JsonUtil.fromJson(json, Task.class);
@@ -318,6 +321,7 @@ void testTaskWithDataPart() throws JsonProcessingException {
318321

319322
// Verify JSON contains data part (v1.0 format uses member name "data", not "kind")
320323
assertTrue(json.contains("\"data\""));
324+
assertFalse(json.contains("\"kind\""));
321325
assertTrue(json.contains("temperature"));
322326

323327
// Deserialize

0 commit comments

Comments
 (0)