|
1 | 1 | package ca.bc.gov.nrs.ilcr.schedule10; |
2 | 2 |
|
| 3 | +import static org.hamcrest.Matchers.contains; |
3 | 4 | import static org.hamcrest.Matchers.hasItem; |
4 | 5 | import static org.hamcrest.Matchers.hasSize; |
5 | 6 | import static org.hamcrest.Matchers.is; |
@@ -329,6 +330,76 @@ void codeLists_areYearFilteredAndOmitRemovedFields() throws Exception { |
329 | 330 | .andExpect(jsonPath("$.codeLists.soilMoistureCodes").doesNotExist()); |
330 | 331 | } |
331 | 332 |
|
| 333 | + @Test |
| 334 | + @DisplayName("the TSA and supply-block lists are served, year-filtered, and not transposed") |
| 335 | + void tsaAndSupplyBlockLists_areServedAndYearFiltered() throws Exception { |
| 336 | + // Both lists shipped with NO coverage at all: deleting either @Query body, or swapping the two |
| 337 | + // toCodes(...) arguments in the assembler -- they are both List<CodeDescriptionDto>, so it |
| 338 | + // compiles -- left the suite green. Each assertion below fails under one of those mutations. |
| 339 | + mockMvc.perform(get(ENDPOINT).param("millId", "710").param("year", "2021") |
| 340 | + .accept(MediaType.APPLICATION_JSON)) |
| 341 | + .andExpect(status().isOk()) |
| 342 | + // Present and populated, so deleting a @Query body reds this. |
| 343 | + .andExpect(jsonPath("$.codeLists.tsaNumbers").isArray()) |
| 344 | + .andExpect(jsonPath("$.codeLists.supplyBlocks").isArray()) |
| 345 | + .andExpect(jsonPath("$.codeLists.tsaNumbers[?(@.code == '01')]", hasSize(1))) |
| 346 | + .andExpect(jsonPath("$.codeLists.tsaNumbers[?(@.code == '16')]", hasSize(1))) |
| 347 | + .andExpect(jsonPath("$.codeLists.supplyBlocks[?(@.code == '01A')]", hasSize(1))) |
| 348 | + .andExpect(jsonPath("$.codeLists.supplyBlocks[?(@.code == '16G')]", hasSize(1))) |
| 349 | + // The descriptions are what the control DISPLAYS, so they are part of the contract. |
| 350 | + .andExpect(jsonPath("$.codeLists.tsaNumbers[?(@.code == '01')].description", |
| 351 | + contains("Arrow TSA"))) |
| 352 | + .andExpect(jsonPath("$.codeLists.supplyBlocks[?(@.code == '01A')].description", |
| 353 | + contains("Arrow TSA Block A"))) |
| 354 | + // TRANSPOSITION GUARD: a TSA code must never appear in the block list, or the reverse. A |
| 355 | + // swap of the two assembler arguments compiles and is caught only here. |
| 356 | + .andExpect(jsonPath("$.codeLists.tsaNumbers[?(@.code == '01A')]", hasSize(0))) |
| 357 | + .andExpect(jsonPath("$.codeLists.tsaNumbers[?(@.code == '16G')]", hasSize(0))) |
| 358 | + .andExpect(jsonPath("$.codeLists.supplyBlocks[?(@.code == '01')]", hasSize(0))) |
| 359 | + .andExpect(jsonPath("$.codeLists.supplyBlocks[?(@.code == '16')]", hasSize(0))) |
| 360 | + // V20260821 seeds '90','Retired TSA' expiring 2010-12-31 stating it exists "to pin that the |
| 361 | + // year filter drops a code". Nothing pinned it until now: no stored 2021 page references |
| 362 | + // '90', so neither leg of the predicate can rescue it. |
| 363 | + .andExpect(jsonPath("$.codeLists.tsaNumbers[?(@.code == '90')]", hasSize(0))); |
| 364 | + } |
| 365 | + |
| 366 | + @Test |
| 367 | + @DisplayName("an expired block a stored page references is rescued by the referenced-union leg") |
| 368 | + void supplyBlocks_referencedUnionRescuesAnExpiredCode() throws Exception { |
| 369 | + // The union leg on both queries had NO coverage: it can only rescue a code that HAS a row and |
| 370 | + // fell outside the date window, and no fixture created that shape until V20260821 was corrected |
| 371 | + // to seed '16Z' expired (see that file's CORRECTED note). Page 8904 references it, on mill 712. |
| 372 | + mockMvc.perform(get(ENDPOINT).param("millId", "712").param("year", "2021") |
| 373 | + .accept(MediaType.APPLICATION_JSON)) |
| 374 | + .andExpect(status().isOk()) |
| 375 | + // Drop the `OR TSB_NUMBER_CODE IN (...)` leg and this goes to 0. |
| 376 | + .andExpect(jsonPath("$.codeLists.supplyBlocks[?(@.code == '16Z')]", hasSize(1))); |
| 377 | + |
| 378 | + // Scoped to the MILL and the YEAR: mill 710 references no such block, so the expired code stays |
| 379 | + // dropped there. Remove the date predicate and this goes to 1. |
| 380 | + mockMvc.perform(get(ENDPOINT).param("millId", "710").param("year", "2021") |
| 381 | + .accept(MediaType.APPLICATION_JSON)) |
| 382 | + .andExpect(status().isOk()) |
| 383 | + .andExpect(jsonPath("$.codeLists.supplyBlocks[?(@.code == '16Z')]", hasSize(0))); |
| 384 | + } |
| 385 | + |
| 386 | + @Test |
| 387 | + @DisplayName("a code absent from its table entirely is not served, however many pages reference it") |
| 388 | + void codeLists_cannotServeACodeWithNoRow() throws Exception { |
| 389 | + // Page 8903 (mill 712) stores TSA '99' / TSB '99A', neither of which has a row in its code table. |
| 390 | + // The union leg selects FROM the code table, so it cannot invent one. This is the contract |
| 391 | + // boundary that makes the FRONTEND synthesise a stored code as its own option (review H2) — pinned |
| 392 | + // here so nobody "fixes" the client by pointing at a backend guarantee that does not exist. |
| 393 | + mockMvc.perform(get(ENDPOINT).param("millId", "712").param("year", "2021") |
| 394 | + .accept(MediaType.APPLICATION_JSON)) |
| 395 | + .andExpect(status().isOk()) |
| 396 | + .andExpect(jsonPath("$.codeLists.tsaNumbers[?(@.code == '99')]", hasSize(0))) |
| 397 | + .andExpect(jsonPath("$.codeLists.supplyBlocks[?(@.code == '99A')]", hasSize(0))) |
| 398 | + // The page itself still lists, carrying its stored location verbatim. |
| 399 | + .andExpect(jsonPath("$.pages[?(@.pageId == 8903)].tsaNumber", contains("99"))) |
| 400 | + .andExpect(jsonPath("$.pages[?(@.pageId == 8903)].tsbNumberCode", contains("99A"))); |
| 401 | + } |
| 402 | + |
332 | 403 | @Test |
333 | 404 | @DisplayName("BEC is served structurally, gated by the surviving BR-06 xref (deviation (e))") |
334 | 405 | void becIsStructuralAndXrefGated() throws Exception { |
|
0 commit comments