1 package fr.tiogars.chat.client;
2
3 import static org.assertj.core.api.Assertions.assertThat;
4 import static org.assertj.core.api.Assertions.assertThatThrownBy;
5
6 import java.io.IOException;
7 import java.time.Instant;
8 import java.util.List;
9
10 import org.junit.jupiter.api.AfterEach;
11 import org.junit.jupiter.api.BeforeEach;
12 import org.junit.jupiter.api.Test;
13
14 import okhttp3.mockwebserver.MockResponse;
15 import okhttp3.mockwebserver.MockWebServer;
16 import okhttp3.mockwebserver.RecordedRequest;
17
18 public class TaskClientTest {
19
20 private static final String TEMPLATE_JSON = """
21 {
22 "id": 42,
23 "code": "customers",
24 "label": "Customers",
25 "description": null,
26 "createdDate": "2026-08-29T12:00:00Z",
27 "lastModifiedDate": "2026-08-30T12:00:00Z",
28 "createdBy": "creator",
29 "lastModifiedBy": "editor"
30 }
31 """;
32 private static final String TEMPLATE_LIST_JSON = "[" + TEMPLATE_JSON + "]";
33
34 private MockWebServer server;
35 private TaskClient client;
36
37 @BeforeEach
38 public void startServer() throws IOException {
39 server = new MockWebServer();
40 server.start();
41 client = new TaskClient(server.url("/").uri());
42 }
43
44 @AfterEach
45 public void stopServer() throws IOException {
46 server.shutdown();
47 }
48
49 @Test
50 public void create_shouldPostRequestAndReturnTemplate() throws Exception {
51 enqueueJson(201, TEMPLATE_JSON);
52
53 Task template = client.create(new CreateTaskRequest("customers", "Customers", null));
54
55 assertThat(template).isEqualTo(expectedTemplate());
56 RecordedRequest request = server.takeRequest();
57 assertThat(request.getMethod()).isEqualTo("POST");
58 assertThat(request.getPath()).isEqualTo("/api/v1/task");
59 assertThat(request.getHeader("Content-Type")).isEqualTo("application/json");
60 assertThat(request.getBody().readUtf8())
61 .isEqualTo("{\"code\":\"customers\",\"label\":\"Customers\",\"description\":null,"
62 + "\"status\":\"TODO\",\"priority\":\"NORMAL\",\"dueDate\":null,\"assignedTo\":null,"
63 + "\"tagIds\":[],\"nomenclatureId\":null}");
64 }
65
66 @Test
67 public void findAll_shouldGetCollection() throws Exception {
68 enqueueJson(200, TEMPLATE_LIST_JSON);
69
70 assertThat(client.findAll()).containsExactly(expectedTemplate());
71 assertRequest("GET", "/api/v1/task", "application/json");
72 }
73
74 @Test
75 public void findById_shouldGetTemplate() throws Exception {
76 enqueueJson(200, TEMPLATE_JSON);
77
78 assertThat(client.findById(42)).isEqualTo(expectedTemplate());
79 assertRequest("GET", "/api/v1/task/42", "application/json");
80 }
81
82 @Test
83 public void findById_acceptsOlderResponsesWithoutAuditActors() throws Exception {
84 enqueueJson(200, """
85 {"id":42,"code":"customers","label":"Customers","description":null,
86 "createdDate":"2026-08-29T12:00:00Z","lastModifiedDate":"2026-08-30T12:00:00Z"}
87 """);
88
89 assertThat(client.findById(42)).isEqualTo(new Task(
90 42L, "customers", "Customers", null,
91 Instant.parse("2026-08-29T12:00:00Z"), Instant.parse("2026-08-30T12:00:00Z")));
92 }
93
94 @Test
95 void taskAttributesUseIsoDatesAndRoundtrip() throws Exception {
96 enqueueJson(201, """
97 {"id":42,"code":"TASK","label":"Task","description":null,
98 "status":"IN_PROGRESS","priority":"URGENT","dueDate":"2026-12-31",
99 "assignedTo":"alice","tagIds":[2,7],"nomenclatureId":5,
100 "createdDate":"2026-08-29T12:00:00Z","lastModifiedDate":"2026-08-30T12:00:00Z",
101 "createdBy":"creator","lastModifiedBy":"editor"}
102 """);
103 var request = new CreateTaskRequest("TASK", "Task", null, TaskStatus.IN_PROGRESS, TaskPriority.URGENT,
104 java.time.LocalDate.parse("2026-12-31"), "alice", List.of(2L, 7L), 5L);
105 Task result = client.create(request);
106 assertThat(result.status()).isEqualTo(request.status());
107 assertThat(result.priority()).isEqualTo(request.priority());
108 assertThat(result.dueDate()).isEqualTo(request.dueDate());
109 assertThat(result.assignedTo()).isEqualTo(request.assignedTo());
110 assertThat(result.tagIds()).isEqualTo(request.tagIds());
111 assertThat(result.nomenclatureId()).isEqualTo(request.nomenclatureId());
112 assertThat(server.takeRequest().getBody().readUtf8()).contains("\"dueDate\":\"2026-12-31\"");
113 }
114
115 @Test
116 public void update_shouldPutRequestAndReturnTemplate() throws Exception {
117 enqueueJson(200, TEMPLATE_JSON);
118
119 assertThat(client.update(42, new UpdateTaskRequest("Customers", null)))
120 .isEqualTo(expectedTemplate());
121 RecordedRequest request = server.takeRequest();
122 assertThat(request.getMethod()).isEqualTo("PUT");
123 assertThat(request.getPath()).isEqualTo("/api/v1/task/42");
124 assertThat(request.getBody().readUtf8())
125 .isEqualTo("{\"label\":\"Customers\",\"description\":null,"
126 + "\"status\":\"TODO\",\"priority\":\"NORMAL\",\"dueDate\":null,\"assignedTo\":null,"
127 + "\"tagIds\":[],\"nomenclatureId\":null}");
128 }
129
130 @Test
131 public void delete_shouldSendDeleteRequest() throws Exception {
132 server.enqueue(new MockResponse().setResponseCode(204));
133
134 client.delete(42);
135
136 assertRequest("DELETE", "/api/v1/task/42", null);
137 }
138
139 @Test
140 public void seed_shouldPostRequestsAndReturnTemplates() throws Exception {
141 enqueueJson(201, TEMPLATE_LIST_JSON);
142
143 List<Task> templates = client.seed(
144 List.of(new CreateTaskRequest("customers", "Customers", null)));
145
146 assertThat(templates).containsExactly(expectedTemplate());
147 RecordedRequest request = server.takeRequest();
148 assertThat(request.getMethod()).isEqualTo("POST");
149 assertThat(request.getPath()).isEqualTo("/api/v1/task/seed");
150 assertThat(request.getBody().readUtf8()).startsWith("[{\"code\":\"customers\"");
151 }
152
153 @Test
154 public void importJson_shouldPostRequestsAndReturnTemplates() throws Exception {
155 enqueueJson(201, TEMPLATE_LIST_JSON);
156
157 assertThat(client.importJson(List.of(new CreateTaskRequest("customers", "Customers", null))))
158 .containsExactly(expectedTemplate());
159 RecordedRequest request = server.takeRequest();
160 assertThat(request.getMethod()).isEqualTo("POST");
161 assertThat(request.getPath()).isEqualTo("/api/v1/task/import.json");
162 assertThat(request.getHeader("Content-Type")).isEqualTo("application/json");
163 }
164
165 @Test
166 public void importCsv_shouldPostCsvAndReturnTemplates() throws Exception {
167 enqueueJson(201, TEMPLATE_LIST_JSON);
168 String csv = "code,label,description\ncustomers,Customers,";
169
170 assertThat(client.importCsv(csv)).containsExactly(expectedTemplate());
171 RecordedRequest request = server.takeRequest();
172 assertThat(request.getMethod()).isEqualTo("POST");
173 assertThat(request.getPath()).isEqualTo("/api/v1/task/import.csv");
174 assertThat(request.getHeader("Content-Type")).isEqualTo("text/csv");
175 assertThat(request.getBody().readUtf8()).isEqualTo(csv);
176 }
177
178 @Test
179 public void exportJson_shouldGetTemplates() throws Exception {
180 enqueueJson(200, TEMPLATE_LIST_JSON);
181
182 assertThat(client.exportJson()).containsExactly(expectedTemplate());
183 assertRequest("GET", "/api/v1/task/export.json", "application/json");
184 }
185
186 @Test
187 public void exportCsv_shouldGetCsvDocument() throws Exception {
188 String csv = "id,code,label,description,createdDate,lastModifiedDate\n";
189 server.enqueue(new MockResponse().setResponseCode(200).setHeader("Content-Type", "text/csv").setBody(csv));
190
191 assertThat(client.exportCsv()).isEqualTo(csv);
192 assertRequest("GET", "/api/v1/task/export.csv", "text/csv");
193 }
194
195 @Test
196 public void request_shouldThrowTaskExceptionOnUnexpectedStatus() {
197 server.enqueue(new MockResponse().setResponseCode(404));
198
199 assertThatThrownBy(() -> client.findById(99))
200 .isInstanceOf(TaskException.class)
201 .hasMessage("Expected HTTP 200 but received 404");
202 }
203
204 private void enqueueJson(int status, String body) {
205 server.enqueue(new MockResponse()
206 .setResponseCode(status)
207 .setHeader("Content-Type", "application/json")
208 .setBody(body));
209 }
210
211 private void assertRequest(String method, String path, String accept) throws InterruptedException {
212 RecordedRequest request = server.takeRequest();
213 assertThat(request.getMethod()).isEqualTo(method);
214 assertThat(request.getPath()).isEqualTo(path);
215 if (accept != null) {
216 assertThat(request.getHeader("Accept")).isEqualTo(accept);
217 }
218 }
219
220 private Task expectedTemplate() {
221 return new Task(
222 42L,
223 "customers",
224 "Customers",
225 null,
226 Instant.parse("2026-08-29T12:00:00Z"),
227 Instant.parse("2026-08-30T12:00:00Z"),
228 "creator",
229 "editor");
230 }
231 }