|
49 | 49 | import org.apache.commons.io.IOUtils; |
50 | 50 | import org.eclipse.jetty.server.Server; |
51 | 51 | import org.joda.time.DateTime; |
| 52 | +import org.joda.time.DateTimeZone; |
52 | 53 | import org.joda.time.LocalDate; |
53 | 54 | import org.joda.time.format.ISODateTimeFormat; |
54 | 55 | import org.junit.AfterClass; |
@@ -328,6 +329,51 @@ public void testProvideSlots() { |
328 | 329 | } |
329 | 330 | } |
330 | 331 |
|
| 332 | + /** |
| 333 | + * When the exam period starts April 2nd (in the room's timezone) and the requested week |
| 334 | + * includes April 1st, provideSlots must not return slots for April 1st. This guards |
| 335 | + * against DST bugs where the wrong offset was used to derive the exam start date. |
| 336 | + */ |
| 337 | + @Test |
| 338 | + public void testProvideSlotsExcludesDayBeforeExamStartAfterDstChange() { |
| 339 | + room = DB.find(ExamRoom.class, 1L); |
| 340 | + room.setExternalRef(ROOM_REF); |
| 341 | + room.setLocalTimezone("Europe/Helsinki"); |
| 342 | + room.update(); |
| 343 | + |
| 344 | + GeneralSettings gs = new GeneralSettings(); |
| 345 | + gs.setName("reservation_window_size"); |
| 346 | + gs.setValue("60"); |
| 347 | + gs.setId(3L); |
| 348 | + gs.save(); |
| 349 | + |
| 350 | + // Exam period: April 2–5 in room's timezone. Use UTC instants so the URL has no '+' (e.g. +03:00). |
| 351 | + // April 2 00:00 Helsinki (EEST) = 2026-04-01T21:00:00Z, April 5 23:59 Helsinki = 2026-04-05T20:59:59Z. |
| 352 | + DateTime examStartUtc = new DateTime(2026, 4, 1, 21, 0, 0, DateTimeZone.UTC); |
| 353 | + DateTime examEndUtc = new DateTime(2026, 4, 5, 20, 59, 59, DateTimeZone.UTC); |
| 354 | + |
| 355 | + // Request slots for the week containing April 1 (Monday 2026-03-31). |
| 356 | + // Without the DST fix, search could start on April 1 and return slots for that day. |
| 357 | + String url = String.format( |
| 358 | + "/integration/iop/slots?roomId=%s&date=2026-03-31&start=%s&end=%s&duration=%d", |
| 359 | + room.getExternalRef(), |
| 360 | + ISODateTimeFormat.dateTime().print(examStartUtc), |
| 361 | + ISODateTimeFormat.dateTime().print(examEndUtc), |
| 362 | + 180 |
| 363 | + ); |
| 364 | + Result result = get(url); |
| 365 | + assertThat(result.status()).isEqualTo(Http.Status.OK); |
| 366 | + JsonNode node = Json.parse(contentAsString(result)); |
| 367 | + ArrayNode slots = (ArrayNode) node; |
| 368 | + |
| 369 | + LocalDate examStartDate = new LocalDate(2026, 4, 2); |
| 370 | + for (JsonNode slot : slots) { |
| 371 | + String startIso = slot.get("start").asText(); |
| 372 | + LocalDate slotDate = ISODateTimeFormat.dateTimeParser().parseDateTime(startIso).toLocalDate(); |
| 373 | + assertThat(slotDate.isBefore(examStartDate)).isFalse(); |
| 374 | + } |
| 375 | + } |
| 376 | + |
331 | 377 | @Test |
332 | 378 | public void testProvideReservation() { |
333 | 379 | room = DB.find(ExamRoom.class, 1L); |
|
0 commit comments