Verifiy trip details 108#171
Conversation
aaronbrethorst
left a comment
There was a problem hiding this comment.
Thanks for putting together this verification PR for the trip-details endpoint! The testing checklist in the PR description is thorough, and it's clear you've done substantial comparison against the production Puget Sound server. The changes here address real discrepancies between Maglev and the production API.
Issues to Fix
1. Inconsistent Service Date Fix
The service date calculation fix in trip_details_handler.go is correct—using time.Date() with the proper timezone is the right approach since Truncate(24 * time.Hour) truncates to UTC midnight, not local midnight:
// Fixed in this PR (correct):
y, m, d := currentTime.Date()
serviceDate = time.Date(y, m, d, 0, 0, 0, 0, loc)However, the same problematic pattern exists in trip_for_vehicle_handler.go:103:
serviceDate = currentTime.Truncate(24 * time.Hour) // Still uses the old approachWhat needs to change:
- Apply the same fix to
trip_for_vehicle_handler.gofor consistency - This handler shares similar logic and parameters with
trip_details_handler.go
Suggestions
Consider centralizing wheelchair boarding constants usage
The new constants (Accessible, NotAccessible) are well-placed in constants.go. You might want to check if there are other places in the codebase using hardcoded "ACCESSIBLE" or "NOT_ACCESSIBLE" strings that should be updated for consistency:
grep -r '"ACCESSIBLE"\|"NOT_ACCESSIBLE"' internal/The directionId type discrepancy
You noted in the PR description:
in the trips field inside the references field, the directionId is integer in the development but it is string in the server response
This is a valid observation. The GTFS spec defines direction_id as an enum (0 or 1), so integer is arguably more correct, but matching production behavior is important for compatibility. This might warrant a separate follow-up issue.
What Looks Good
- Vehicle ID fix in
trips_helper.go:33correctly usesFormCombinedIDto match the expected format - Wheelchair boarding mapping now properly uses the utility function rather than hardcoding
"UNKNOWN" - JSON tag changes in
trip_details.go(removingomitemptyfromfrequencyandsituationIds) align with production behavior - The constants approach in
models/constants.goimproves maintainability
Once the service date fix is applied consistently to trip_for_vehicle_handler.go, this PR is good to go!
aaronbrethorst
left a comment
There was a problem hiding this comment.
Good verification work here! The service date calculation fix is an important timezone bug fix, and centralizing the wheelchair boarding constants improves maintainability. However, there are several issues that need attention before merging.
Critical
1. Unsafe WheelchairBoarding type cast without null check
The PR introduces direct Int64 casts without checking the Valid flag on sql.NullInt64 fields.
Files: internal/restapi/arrivals_and_departure_for_stop.go:339, internal/restapi/trip_details_handler.go:337
WheelchairBoarding: utils.MapWheelchairBoarding(gtfs.WheelchairBoarding(stopData.WheelchairBoarding.Int64)),When stopData.WheelchairBoarding is NULL in the database (Valid == false), accessing .Int64 returns 0, which happens to map correctly. However, if the database contains an invalid integer (e.g., 5, 99, -1), this silently casts to gtfs.WheelchairBoarding, falls through the default case, and returns "UNKNOWN" - indistinguishable from missing data.
The codebase already has a safe pattern for this:
// Used correctly in trips_for_location_handler.go:370, stops_for_location_handler.go:199
WheelchairBoarding: utils.MapWheelchairBoarding(utils.NullWheelchairBoardingOrUnknown(stopData.WheelchairBoarding)),Fix: Use the existing safe utility function utils.NullWheelchairBoardingOrUnknown.
2. PR branch needs rebase - stale Direction TODO and hardcoded value
The PR contains a stale TODO that main branch has already resolved.
File: internal/restapi/arrivals_and_departure_for_stop.go:337
Direction: "N", // TODO: Calculate actual directionMain branch has already implemented api.calculateStopDirection(ctx, stopID) which is used consistently across:
trip_details_handler.go:335arrival_and_departure_for_stop_handler.go:412stops_for_route_handler.go:159trip_for_vehicle_handler.go:291block_handler.go:201
This PR would reintroduce a hardcoded value and stale TODO.
Fix: Rebase against current main and use api.calculateStopDirection(ctx, stopID).
High Priority
3. Silent timezone loading failure affects service date calculation
The timezone loading error is explicitly ignored, causing silent fallback to UTC.
Files: internal/restapi/trip_details_handler.go:88, internal/restapi/trip_for_vehicle_handler.go:90
loc, _ := time.LoadLocation(agency.Timezone)If the timezone string is invalid, loc becomes nil, defaulting to UTC. The PR's new service date code then uses this:
y, m, d := currentTime.Date()
serviceDate = time.Date(y, m, d, 0, 0, 0, 0, loc)For an agency in "America/Los_Angeles" (-8 hours), a request at 11 PM local time would calculate the service date as the next day, returning wrong schedule data.
Fix: Log when timezone loading fails:
loc, err := time.LoadLocation(agency.Timezone)
if err != nil {
slog.Warn("invalid agency timezone, using UTC",
slog.String("agencyID", agency.ID),
slog.String("timezone", agency.Timezone),
slog.String("error", err.Error()))
loc = time.UTC
}4. Incomplete WheelchairBoarding fix - inconsistent across codebase
The PR fixes some locations but leaves others with hardcoded values, including one in the same file.
Locations still using hardcoded values:
arrivals_and_departure_for_stop.go:349- uses"UNKNOWN"(same file as PR change!)trip_for_vehicle_handler.go:293- usesmodels.UnknownValuestop_handler.go:55- usesmodels.UnknownValuearrival_and_departure_for_stop_handler.go:414- usesmodels.UnknownValue
Different endpoints will return different WheelchairBoarding values for the same stop.
Fix: Either fix all occurrences for consistency, or document why certain handlers intentionally use hardcoded values.
5. Debug print statements in production code
File: internal/restapi/trip_for_vehicle_handler.go:189-190, 115
fmt.Println("RefStops:", stops)
fmt.Println("RefRoutes:", uniqueRouteMap)
fmt.Println("GetTrip error:", err)Per CLAUDE.md, the project uses structured logging. Raw fmt.Println calls should not be in production code.
Fix: Remove these debug statements or convert to structured logging.
Test Coverage Gaps
6. Service date calculation fix is not tested
The fix changes from Truncate(24 * time.Hour) to time.Date(y, m, d, 0, 0, 0, 0, loc) to handle timezones correctly. This is a critical bug fix but has no test coverage.
Impact: The old code in America/Los_Angeles (UTC-8) at 2024-01-15 03:00:00 PST would truncate to 2024-01-15 00:00:00 UTC, which is 2024-01-14 16:00:00 PST - the previous day.
The existing tests all provide explicit serviceDate parameters, so the default calculation path is untested.
Add test for:
func TestServiceDateCalculationWithTimezone(t *testing.T) {
// Given: currentTime is 3 AM in America/Los_Angeles
// When: serviceDate is calculated without explicit parameter
// Then: serviceDate should be the local date, not UTC-truncated
}7. VehicleID format change is not tested
File: internal/restapi/trips_helper.go:33
The change from vehicle.ID.ID to utils.FormCombinedID(agencyID, vehicle.ID.ID) changes the API response format from "VEHICLE123" to "agency_VEHICLE123". This API contract change has no test coverage.
Add test for:
func TestBuildTripStatus_VehicleIDFormat(t *testing.T) {
// Given: A vehicle with ID "V123" and agency "AGENCY1"
// When: BuildTripStatus is called
// Then: status.VehicleID should be "AGENCY1_V123"
}8. JSON structure change not tested
File: internal/models/trip_details.go
The removal of omitempty from Frequency and SituationIDs changes JSON output. Before: null values omitted. After: explicit null or []. This could break clients checking for field existence.
Lower Priority
9. New constants lack documentation comments
File: internal/models/constants.go:7-8
const (
// UnknownValue is the fallback value when data is unavailable or calculation fails
UnknownValue = "UNKNOWN"
Accessible = "ACCESSIBLE" // No documentation
NotAccessible = "NOT_ACCESSIBLE" // No documentation
)Fix: Add documentation matching the existing style:
// Accessible indicates wheelchair boarding is possible (GTFS wheelchair_boarding = 1)
Accessible = "ACCESSIBLE"
// NotAccessible indicates wheelchair boarding is not possible (GTFS wheelchair_boarding = 2)
NotAccessible = "NOT_ACCESSIBLE"10. Service date fix lacks explanatory comment
The fix is correct but non-obvious. Consider adding a comment:
// Use time.Date() to get local midnight, not Truncate() which uses UTC
y, m, d := currentTime.Date()
serviceDate = time.Date(y, m, d, 0, 0, 0, 0, loc)What's Good
- Service date calculation fix is correct - Using
time.Date()instead ofTruncate(24 * time.Hour)properly handles local timezones - Centralizing constants (
models.Accessible,models.NotAccessible) improves maintainability - JSON tag changes removing
omitemptymatches production API behavior - VehicleID formatting with
FormCombinedIDensures consistent ID format across the API - Test updates use constants instead of string literals
Summary
| Priority | Issue | Location |
|---|---|---|
| Critical | Unsafe WheelchairBoarding type cast | arrivals_and_departure_for_stop.go:339, trip_details_handler.go:337 |
| Critical | PR needs rebase - stale Direction TODO | arrivals_and_departure_for_stop.go:337 |
| High | Silent timezone loading failure | trip_details_handler.go:88, trip_for_vehicle_handler.go:90 |
| High | Incomplete WheelchairBoarding fix | Multiple files |
| High | Debug print statements | trip_for_vehicle_handler.go |
| Tests | Service date calculation untested | trip_details_handler.go, trip_for_vehicle_handler.go |
| Tests | VehicleID format change untested | trips_helper.go |
| Tests | JSON structure change untested | trip_details.go |
| Lower | Constants lack documentation | constants.go |
| Lower | Service date fix lacks comment | trip_details_handler.go |
The verification effort is valuable, and the service date fix addresses a real timezone bug. However, the PR needs a rebase against main to avoid reintroducing the hardcoded direction value, and the WheelchairBoarding changes need to use the safe utility pattern that already exists in the codebase.
Let me know if you have questions about any of this feedback.
d76d866 to
a1c9659
Compare
|
Verify Trip Details PR: What have been solved:
i am not sure if writing test for the service date calculation this way is correct or not
@aaronbrethorst let me know if any other changes are needed |
There was a problem hiding this comment.
Thanks for the thorough work addressing all the feedback from my previous review. The changes look solid and the verification checklist in the PR description demonstrates good attention to detail.
Important Items
Potential test flakiness at midnight boundary
Files: internal/restapi/trip_details_handler_test.go:47-53, internal/restapi/trip_for_vehicle_handler_test.go:155-163
The service date tests calculate the expected value using time.Now() after the HTTP request has already been made:
currentTimeInLoc := time.Now().In(loc)
y, m, d := currentTimeInLoc.Date()
expectedServiceDate := time.Date(y, m, d, 0, 0, 0, 0, loc)If the test runs exactly at midnight and the request happens just before midnight while the assertion happens just after, the dates will differ and the test will fail. This is a rare edge case but worth noting.
I don't think it's worth delaying merging this pull request for that very unlikely occurrence.
|
will try to open a pr for that test as well once iam done with other prs |
solves #108
Verification & Testing Checklist for trip-details Endpoint:
i tested the endpoint against pugetsound server with agency id 1 and 40
[x] Verify the endpoint is reachable and responding with correct status codes (e.g., 200 OK for valid requests).
[x] Compare the response from testing against production to ensure consistency in data and structure.
[x] Validate response schema (fields, types, required properties).
currently there is only one type change against the server
in the trips field inside the references field, the directionId is integer in the development
but it is string in the server response
So I iam not sure should i change or not. i didn't want to because it would result in more refactoring across the codebase
[x] Confirm trip details are accurate for the given trip ID.
[x] Test with different trip IDs (valid, invalid, non-existent).
[x] Test edge cases (e.g., trips with missing fields).
[] Check response times and ensure performance is acceptable. (the direction calculation causes request time out, see issue #165 )
[x] Ensure proper error handling (400, 404, 500 cases).
[] Write/strengthen unit tests if coverage is missing or weak.
[x] Confirm no regressions compared to production behavior.
Notes:
in order for me to test the end point correclty, i had to use the DirectionCalculator in the
precomputation step, so that i don't wait for a long times and maybe don't even get a response when testing
when i did that, i noticed difference in the direction values compared to the server response
i didn't test the status field much, since it depends on real time data