From 88716ef8edd32b1965111a75824f2254dc4d79dd Mon Sep 17 00:00:00 2001 From: danielhep Date: Fri, 11 Sep 2026 15:34:28 -0700 Subject: [PATCH 1/3] Expose failed itinerary expectations for reports --- .../assertions/ItineraryAssertionError.java | 31 +++++++++++++++++-- .../assertions/ItineraryAssertions.java | 11 +++++-- 2 files changed, 37 insertions(+), 5 deletions(-) diff --git a/assertions/src/main/java/org/opentripplanner/assertions/ItineraryAssertionError.java b/assertions/src/main/java/org/opentripplanner/assertions/ItineraryAssertionError.java index 40a9e3c..1203b64 100644 --- a/assertions/src/main/java/org/opentripplanner/assertions/ItineraryAssertionError.java +++ b/assertions/src/main/java/org/opentripplanner/assertions/ItineraryAssertionError.java @@ -1,17 +1,42 @@ package org.opentripplanner.assertions; import java.util.List; +import org.opentripplanner.client.model.TripPlan; public class ItineraryAssertionError extends AssertionError { - private final List failedResults; + private final List> expectedLegs; + private final boolean strictTransitMatching; + private final TripPlan tripPlan; - public ItineraryAssertionError(String message, List failedResults) { + /** Captures reportable expectations without exposing executable predicates. */ + public ItineraryAssertionError( + String message, + List failedResults, + List> expectedLegs, + boolean strictTransitMatching, + TripPlan tripPlan) { super(message); - this.failedResults = failedResults; + this.failedResults = List.copyOf(failedResults); + this.expectedLegs = expectedLegs.stream().map(List::copyOf).toList(); + this.strictTransitMatching = strictTransitMatching; + this.tripPlan = tripPlan; } public List getFailedResults() { return failedResults; } + + public List> getExpectedLegs() { + return expectedLegs; + } + + public boolean isStrictTransitMatching() { + return strictTransitMatching; + } + + /** The response used by the failed assertion, or null for the legacy constructor. */ + public TripPlan getTripPlan() { + return tripPlan; + } } diff --git a/assertions/src/main/java/org/opentripplanner/assertions/ItineraryAssertions.java b/assertions/src/main/java/org/opentripplanner/assertions/ItineraryAssertions.java index 3ef6533..57fffe4 100644 --- a/assertions/src/main/java/org/opentripplanner/assertions/ItineraryAssertions.java +++ b/assertions/src/main/java/org/opentripplanner/assertions/ItineraryAssertions.java @@ -71,7 +71,7 @@ public ItineraryAssertions withRouteShortName(String... shortNames) { public ItineraryAssertions withFarePrice(float price, String riderCategoryId, String mediumId) { addCurrentLegCriterion( - "fare $%.2f".formatted(price), + "fare %.2f (rider category %s, medium %s)".formatted(price, riderCategoryId, mediumId), leg -> leg.fareProducts().stream() .filter(fp -> fp.product().riderCategory().isPresent()) @@ -154,7 +154,14 @@ public void assertMatches(TripPlan tripPlan) { } String fullError = header + criteriaSection + failuresSection; - throw new ItineraryAssertionError(fullError, failedResults); + throw new ItineraryAssertionError( + fullError, + failedResults, + distinctLegCriteria.stream() + .map(criteria -> criteria.stream().map(LegCriterion::message).toList()) + .toList(), + strictTransitMatching, + tripPlan); } /** From a2379bedbacc62b0b4a582e1950a8fab26285dcd Mon Sep 17 00:00:00 2001 From: danielhep Date: Tue, 15 Sep 2026 14:51:12 -0700 Subject: [PATCH 2/3] remove unneeded test --- .../assertions/ItineraryAssertionsTest.java | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/assertions/src/test/java/org/opentripplanner/assertions/ItineraryAssertionsTest.java b/assertions/src/test/java/org/opentripplanner/assertions/ItineraryAssertionsTest.java index bfd03e9..da34e1b 100644 --- a/assertions/src/test/java/org/opentripplanner/assertions/ItineraryAssertionsTest.java +++ b/assertions/src/test/java/org/opentripplanner/assertions/ItineraryAssertionsTest.java @@ -156,16 +156,6 @@ void withFarePriceHandlesPositiveAndNegativeMatches() { assertThat(error.getMessage()).contains("fare $3.00"); } - @Test - void deprecatedAliasExtendsCanonicalError() { - List failedResults = List.of(ItineraryMatchResult.success(List.of())); - - ItineraryAssertionError error = new ItineraryAssertionError("boom", failedResults); - - assertThat(error).isInstanceOf(ItineraryAssertionError.class); - assertThat(error.getFailedResults()).isEqualTo(failedResults); - } - private static TripPlan tripPlan(Itinerary... itineraries) { return new TripPlan(List.of(itineraries), "", ""); } From 314fc6ec8256335a5f5859477d915922883410e5 Mon Sep 17 00:00:00 2001 From: danielhep Date: Wed, 16 Sep 2026 14:55:44 -0700 Subject: [PATCH 3/3] improve test relevance --- .../assertions/ItineraryAssertionsTest.java | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/assertions/src/test/java/org/opentripplanner/assertions/ItineraryAssertionsTest.java b/assertions/src/test/java/org/opentripplanner/assertions/ItineraryAssertionsTest.java index da34e1b..be7b3d2 100644 --- a/assertions/src/test/java/org/opentripplanner/assertions/ItineraryAssertionsTest.java +++ b/assertions/src/test/java/org/opentripplanner/assertions/ItineraryAssertionsTest.java @@ -130,9 +130,8 @@ void partialMatchesAndErrorDetailsAreIncluded() { } @Test - void withFarePriceHandlesPositiveAndNegativeMatches() { - List fares = - List.of(fare("orca:regular", "orca:cash"), fare(2.75f, "orca:regular", "orca:cash")); + void withFarePriceMatchesFareWithSpecifiedPriceRiderCategoryAndMedium() { + List fares = List.of(fare(2.75f, "orca:regular", "orca:cash")); TripPlan plan = tripPlan(itinerary(transitLeg("E", "E Line", LegMode.BUS, Duration.ofMinutes(12), fares))); @@ -143,6 +142,13 @@ void withFarePriceHandlesPositiveAndNegativeMatches() { .withRouteShortName("E") .withFarePrice(2.75f, "orca:regular", "orca:cash") .assertMatches(plan)); + } + + @Test + void withFarePriceReportsMismatchedFare() { + List fares = List.of(fare(2.75f, "orca:regular", "orca:cash")); + TripPlan plan = + tripPlan(itinerary(transitLeg("E", "E Line", LegMode.BUS, Duration.ofMinutes(12), fares))); ItineraryAssertionError error = assertThrows( @@ -153,7 +159,15 @@ void withFarePriceHandlesPositiveAndNegativeMatches() { .withRouteShortName("E") .withFarePrice(3.00f, "orca:regular", "orca:cash") .assertMatches(plan)); - assertThat(error.getMessage()).contains("fare $3.00"); + + String expectedFareCriterion = "fare 3.00 (rider category orca:regular, medium orca:cash)"; + assertThat(error.getExpectedLegs()) + .containsExactly(List.of("route '[E]'", expectedFareCriterion)); + assertThat(error.getFailedResults()).hasSize(1); + assertThat(error.getFailedResults().get(0).getPartialMatches()).hasSize(1); + LegMatchingState partialMatch = error.getFailedResults().get(0).getPartialMatches().get(0); + assertThat(partialMatch.getMatchingCriteria()).isEqualTo("route '[E]'"); + assertThat(partialMatch.getMissingCriteria()).isEqualTo(expectedFareCriterion); } private static TripPlan tripPlan(Itinerary... itineraries) {