View Javadoc
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 }