package traffic import ( "strings" "testing" ) func TestPolicyFor(t *testing.T) { cases := []struct { in string ok bool kph int }{ {"Road Closed", true, 5}, {"ACCIDENT", true, 25}, {"Accident - Multi Vehicle", true, 25}, {"road work", true, 50}, {"Bridge Outage (Unknown)", false, 0}, {"", false, 0}, } for _, c := range cases { p, ok := PolicyFor(c.in) if ok != c.ok || (ok && p.SpeedKmh != c.kph) { t.Errorf("PolicyFor(%q) = %+v, %v; want ok=%v kph=%d", c.in, p, ok, c.ok, c.kph) } } } func TestBuildCSV(t *testing.T) { // same pair, two incidents → most severe (highest weight) wins csv := BuildCSV([]Assignment{ {Pair: [2]int64{100, 200}, SpeedKmh: 50, Weight: 40}, {Pair: [2]int64{200, 100}, SpeedKmh: 5, Weight: 100}, // reversed order, must dedupe {Pair: [2]int64{300, 400}, SpeedKmh: 25, Weight: 90}, }) lines := strings.Split(strings.TrimSpace(csv), "\n") if len(lines) != 2 { t.Fatalf("want 2 rows, got %d: %v", len(lines), lines) } if lines[0] != "100,200,5" { t.Errorf("row0 = %q; want 100,200,5", lines[0]) } if lines[1] != "300,400,25" { t.Errorf("row1 = %q; want 300,400,25", lines[1]) } } func TestClassRank(t *testing.T) { if !(classRank("motorway") > classRank("trunk") && classRank("trunk") > classRank("primary") && classRank("primary") > classRank("residential")) { t.Errorf("classRank ordering wrong") } } func TestClosestNodePairPrefersClass(t *testing.T) { // motorway at 120m, service at 20m → motorway wins (within class window) ways := []Way{ { ID: 1, Highway: "service", Nodes: []WayNode{{ID: 11, Lat: 45.0000, Lon: -73.0000}, {ID: 12, Lat: 45.0000, Lon: -72.9990}}, }, { ID: 2, Highway: "motorway", Nodes: []WayNode{{ID: 21, Lat: 45.0011, Lon: -73.0000}, {ID: 22, Lat: 45.0011, Lon: -72.9990}}, }, } // point 20m from service line (lat 45.0000) and ~120m from motorway line (lat 45.0011) way, a, b, _, ok := ClosestNodePair(ways, 45.0002, -73.0) if !ok { t.Fatal("no match") } if way.Highway != "motorway" || a != 21 || b != 22 { t.Errorf("got %s (%d→%d); want motorway (21→22)", way.Highway, a, b) } } func TestClosestNodePairNearestFallback(t *testing.T) { // only a service road exists, 500m away → nearest still wins ways := []Way{ { ID: 1, Highway: "service", Nodes: []WayNode{{ID: 11, Lat: 45.0050, Lon: -73.0000}, {ID: 12, Lat: 45.0050, Lon: -72.9900}}, }, } way, _, _, d, ok := ClosestNodePair(ways, 45.0, -73.0) if !ok || way.ID != 1 { t.Fatalf("got ok=%v way=%+v", ok, way) } if d < 500 || d > 560 { t.Errorf("dist = %.0f; want ~500", d) } } func TestDecodeEvents(t *testing.T) { // OData v3 envelope body := []byte(`{"d":[{"EventType":"Road Closed","Description":"I-90 closed","City":"Albany","Location":{"Latitude":42.75,"Longitude":-73.93}},{"EventType":"Accident","lat":42.1,"lon":-73.5}]}`) events, err := decodeEvents(body) if err != nil { t.Fatal(err) } if len(events) != 2 { t.Fatalf("want 2 events, got %d", len(events)) } e0 := events[0] if e0.Type != "Road Closed" || !e0.Valid() || e0.Lat != 42.75 || e0.Lon != -73.93 { t.Errorf("event0 = %+v", e0) } e1 := events[1] if e1.Type != "Accident" || !e1.Valid() { t.Errorf("event1 = %+v", e1) } }