Files
doormile_backend/internal/assignment/balance_test.go

60 lines
2.0 KiB
Go

package assignment
import (
"testing"
"doormile/models"
)
func cand(id int, inHand, session int64, km, rating float64) *milerCandidate {
return &milerCandidate{
profile: models.MilerProfile{Userid: id, Rating: rating},
distanceKm: km,
activeBookings: inHand,
sessionStops: session,
}
}
// The balancing order: in hand, then this session, then distance, then rating.
func TestPickBestBalancesBeforeDistance(t *testing.T) {
cases := []struct {
name string
in []*milerCandidate
want int
}{
{"fewer in hand beats nearer", []*milerCandidate{cand(1, 2, 0, 0.2, 5), cand(2, 1, 9, 9.0, 1)}, 2},
{"tie in hand: fewer this session", []*milerCandidate{cand(1, 1, 5, 0.2, 5), cand(2, 1, 2, 4.0, 3)}, 2},
{"tie in hand and session: nearer", []*milerCandidate{cand(1, 1, 2, 3.0, 5), cand(2, 1, 2, 1.0, 3)}, 2},
{"full tie: better rating", []*milerCandidate{cand(1, 1, 2, 1.0, 4.1), cand(2, 1, 2, 1.0, 4.8)}, 2},
{"single candidate", []*milerCandidate{cand(7, 3, 3, 3, 3)}, 7},
}
for _, c := range cases {
if got := pickBestFromCandidates(c.in); got == nil || got.profile.Userid != c.want {
t.Errorf("%s: got %v, want rider %d", c.name, got, c.want)
}
}
if pickBestFromCandidates(nil) != nil {
t.Error("no candidates must give nil")
}
}
// leastLoaded keeps only the riders with the fewest in hand, with their
// parallel AI rows.
func TestLeastLoaded(t *testing.T) {
cs := []*milerCandidate{cand(1, 2, 0, 1, 4), cand(2, 0, 0, 2, 4), cand(3, 1, 0, 3, 4), cand(4, 0, 5, 4, 4)}
ai := []aiCandidate{{MilerID: 1}, {MilerID: 2}, {MilerID: 3}, {MilerID: 4}}
gotC, gotAI := leastLoaded(cs, ai)
if len(gotC) != 2 || gotC[0].profile.Userid != 2 || gotC[1].profile.Userid != 4 {
t.Fatalf("least loaded = %v", gotC)
}
if len(gotAI) != 2 || gotAI[0].MilerID != 2 || gotAI[1].MilerID != 4 {
t.Fatalf("AI rows must follow: %v", gotAI)
}
if c, a := leastLoaded(nil, nil); len(c) != 0 || len(a) != 0 {
t.Fatal("empty in, empty out")
}
if c, _ := leastLoaded(cs, nil); len(c) != 2 {
t.Fatal("AI rows are optional")
}
}