Skip to content

Commit 75d4b98

Browse files
Niravkumar-Patelnirav patel
andauthored
fix: Task.artifacts and Task.history null deserialization (fixes #257) (#258)
# Description - Initialize artifacts and history fields with empty lists when null - Add comprehensive unit tests for deserialization scenarios - Ensures type safety for optional array fields per A2A specification Fixes #257 Co-authored-by: nirav patel <[email protected]>
1 parent 8903e3d commit 75d4b98

File tree

2 files changed

+92
-2
lines changed

2 files changed

+92
-2
lines changed

spec/src/main/java/io/a2a/spec/Task.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,8 +51,8 @@ public Task(@JsonProperty("id") String id, @JsonProperty("contextId") String con
5151
this.id = id;
5252
this.contextId = contextId;
5353
this.status = status;
54-
this.artifacts = artifacts;
55-
this.history = history;
54+
this.artifacts = artifacts != null ? List.copyOf(artifacts) : List.of();
55+
this.history = history != null ? List.copyOf(history) : List.of();
5656
this.metadata = metadata;
5757
this.kind = kind;
5858
}
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
package io.a2a.spec;
2+
3+
import com.fasterxml.jackson.databind.ObjectMapper;
4+
import org.junit.jupiter.api.Test;
5+
6+
import static org.junit.jupiter.api.Assertions.*;
7+
8+
public class TaskDeserializationTest {
9+
private final ObjectMapper objectMapper = new ObjectMapper();
10+
11+
@Test
12+
void testTaskWithMissingHistoryAndArtifacts() throws Exception {
13+
// JSON without history and artifacts fields (common server response)
14+
String json = """
15+
{
16+
"id": "task-123",
17+
"contextId": "context-456",
18+
"status": {
19+
"state": "completed"
20+
},
21+
"kind": "task"
22+
}
23+
""";
24+
25+
Task task = objectMapper.readValue(json, Task.class);
26+
27+
assertNotNull(task.getHistory(), "history should not be null");
28+
assertNotNull(task.getArtifacts(), "artifacts should not be null");
29+
30+
assertTrue(task.getHistory().isEmpty(), "history should be empty list when not provided");
31+
assertTrue(task.getArtifacts().isEmpty(), "artifacts should be empty list when not provided");
32+
}
33+
34+
@Test
35+
void testTaskWithExplicitNullValues() throws Exception {
36+
// JSON with explicit null values
37+
String json = """
38+
{
39+
"id": "task-123",
40+
"contextId": "context-456",
41+
"status": {
42+
"state": "completed"
43+
},
44+
"history": null,
45+
"artifacts": null,
46+
"kind": "task"
47+
}
48+
""";
49+
50+
Task task = objectMapper.readValue(json, Task.class);
51+
52+
// Should never be null even with explicit null in JSON
53+
assertNotNull(task.getHistory(), "history should not be null even when JSON contains null");
54+
assertNotNull(task.getArtifacts(), "artifacts should not be null even when JSON contains null");
55+
56+
assertTrue(task.getHistory().isEmpty());
57+
assertTrue(task.getArtifacts().isEmpty());
58+
}
59+
60+
@Test
61+
void testTaskWithPopulatedArrays() throws Exception {
62+
String json = """
63+
{
64+
"id": "task-123",
65+
"contextId": "context-456",
66+
"status": {
67+
"state": "completed"
68+
},
69+
"history": [
70+
{
71+
"role": "user",
72+
"parts": [{"kind": "text", "text": "hello"}],
73+
"messageId": "msg-1",
74+
"kind": "message"
75+
}
76+
],
77+
"artifacts": [],
78+
"kind": "task"
79+
}
80+
""";
81+
82+
Task task = objectMapper.readValue(json, Task.class);
83+
84+
assertNotNull(task.getHistory());
85+
assertEquals(1, task.getHistory().size());
86+
87+
assertNotNull(task.getArtifacts());
88+
assertTrue(task.getArtifacts().isEmpty());
89+
}
90+
}

0 commit comments

Comments
 (0)