session["end"].nil? was the active signal since this was first built,
on the assumption Garmin leaves it unset until the activity actually
stops. Checked directly against a real still-recording session today
(2026-09-20): end was already non-nil, set to exactly start + 24h — the
share link's own expiry, not an end time — so every currently-active
session was showing as "Ended". A genuinely ended session's end does
hold its real end time (confirmed against the 2026-09-06 session this
was originally, and only, verified against), which is what made the
bug easy to miss.
Recency of the last track point is a directly-observable signal
instead of reverse-engineering what a given `end` value means: Garmin
posts one roughly every postTrackPointFrequency seconds (~15, observed)
while genuinely still recording.
`bundle exec rake test` (minitest, a default gem but pinned in
Gemfile for reproducibility) now covers what ad-hoc scripts kept
re-verifying by hand during development: Gpx/Fit/TrackStats parsing,
GpxStore/LiveTrackStore file/TTL logic, LiveTrackClient's payload
parsing, and app.rb's routes end to end via Rack::MockRequest.
MAPTOOL_DATA_DIR (new, app.rb) lets the suite point GPX_STORE/
LIVETRACK_STORE at a throwaway directory instead of real data/ —
without it every run would leave uploaded test files behind. Each
lib file's old `if __FILE__ == $0` self-check is now redundant with
real tests covering the same ground, so removed rather than kept as
a second copy of the same assertions. Wired into ci.yml so it isn't
just a local convenience.