Skip to content

Verifiy trip details 108#171

Merged
aaronbrethorst merged 4 commits intoOneBusAway:mainfrom
MohamedKamal000:verifiy-trip-details-108
Jan 28, 2026
Merged

Verifiy trip details 108#171
aaronbrethorst merged 4 commits intoOneBusAway:mainfrom
MohamedKamal000:verifiy-trip-details-108

Conversation

@MohamedKamal000
Copy link
Copy Markdown
Contributor

@MohamedKamal000 MohamedKamal000 commented Dec 30, 2025

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

Copy link
Copy Markdown
Member

@aaronbrethorst aaronbrethorst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 approach

What needs to change:

  • Apply the same fix to trip_for_vehicle_handler.go for 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:33 correctly uses FormCombinedID to match the expected format
  • Wheelchair boarding mapping now properly uses the utility function rather than hardcoding "UNKNOWN"
  • JSON tag changes in trip_details.go (removing omitempty from frequency and situationIds) align with production behavior
  • The constants approach in models/constants.go improves maintainability

Once the service date fix is applied consistently to trip_for_vehicle_handler.go, this PR is good to go!

Copy link
Copy Markdown
Member

@aaronbrethorst aaronbrethorst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 direction

Main branch has already implemented api.calculateStopDirection(ctx, stopID) which is used consistently across:

  • trip_details_handler.go:335
  • arrival_and_departure_for_stop_handler.go:412
  • stops_for_route_handler.go:159
  • trip_for_vehicle_handler.go:291
  • block_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 - uses models.UnknownValue
  • stop_handler.go:55 - uses models.UnknownValue
  • arrival_and_departure_for_stop_handler.go:414 - uses models.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 of Truncate(24 * time.Hour) properly handles local timezones
  • Centralizing constants (models.Accessible, models.NotAccessible) improves maintainability
  • JSON tag changes removing omitempty matches production API behavior
  • VehicleID formatting with FormCombinedID ensures 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.

@MohamedKamal000 MohamedKamal000 force-pushed the verifiy-trip-details-108 branch from d76d866 to a1c9659 Compare January 27, 2026 23:28
@MohamedKamal000
Copy link
Copy Markdown
Contributor Author

MohamedKamal000 commented Jan 27, 2026

Verify Trip Details PR:

What have been solved:

  • 1.Unsafe WheelChairBoarding Type case:
    i have solved the WheelChairBoarding Unsafe Cast for the following places:
    internal/restapi/arrivals_and_departure_for_stop.go
    internal/restapi/stops_for_agency_handler.go
    internal/restapi/stops_for_route_handler.go
    internal/restapi/trip_details_handler.go

  • 2.PR branch needs rebase - stale Direction TODO and hardcoded value

  • 3. Silent timezone loading failure affects service date calculation:
    i have made a utility function so i can use it in different handlers and avoid duplication of same code
    i also wrote a unit test for it
    if the function 'LoadLocationWithUTCFallBack' location is not correct feel free to point out
    handlers i used it at:
    trip_details_handler.go
    trip_for_vehicle_handler.go
    arrival_and_depature_for_stop_handler.go
    arrivals_and_depature_for_stop.go

  • 4. Incomplete WheelchairBoarding fix - inconsistent across codebase:
    Changed from using the unknown constant to use the WheelChair Mapping`
    arrival_and_departure_for_stop_handler.go
    stop_handler.go
    trip_for_vehicle_handler.go

  • 5. Debug print statements in production code:
    removed them since it was used for debugging, i don't think there is no need to log them

  • 6. Service date calculation fix is not tested:
    i have tested the default path where the service date parameter is not given for the following handlers
    trip_details_handler_test.go
    trip_for_vehicle_handler_test.go
    note:
    i didn't want to pass a hard coded time zone and passed the agency time zone instead
    because i if i did so, i might get different results based on what is the time zone of the GetAgencies()[0]
    function call returns
    meaning the time zone that i would write hardcoded might not match the one i get from the function call.

i am not sure if writing test for the service date calculation this way is correct or not
i have been overthinking this for a long time, so feel free to review it and give your feedback

  • 7. VehicleID format change is not tested:
    i copied some code from another function 'setupTestApiWithMockVehicle' for mocking
    i only focused on testing the VehicleID format

  • 8. JSON structure change not tested:

  • 9. New constants lack documentation comments

  • 10. Service date fix lacks explanatory comment

@aaronbrethorst let me know if any other changes are needed

Copy link
Copy Markdown
Member

@aaronbrethorst aaronbrethorst left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@aaronbrethorst aaronbrethorst merged commit 0d03ffe into OneBusAway:main Jan 28, 2026
4 checks passed
@MohamedKamal000
Copy link
Copy Markdown
Contributor Author

will try to open a pr for that test as well once iam done with other prs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants