Skip to content

Commit bdfa457

Browse files
gpascucciclaude
andcommitted
test(schedule10): section-order + combined-print ITs for Schedule 10 (Story 20.4 AC-8)
- ScheduleKeyTest (unit): pins the BR-08 fixed section order and that SCHEDULE_10 sits between SCHEDULE_9 and SCHEDULE_11, so a combined print always emits it in that fixed position; plus its template path + bookmark title. - PrintScheduleIT (Oracle IT), three cases on the existing Schedule 10 fixtures: - 710/2021 Schedule-10-only -> renders the section (heading, mill title block, division, region code->label "Northern Interior", road name) with EXACTLY one top-level bookmark (BR-08). - 514/2021 select 5+10 -> Schedule 5 prints, Schedule 10 skipped-with-a-log (BR-09), leaving only the Schedule 5 bookmark. - 715/2021 Schedule-10-only, zero pages -> 404 'Schedule not found.' (ERR-005). (The standalone Schedule 9 path is unchanged and stays covered by ReportSchedule9IT.) Verified: PrintScheduleIT green under the Oracle Testcontainers profile. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 4a7c3c7 commit bdfa457

2 files changed

Lines changed: 137 additions & 0 deletions

File tree

backend/src/test/java/ca/bc/gov/nrs/ilcr/reporting/PrintScheduleIT.java

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -460,6 +460,94 @@ void combinedFill_usesVirtualizer() throws Exception {
460460
verify(virtualizerFactory, atLeastOnce()).create();
461461
}
462462

463+
@Test
464+
@DisplayName("Schedule 10 (Story 20.4): 710/2021 renders the section + exactly one bookmark")
465+
void schedule10_rendersWithOneBookmark() throws Exception {
466+
// Mill 710/2021 carries the rich Schedule 10 fixture (V20260817): 2 construction pages with
467+
// road
468+
// details, region RNI resolved to "Northern Interior". Selecting Schedule 10 alone must render
469+
// its section (heading + real seeded values) and carry exactly its one top-level bookmark
470+
// (BR-08).
471+
String selection =
472+
"""
473+
{"schedule10":true,"printScheduleInformation":true,"printComments":true}
474+
""";
475+
MvcResult result =
476+
streamPdf(
477+
post(ENDPOINT)
478+
.param("millId", "710")
479+
.param("year", "2021")
480+
.contentType(MediaType.APPLICATION_JSON)
481+
.content(body(selection))
482+
.accept(MediaType.APPLICATION_PDF))
483+
.andExpect(status().isOk())
484+
.andExpect(content().contentType(MediaType.APPLICATION_PDF))
485+
.andReturn();
486+
487+
byte[] pdf = result.getResponse().getContentAsByteArray();
488+
assertThat(new String(pdf, 0, 4)).isEqualTo("%PDF");
489+
490+
String text = extractText(pdf);
491+
assertThat(text).contains("Schedule 10: New Road Construction Costs");
492+
assertThat(text).contains("Sch10 Rich Construction"); // mill title block (name-number)
493+
assertThat(text).contains("North Division"); // page 8900 division
494+
assertThat(text).contains("Northern Interior"); // region RNI resolved code -> label
495+
assertThat(text).contains("Mainline A"); // road name (detail 8910)
496+
497+
// BR-08: a single-schedule print still carries exactly one top-level bookmark.
498+
assertThat(topLevelBookmarks(pdf)).containsExactly(ScheduleKey.SCHEDULE_10.bookmarkTitle());
499+
}
500+
501+
@Test
502+
@DisplayName("skip-empty (BR-09): 514/2021 select 5+10, 10 has no data -> 5 prints, 10 omitted")
503+
void schedule10_skipEmpty_keepsTheRest() throws Exception {
504+
// Mill 514/2021 has Schedule 5 camps but NO Schedule 10 construction pages (the Schedule 10
505+
// fixtures are mills 710-716), so selecting both must print Schedule 5 and silently omit
506+
// Schedule 10 (BR-09) — leaving exactly the Schedule 5 bookmark.
507+
String selection =
508+
"""
509+
{"schedule5":true,"schedule10":true,"printScheduleInformation":true,"printComments":true}
510+
""";
511+
MvcResult result =
512+
streamPdf(
513+
post(ENDPOINT)
514+
.param("millId", "514")
515+
.param("year", "2021")
516+
.contentType(MediaType.APPLICATION_JSON)
517+
.content(body(selection)))
518+
.andExpect(status().isOk())
519+
.andExpect(content().contentType(MediaType.APPLICATION_PDF))
520+
.andReturn();
521+
522+
byte[] pdf = result.getResponse().getContentAsByteArray();
523+
String text = extractText(pdf);
524+
assertThat(text).contains("Schedule 5: Camp and Access Expense");
525+
assertThat(text).contains("Cedar Flats Camp");
526+
assertThat(text).doesNotContain("Schedule 10: New Road Construction Costs");
527+
assertThat(topLevelBookmarks(pdf)).containsExactly(ScheduleKey.SCHEDULE_5.bookmarkTitle());
528+
}
529+
530+
@Test
531+
@DisplayName("all-empty (ERR-005): 715/2021 select 10, no pages -> 404 'Schedule not found.'")
532+
void schedule10Only_noData_returns404() throws Exception {
533+
// Mill 715/2021 is a valid active context with ZERO Schedule 10 pages; a Schedule-10-only print
534+
// is then all-empty, the legacy single-schedule outcome (ERR-005), not a blank PDF.
535+
String selection =
536+
"""
537+
{"schedule10":true,"printScheduleInformation":true,"printComments":true}
538+
""";
539+
mockMvc
540+
.perform(
541+
post(ENDPOINT)
542+
.param("millId", "715")
543+
.param("year", "2021")
544+
.contentType(MediaType.APPLICATION_JSON)
545+
.content(body(selection)))
546+
.andExpect(status().isNotFound())
547+
.andExpect(content().contentTypeCompatibleWith(PROBLEM_JSON))
548+
.andExpect(jsonPath("$.detail").value(ERR_005));
549+
}
550+
463551
private static String extractText(byte[] pdf) throws Exception {
464552
try (PDDocument document = Loader.loadPDF(pdf)) {
465553
return new PDFTextStripper().getText(document);
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package ca.bc.gov.nrs.ilcr.reporting;
2+
3+
import static org.assertj.core.api.Assertions.assertThat;
4+
5+
import java.util.Arrays;
6+
import java.util.List;
7+
import org.junit.jupiter.api.DisplayName;
8+
import org.junit.jupiter.api.Test;
9+
10+
/**
11+
* Unit test — {@link ScheduleKey} declaration order (BR-08). The enum's declaration order IS the
12+
* combined-PDF section order (the orchestrator iterates {@link ScheduleKey#values()} with no
13+
* separate ordering table), so this pins the FIXED legacy sequence and, in particular, that
14+
* Schedule 10 (Story 20.4) sits between Schedule 9 and Schedule 11 — i.e. a combined print always
15+
* emits it in that fixed position relative to the other selected sections. No Spring context or
16+
* database.
17+
*/
18+
@DisplayName("ScheduleKey — fixed legacy section order (BR-08)")
19+
class ScheduleKeyTest {
20+
21+
@Test
22+
@DisplayName("values() are in the fixed legacy print order, with SCHEDULE_10 between 9 and 11")
23+
void values_areInFixedLegacyOrder() {
24+
List<ScheduleKey> order = Arrays.asList(ScheduleKey.values());
25+
26+
assertThat(order)
27+
.containsExactly(
28+
ScheduleKey.SCHEDULE_5,
29+
ScheduleKey.SCHEDULE_6,
30+
ScheduleKey.SCHEDULE_7A,
31+
ScheduleKey.SCHEDULE_7B,
32+
ScheduleKey.SCHEDULE_9,
33+
ScheduleKey.SCHEDULE_10,
34+
ScheduleKey.SCHEDULE_11);
35+
36+
// The load-bearing 20.4 fact: Schedule 10 renders after 9 and before 11 in any combined print.
37+
assertThat(order.indexOf(ScheduleKey.SCHEDULE_10))
38+
.isGreaterThan(order.indexOf(ScheduleKey.SCHEDULE_9))
39+
.isLessThan(order.indexOf(ScheduleKey.SCHEDULE_11));
40+
}
41+
42+
@Test
43+
@DisplayName("SCHEDULE_10 is bound to its template + bookmark title")
44+
void schedule10_hasTemplateAndBookmark() {
45+
assertThat(ScheduleKey.SCHEDULE_10.templatePath()).isEqualTo("reports/schedule10.jrxml");
46+
assertThat(ScheduleKey.SCHEDULE_10.bookmarkTitle())
47+
.isEqualTo("Schedule 10: New Road Construction Costs");
48+
}
49+
}

0 commit comments

Comments
 (0)