Cover schedule-for-stop response shape guarantees - #1388
Conversation
The handler returned an empty references block as soon as a stop had no routes, skipping the includeReferences check further down. A valid stop with no routes therefore answered with its own ID in entry.stopId and no matching record in references.stops. The early return was only an optimization. GetScheduleForStopOnDate expands an empty route ID slice to IN (NULL), so the normal path already returns no schedule rows and builds references correctly.
Route and agency references were collected from the schedule rows of the queried date, so both lists emptied out whenever no service was active -- a holiday, a date outside the feed's validity period, or an expired feed. The reference server keeps them populated in that case, since references describe the stop, not the day. Build them from the routes serving the stop instead. GetRoutesForStop already selects every column the route reference needs, so the GetRoutesByIDs round trip goes away, and agencies now derive from those routes rather than being seeded with the queried stop's own agency.
|
Warning Review limit reached
Next review available in: 24 minutes Limit details: You’ve used the included review currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?Wait for the limit to reset, then comment An organization admin can change what happens after included review limits in Billing. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (6)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
The no-service case only checked that routes and agencies came back non-empty, which passes just as well if the handler references the wrong routes. Derive the expected sets from the stop's own routes and compare against them, so the test fails if the reference contents drift.
The agency lookup ran first, so an unparseable date on an ID whose agency prefix does not exist answered 404 instead of the field error the reference server returns. The spec requires the field error to come back before any stop lookup is attempted. Check the date's format up front and keep resolving it to a service date below, where the agency's timezone is available.
Frequency-based trips were indistinguishable from fixed-schedule ones: the handler passed nil for a direction group's frequencies, so a stop served only on a headway answered with scheduleFrequencies empty and its headway trips listed as if each ran once. Batch-load each trip's frequency windows and route those trips into scheduleFrequencies instead of scheduleStopTimes, one entry per window, sorted by start time. A trip's headsign now weighs as many votes as the runs it is expected to make, so a frequent route no longer loses the representative-headsign vote to a single scheduled trip. The three parallel maps that carried a direction group's stop times and headsign votes are replaced by one accumulator type, since frequencies would otherwise have added a fourth. The RABA fixture has no frequencies.txt, so the test builds a feed that does and loads it through a new createTestApiWithFeed helper, leaving the shared fixture untouched.
GTFS treats exact_times=1 as a compact encoding of a fixed timetable, not an approximate headway, so a rider reading the schedule expects the same individual departures a fully written-out timetable would show. Split a trip's frequency windows on exact_times: headway windows keep populating scheduleFrequencies, while exact ones expand into one stop time per interval, each repeating the template trip's offset from its first arrival. Stop times are sorted by departure time afterwards, since expanded runs are appended after the ones the query already ordered. This deviates from legacy Java, which routes both kinds into scheduleFrequencies undifferentiated; the wiki records the reasoning and the fallback.
The suite asserted sorting and block boundaries but never the response's own shape: which reference lists exist, that trips and situations stay empty because the entry references trips by ID only, that stop times carry combined IDs, and that they come back in departure order. Cover those four, and that every route named in the entry has a record in references.
cf19452 to
e8a3464
Compare
|
Ahmedhossamdev
left a comment
There was a problem hiding this comment.
LGTM. Scoped correctly to the last commit as noted in the description - the stacked commits from #1385/#1386/#1387 aren't part of this review. Confirmed the new assertions against the actual handler: references.trips/.situations/.stopTimes really do stay empty for this endpoint, and the four guarantees this adds (envelope shape, reference-list presence, route-reference completeness, stop-time ordering/ID shape) are checked against real handler output, not just non-emptiness. Test passes, gofmt/go vet clean. No changes requested.



Fixes #1034
Stacked on #1387 — its commits show up in this diff until it merges. Review the last commit here.
What changed
Re-read the endpoint against the wiki and covered what the suite was missing. The existing tests already assert natural route sorting, alphabetical direction sorting, block-boundary flags, both
dateformats, invalid dates, and malformed IDs, so this adds only the gaps:code,text,version,currentTime.trips,stopTimesandsituationsstay empty — the entry references trips by ID only, which schedule-for-stop: Trips are incorrectly included in references block #1032 established.stopRouteScheduleshas a record inreferences.routes.tripIdandserviceId, always includestopHeadsign, and come back sorted by departure time.Departure-time ordering was previously guaranteed only by the query's
ORDER BY, with nothing asserting it end to end. That matters more now that #1387 appends synthesized stop times after the ordered ones.Spec
Success Guarantees and the Response Structure section, which documents the envelope and the
scheduleStopTimes[]field shape.Coverage of the remaining spec clauses lands with the PRs that implement them: route-less stops and no-service dates in #1384, date-validation ordering in #1385, frequencies in #1386,
exact_times=1expansion in #1387.