mirror of
https://github.com/AmanTahiliani/box-box.git
synced 2026-08-07 11:54:59 -04:00
Tighten race result ingestion ordering
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import type { EnrichedResult, Stint, PitStop } from '../types'
|
import type { EnrichedResult, Stint, PitStop } from '../types'
|
||||||
|
import { compareFinishPosition } from '../utils'
|
||||||
|
|
||||||
const COMPOUND_COLORS: Record<string, string> = {
|
const COMPOUND_COLORS: Record<string, string> = {
|
||||||
SOFT: '#e8002d',
|
SOFT: '#e8002d',
|
||||||
@@ -84,7 +85,10 @@ export function StrategyView({ results, stints, pit_stops, hasStints }: Props) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const sortedDrivers = [...results].sort((a, b) => a.position - b.position)
|
const sortedDrivers = [...results].sort((a, b) => {
|
||||||
|
const cmp = compareFinishPosition(a.position, b.position)
|
||||||
|
return cmp !== 0 ? cmp : a.driver_number - b.driver_number
|
||||||
|
})
|
||||||
const totalLaps = Math.max(
|
const totalLaps = Math.max(
|
||||||
...stints.map((s) => s.lap_end),
|
...stints.map((s) => s.lap_end),
|
||||||
...results.map((r) => r.number_of_laps),
|
...results.map((r) => r.number_of_laps),
|
||||||
|
|||||||
@@ -64,3 +64,12 @@ export function positionClass(pos: number): string {
|
|||||||
if (pos === 3) return 'pos-p3'
|
if (pos === 3) return 'pos-p3'
|
||||||
return 'pos-n'
|
return 'pos-n'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Classified finish order; position 0 (DNF/DNS) sorts last. */
|
||||||
|
export function finishPositionOrder(pos: number): number {
|
||||||
|
return pos > 0 ? pos : 9999
|
||||||
|
}
|
||||||
|
|
||||||
|
export function compareFinishPosition(a: number, b: number): number {
|
||||||
|
return finishPositionOrder(a) - finishPositionOrder(b)
|
||||||
|
}
|
||||||
|
|||||||
@@ -358,6 +358,7 @@ func (c *OpenF1Client) GetSessionResult(sessionKey int) ([]models.SessionResult,
|
|||||||
if err := json.NewDecoder(body).Decode(&result); err != nil {
|
if err := json.NewDecoder(body).Decode(&result); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
models.SortSessionResults(result)
|
||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -217,7 +217,7 @@ func (s *Service) IngestMeeting(meetingKey int) (Summary, error) {
|
|||||||
partialSessions := 0
|
partialSessions := 0
|
||||||
for _, sess := range sessions {
|
for _, sess := range sessions {
|
||||||
s.opts.Progress.Step("ingesting Race Hub datasets for session %d (%s)", sess.SessionKey, sess.SessionName)
|
s.opts.Progress.Step("ingesting Race Hub datasets for session %d (%s)", sess.SessionKey, sess.SessionName)
|
||||||
sessSummary, err := s.ingestSessionDatasets(sess.SessionKey, meetingKey)
|
sessSummary, err := s.ingestSessionDatasets(sess)
|
||||||
ss := SessionSummary{
|
ss := SessionSummary{
|
||||||
SessionKey: sess.SessionKey,
|
SessionKey: sess.SessionKey,
|
||||||
SessionName: sess.SessionName,
|
SessionName: sess.SessionName,
|
||||||
@@ -332,7 +332,7 @@ func (s *Service) IngestSession(sessionKey int) (Summary, error) {
|
|||||||
summary.Sessions++
|
summary.Sessions++
|
||||||
}
|
}
|
||||||
|
|
||||||
datasetSummary, err := s.ingestSessionDatasets(sessionKey, meetingKey)
|
datasetSummary, err := s.ingestSessionDatasets(sess)
|
||||||
summary.mergeCounts(datasetSummary)
|
summary.mergeCounts(datasetSummary)
|
||||||
summary.Errors = append(summary.Errors, datasetSummary.Errors...)
|
summary.Errors = append(summary.Errors, datasetSummary.Errors...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -345,7 +345,9 @@ func (s *Service) IngestSession(sessionKey int) (Summary, error) {
|
|||||||
return summary, nil
|
return summary, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Service) ingestSessionDatasets(sessionKey, meetingKey int) (Summary, error) {
|
func (s *Service) ingestSessionDatasets(sess models.Session) (Summary, error) {
|
||||||
|
sessionKey := sess.SessionKey
|
||||||
|
meetingKey := sess.MeetingKey
|
||||||
summary := Summary{
|
summary := Summary{
|
||||||
ScopeType: "session",
|
ScopeType: "session",
|
||||||
ScopeKey: fmt.Sprintf("%d", sessionKey),
|
ScopeKey: fmt.Sprintf("%d", sessionKey),
|
||||||
@@ -410,37 +412,13 @@ func (s *Service) ingestSessionDatasets(sessionKey, meetingKey int) (Summary, er
|
|||||||
}
|
}
|
||||||
s.delay()
|
s.delay()
|
||||||
|
|
||||||
s.opts.Progress.Step("fetching starting grid for session %d", sessionKey)
|
|
||||||
gridFetch, grid, err := fetchWithRetry(s, func() (FetchResult, []models.StartingGrid, error) {
|
|
||||||
return s.source.FetchStartingGrid(sessionKey)
|
|
||||||
})
|
|
||||||
if err != nil {
|
|
||||||
return summary, err
|
|
||||||
}
|
|
||||||
summary.RawPayloads++
|
|
||||||
if !s.opts.DryRun {
|
|
||||||
inserted, err := s.storeRaw(gridFetch, &meetingKey, &sk)
|
|
||||||
if err != nil {
|
|
||||||
return summary, err
|
|
||||||
}
|
|
||||||
if inserted {
|
|
||||||
summary.RawInserted++
|
|
||||||
}
|
|
||||||
for _, g := range grid {
|
|
||||||
if err := s.store.UpsertStartingGridEntry(startingGridToStore(g)); err != nil {
|
|
||||||
return summary, err
|
|
||||||
}
|
|
||||||
summary.StartingGrid++
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
summary.StartingGrid = len(grid)
|
|
||||||
}
|
|
||||||
s.delay()
|
|
||||||
|
|
||||||
optionalIngests := []struct {
|
optionalIngests := []struct {
|
||||||
name string
|
name string
|
||||||
run func(*Summary, int, int) error
|
run func(*Summary, int, int) error
|
||||||
}{
|
}{
|
||||||
|
{name: "starting_grid", run: func(summary *Summary, meetingKey, sessionKey int) error {
|
||||||
|
return s.ingestStartingGrid(summary, sess)
|
||||||
|
}},
|
||||||
{name: "stints", run: s.ingestStints},
|
{name: "stints", run: s.ingestStints},
|
||||||
{name: "pit_stops", run: s.ingestPitStops},
|
{name: "pit_stops", run: s.ingestPitStops},
|
||||||
{name: "positions", run: s.ingestPositions},
|
{name: "positions", run: s.ingestPositions},
|
||||||
@@ -534,6 +512,86 @@ func (s *Service) delay() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *Service) ingestStartingGrid(summary *Summary, sess models.Session) error {
|
||||||
|
sessionKey := sess.SessionKey
|
||||||
|
meetingKey := sess.MeetingKey
|
||||||
|
sourceSessionKey, err := s.startingGridSourceSessionKey(sess)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if sourceSessionKey == sessionKey {
|
||||||
|
s.opts.Progress.Step("fetching starting grid for session %d", sessionKey)
|
||||||
|
} else {
|
||||||
|
s.opts.Progress.Step("fetching starting grid for session %d from qualifying session %d", sessionKey, sourceSessionKey)
|
||||||
|
}
|
||||||
|
fetch, grid, err := fetchWithRetry(s, func() (FetchResult, []models.StartingGrid, error) {
|
||||||
|
return s.source.FetchStartingGrid(sourceSessionKey)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if sourceSessionKey != sessionKey {
|
||||||
|
fetch.RequestKey = fmt.Sprintf("%s;target_session_key=%d", fetch.RequestKey, sessionKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
mk := meetingKey
|
||||||
|
sk := sessionKey
|
||||||
|
summary.RawPayloads++
|
||||||
|
if s.opts.DryRun {
|
||||||
|
summary.StartingGrid = len(grid)
|
||||||
|
s.delay()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
inserted, err := s.storeRaw(fetch, &mk, &sk)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if inserted {
|
||||||
|
summary.RawInserted++
|
||||||
|
}
|
||||||
|
for _, g := range grid {
|
||||||
|
g.SessionKey = sessionKey
|
||||||
|
g.MeetingKey = meetingKey
|
||||||
|
if err := s.store.UpsertStartingGridEntry(startingGridToStore(g)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
summary.StartingGrid++
|
||||||
|
}
|
||||||
|
s.delay()
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Service) startingGridSourceSessionKey(sess models.Session) (int, error) {
|
||||||
|
if !isRaceSession(sess) {
|
||||||
|
return sess.SessionKey, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
_, sessions, err := fetchWithRetry(s, func() (FetchResult, []models.Session, error) {
|
||||||
|
return s.source.FetchSessionsForMeeting(sess.MeetingKey)
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
s.delay()
|
||||||
|
|
||||||
|
for _, candidate := range sessions {
|
||||||
|
if isQualifyingSession(candidate) {
|
||||||
|
return candidate.SessionKey, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0, fmt.Errorf("no qualifying session found for meeting %d", sess.MeetingKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
func isRaceSession(sess models.Session) bool {
|
||||||
|
return strings.EqualFold(sess.SessionType, "Race") || strings.EqualFold(sess.SessionName, "Race")
|
||||||
|
}
|
||||||
|
|
||||||
|
func isQualifyingSession(sess models.Session) bool {
|
||||||
|
return strings.EqualFold(sess.SessionType, "Qualifying") || strings.EqualFold(sess.SessionName, "Qualifying")
|
||||||
|
}
|
||||||
|
|
||||||
func (s *Service) ingestStints(summary *Summary, meetingKey, sessionKey int) error {
|
func (s *Service) ingestStints(summary *Summary, meetingKey, sessionKey int) error {
|
||||||
s.opts.Progress.Step("fetching stints for session %d", sessionKey)
|
s.opts.Progress.Step("fetching stints for session %d", sessionKey)
|
||||||
fetch, stints, err := fetchWithRetry(s, func() (FetchResult, []models.Stint, error) {
|
fetch, stints, err := fetchWithRetry(s, func() (FetchResult, []models.Stint, error) {
|
||||||
|
|||||||
@@ -199,6 +199,7 @@ func openTestStore(t *testing.T) *store.Store {
|
|||||||
func testSessionFixtures() (int, int, *fakeSource) {
|
func testSessionFixtures() (int, int, *fakeSource) {
|
||||||
const meetingKey = 1229
|
const meetingKey = 1229
|
||||||
const sessionKey = 9472
|
const sessionKey = 9472
|
||||||
|
const qualifyingKey = 9471
|
||||||
|
|
||||||
src := newFakeSource()
|
src := newFakeSource()
|
||||||
src.meetingsByKey[meetingKey] = []models.Meeting{{
|
src.meetingsByKey[meetingKey] = []models.Meeting{{
|
||||||
@@ -221,6 +222,16 @@ func testSessionFixtures() (int, int, *fakeSource) {
|
|||||||
SessionType: "Race",
|
SessionType: "Race",
|
||||||
CircuitKey: 10,
|
CircuitKey: 10,
|
||||||
}}
|
}}
|
||||||
|
src.sessionsByMeeting[meetingKey] = []models.Session{
|
||||||
|
{
|
||||||
|
SessionKey: qualifyingKey,
|
||||||
|
MeetingKey: meetingKey,
|
||||||
|
SessionName: "Qualifying",
|
||||||
|
SessionType: "Qualifying",
|
||||||
|
CircuitKey: 10,
|
||||||
|
},
|
||||||
|
src.sessionsByKey[sessionKey][0],
|
||||||
|
}
|
||||||
src.drivers[sessionKey] = []models.Driver{
|
src.drivers[sessionKey] = []models.Driver{
|
||||||
{
|
{
|
||||||
DriverNumber: 1,
|
DriverNumber: 1,
|
||||||
@@ -243,9 +254,9 @@ func testSessionFixtures() (int, int, *fakeSource) {
|
|||||||
{SessionKey: sessionKey, MeetingKey: meetingKey, DriverNumber: 1, Position: 1, Points: 25, NumberOfLaps: 78},
|
{SessionKey: sessionKey, MeetingKey: meetingKey, DriverNumber: 1, Position: 1, Points: 25, NumberOfLaps: 78},
|
||||||
{SessionKey: sessionKey, MeetingKey: meetingKey, DriverNumber: 44, Position: 2, Points: 18, NumberOfLaps: 78, GapToLeader: 1.5},
|
{SessionKey: sessionKey, MeetingKey: meetingKey, DriverNumber: 44, Position: 2, Points: 18, NumberOfLaps: 78, GapToLeader: 1.5},
|
||||||
}
|
}
|
||||||
src.grid[sessionKey] = []models.StartingGrid{
|
src.grid[qualifyingKey] = []models.StartingGrid{
|
||||||
{SessionKey: sessionKey, MeetingKey: meetingKey, DriverNumber: 1, Position: 1, LapDuration: 71.234},
|
{SessionKey: qualifyingKey, MeetingKey: meetingKey, DriverNumber: 1, Position: 1, LapDuration: 71.234},
|
||||||
{SessionKey: sessionKey, MeetingKey: meetingKey, DriverNumber: 44, Position: 2, LapDuration: 71.456},
|
{SessionKey: qualifyingKey, MeetingKey: meetingKey, DriverNumber: 44, Position: 2, LapDuration: 71.456},
|
||||||
}
|
}
|
||||||
src.stints[sessionKey] = []models.Stint{
|
src.stints[sessionKey] = []models.Stint{
|
||||||
{SessionKey: sessionKey, MeetingKey: meetingKey, DriverNumber: 1, StintNumber: 1, Compound: models.CompoundMedium, LapStart: 1, LapEnd: 30},
|
{SessionKey: sessionKey, MeetingKey: meetingKey, DriverNumber: 1, StintNumber: 1, Compound: models.CompoundMedium, LapStart: 1, LapEnd: 30},
|
||||||
@@ -363,6 +374,11 @@ func TestIngestSessionWritesDomainAndRawRows(t *testing.T) {
|
|||||||
if len(grid) != 2 {
|
if len(grid) != 2 {
|
||||||
t.Fatalf("starting grid = %d, want 2", len(grid))
|
t.Fatalf("starting grid = %d, want 2", len(grid))
|
||||||
}
|
}
|
||||||
|
for _, g := range grid {
|
||||||
|
if g.SessionKey != sessionKey {
|
||||||
|
t.Fatalf("starting grid row session_key = %d, want race session %d", g.SessionKey, sessionKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
raw, err := st.ListRawPayloadsBySession(sessionKey)
|
raw, err := st.ListRawPayloadsBySession(sessionKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -371,6 +387,18 @@ func TestIngestSessionWritesDomainAndRawRows(t *testing.T) {
|
|||||||
if len(raw) != 11 {
|
if len(raw) != 11 {
|
||||||
t.Fatalf("raw payloads = %d, want 11", len(raw))
|
t.Fatalf("raw payloads = %d, want 11", len(raw))
|
||||||
}
|
}
|
||||||
|
foundGridRaw := false
|
||||||
|
for _, p := range raw {
|
||||||
|
if p.Endpoint == "starting_grid" {
|
||||||
|
foundGridRaw = true
|
||||||
|
if p.RequestKey != "session_key=9471;target_session_key=9472" {
|
||||||
|
t.Fatalf("starting grid raw request key = %q, want qualifying source and race target", p.RequestKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !foundGridRaw {
|
||||||
|
t.Fatal("raw payloads missing starting_grid")
|
||||||
|
}
|
||||||
|
|
||||||
stints, err := st.ListStints(sessionKey)
|
stints, err := st.ListStints(sessionKey)
|
||||||
if err != nil || len(stints) != 2 {
|
if err != nil || len(stints) != 2 {
|
||||||
@@ -521,6 +549,38 @@ func TestOptionalAnalyticsErrorMakesSessionPartialAndContinues(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestStartingGridErrorMakesSessionPartialAndContinues(t *testing.T) {
|
||||||
|
_, sessionKey, src := testSessionFixtures()
|
||||||
|
src.failOn = "starting_grid"
|
||||||
|
st := openTestStore(t)
|
||||||
|
|
||||||
|
opts := DefaultOptions()
|
||||||
|
opts.RequestDelay = 0
|
||||||
|
svc := NewService(st, src, opts)
|
||||||
|
|
||||||
|
summary, err := svc.IngestSession(sessionKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("IngestSession() error = %v, want nil partial result", err)
|
||||||
|
}
|
||||||
|
if summary.Status != "partial" {
|
||||||
|
t.Fatalf("summary.Status = %q, want partial", summary.Status)
|
||||||
|
}
|
||||||
|
if len(summary.Errors) != 1 {
|
||||||
|
t.Fatalf("summary.Errors = %v, want 1 starting_grid error", summary.Errors)
|
||||||
|
}
|
||||||
|
if summary.StartingGrid != 0 {
|
||||||
|
t.Fatalf("summary.StartingGrid = %d, want 0 for failed starting_grid", summary.StartingGrid)
|
||||||
|
}
|
||||||
|
if summary.Stints != 2 || summary.PitStops != 1 || summary.Positions != 3 || summary.RaceControl != 1 || summary.Weather != 1 || summary.Laps != 1 {
|
||||||
|
t.Fatalf("analytics counts after starting_grid failure = %+v, want remaining analytics preserved", summary)
|
||||||
|
}
|
||||||
|
|
||||||
|
stints, err := st.ListStints(sessionKey)
|
||||||
|
if err != nil || len(stints) != 2 {
|
||||||
|
t.Fatalf("stints = %+v, err = %v, want 2 preserved", stints, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestLiveSessionLockoutSurfacesControlledFailure(t *testing.T) {
|
func TestLiveSessionLockoutSurfacesControlledFailure(t *testing.T) {
|
||||||
_, sessionKey, src := testSessionFixtures()
|
_, sessionKey, src := testSessionFixtures()
|
||||||
src.liveLockout = true
|
src.liveLockout = true
|
||||||
|
|||||||
20
internal/models/sort.go
Normal file
20
internal/models/sort.go
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "sort"
|
||||||
|
|
||||||
|
// SortSessionResults orders results by classified position, with position 0 (DNF/DNS) last.
|
||||||
|
func SortSessionResults(results []SessionResult) {
|
||||||
|
sort.Slice(results, func(i, j int) bool {
|
||||||
|
pi, pj := results[i].Position, results[j].Position
|
||||||
|
if pi == 0 {
|
||||||
|
pi = 9999
|
||||||
|
}
|
||||||
|
if pj == 0 {
|
||||||
|
pj = 9999
|
||||||
|
}
|
||||||
|
if pi != pj {
|
||||||
|
return pi < pj
|
||||||
|
}
|
||||||
|
return results[i].DriverNumber < results[j].DriverNumber
|
||||||
|
})
|
||||||
|
}
|
||||||
21
internal/models/sort_test.go
Normal file
21
internal/models/sort_test.go
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
package models
|
||||||
|
|
||||||
|
import "testing"
|
||||||
|
|
||||||
|
func TestSortSessionResults(t *testing.T) {
|
||||||
|
results := []SessionResult{
|
||||||
|
{DriverNumber: 55, Position: 0, DNF: true},
|
||||||
|
{DriverNumber: 10, Position: 0, DNS: true},
|
||||||
|
{DriverNumber: 1, Position: 1},
|
||||||
|
{DriverNumber: 44, Position: 2},
|
||||||
|
}
|
||||||
|
|
||||||
|
SortSessionResults(results)
|
||||||
|
|
||||||
|
want := []int{1, 44, 10, 55}
|
||||||
|
for i, dn := range want {
|
||||||
|
if results[i].DriverNumber != dn {
|
||||||
|
t.Fatalf("results[%d].DriverNumber = %d, want %d", i, results[i].DriverNumber, dn)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -183,7 +183,7 @@ func (s *Store) ListSessionResults(sessionKey int) ([]SessionResult, error) {
|
|||||||
number_of_laps, duration_json, gap_to_leader_json, dnf, dns, dsq
|
number_of_laps, duration_json, gap_to_leader_json, dnf, dns, dsq
|
||||||
FROM session_results
|
FROM session_results
|
||||||
WHERE session_key = ?
|
WHERE session_key = ?
|
||||||
ORDER BY position ASC, driver_number ASC
|
ORDER BY CASE WHEN position > 0 THEN position ELSE 9999 END ASC, driver_number ASC
|
||||||
`, sessionKey)
|
`, sessionKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|||||||
@@ -443,6 +443,56 @@ func TestSessionResultAndStartingGridUpsertRead(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestListSessionResultsSortsNonFinishersLast(t *testing.T) {
|
||||||
|
s := openTestStore(t)
|
||||||
|
|
||||||
|
meetingKey := 1230
|
||||||
|
sessionKey := 9473
|
||||||
|
|
||||||
|
if err := s.UpsertMeeting(Meeting{
|
||||||
|
MeetingKey: meetingKey,
|
||||||
|
MeetingName: "Test GP",
|
||||||
|
Year: 2025,
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("UpsertMeeting() error = %v", err)
|
||||||
|
}
|
||||||
|
if err := s.UpsertSession(Session{
|
||||||
|
SessionKey: sessionKey,
|
||||||
|
MeetingKey: meetingKey,
|
||||||
|
SessionName: "Race",
|
||||||
|
SessionType: "Race",
|
||||||
|
}); err != nil {
|
||||||
|
t.Fatalf("UpsertSession() error = %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
entries := []SessionResult{
|
||||||
|
{SessionKey: sessionKey, DriverNumber: 55, MeetingKey: meetingKey, Position: 0, DNF: true},
|
||||||
|
{SessionKey: sessionKey, DriverNumber: 10, MeetingKey: meetingKey, Position: 0, DNS: true},
|
||||||
|
{SessionKey: sessionKey, DriverNumber: 1, MeetingKey: meetingKey, Position: 1, Points: 25},
|
||||||
|
{SessionKey: sessionKey, DriverNumber: 44, MeetingKey: meetingKey, Position: 2, Points: 18},
|
||||||
|
}
|
||||||
|
for _, r := range entries {
|
||||||
|
if err := s.UpsertSessionResult(r); err != nil {
|
||||||
|
t.Fatalf("UpsertSessionResult(%d) error = %v", r.DriverNumber, err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results, err := s.ListSessionResults(sessionKey)
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("ListSessionResults() error = %v", err)
|
||||||
|
}
|
||||||
|
if len(results) != len(entries) {
|
||||||
|
t.Fatalf("ListSessionResults() len = %d, want %d", len(results), len(entries))
|
||||||
|
}
|
||||||
|
|
||||||
|
wantOrder := []int{1, 44, 10, 55}
|
||||||
|
for i, want := range wantOrder {
|
||||||
|
if results[i].DriverNumber != want {
|
||||||
|
t.Fatalf("results[%d].DriverNumber = %d, want %d", i, results[i].DriverNumber, want)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func TestAnalyticsUpsertRead(t *testing.T) {
|
func TestAnalyticsUpsertRead(t *testing.T) {
|
||||||
s := openTestStore(t)
|
s := openTestStore(t)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user