package routing import ( "fmt" "testing" ) func TestFindRouteMaxTransfers(t *testing.T) { graph := NewGraph() // Create 6 stations: s1, s2, s3, s4, s5, s6 for i := 0; i < 6; i++ { graph.AddNode(&Node{ID: fmt.Sprintf("s%d", i+1), Type: NodeTypeStation, Name: fmt.Sprintf("Station %d", i+1), CityCode: "c1"}) } // Add direct edge s1 -> s6 (0 transfers) graph.AddEdge(&Edge{ From: graph.Nodes()[0], // s1 To: graph.Nodes()[5], // s6 Kind: EdgeKindReal, Duration: 3600, Transport: "train", TransportType: TransportTypeTrain, IsTransfer: false, }) // Add chain edges s1->s2->s3->s4->s5->s6 (each is a transfer edge) for i := 0; i < 5; i++ { graph.AddEdge(&Edge{ From: graph.Nodes()[i], To: graph.Nodes()[i+1], Kind: EdgeKindReal, Duration: 1000, Transport: "train", TransportType: TransportTypeTrain, IsTransfer: true, }) } // Test with MaxTransfers=0: should only find the direct route (0 transfers) opts0 := SearchOptions{MaxTransfers: 0} results0 := graph.FindRoutesPareto("s1", "s6", opts0) t.Logf("MaxTransfers=0: found %d route(s)", len(results0)) for _, r := range results0 { t.Logf(" Route: duration=%d, transfers=%d", r.TotalDuration, r.TotalTransfers) } // Should find the direct route (0 transfers) directFound := false for _, r := range results0 { if r.TotalTransfers == 0 { directFound = true break } } if !directFound { t.Error("expected direct route (0 transfers) with MaxTransfers=0") return } // Test with MaxTransfers=1: should find direct route + 1-transfer route if any opts1 := SearchOptions{MaxTransfers: 1} results1 := graph.FindRoutesPareto("s1", "s6", opts1) t.Logf("MaxTransfers=1: found %d route(s)", len(results1)) for _, r := range results1 { t.Logf(" Route: duration=%d, transfers=%d", r.TotalDuration, r.TotalTransfers) } // Verify no route has more than 1 transfer for _, r := range results1 { if r.TotalTransfers > 1 { t.Errorf("route with MaxTransfers=1 has %d transfers, expected <= 1", r.TotalTransfers) } } // Test with MaxTransfers=2: should find more routes opts2 := SearchOptions{MaxTransfers: 2} results2 := graph.FindRoutesPareto("s1", "s6", opts2) t.Logf("MaxTransfers=2: found %d route(s)", len(results2)) for _, r := range results2 { t.Logf(" Route: duration=%d, transfers=%d", r.TotalDuration, r.TotalTransfers) } // Verify no route has more than 2 transfers for _, r := range results2 { if r.TotalTransfers > 2 { t.Errorf("route with MaxTransfers=2 has %d transfers, expected <= 2", r.TotalTransfers) } } } // TestLazyExpansionDepthLimit tests that BFS stops expanding when transfer depth exceeds MaxTransfers. func TestLazyExpansionDepthLimit(t *testing.T) { graph := NewGraph() // Create 7 stations: s1, s2, s3, s4, s5, s6, s7 for i := 0; i < 7; i++ { graph.AddNode(&Node{ID: fmt.Sprintf("s%d", i+1), Type: NodeTypeStation, Name: fmt.Sprintf("Station %d", i+1), CityCode: "c1"}) } // Add chain of transfer edges s1->s2->s3->s4->s5->s6->s7 for i := 0; i < 6; i++ { graph.AddEdge(&Edge{ From: graph.Nodes()[i], To: graph.Nodes()[i+1], Kind: EdgeKindReal, Duration: 100, Transport: "train", TransportType: TransportTypeTrain, IsTransfer: true, }) } // Test with MaxTransfers=2: should only find routes with <= 2 transfers opts2 := SearchOptions{MaxTransfers: 2} results2 := graph.FindRoute("s1", "s7", opts2) if results2 != nil { t.Logf("MaxTransfers=2: found route with %d transfers", results2.TotalTransfers) for _, leg := range results2.Legs { t.Logf(" Leg: %s -> %s (isTransfer=%v)", leg.From.Name, leg.To.Name, leg.IsTransfer) } // With MaxTransfers=2, a chain of 6 transfers (s1->...->s7) should not be found if results2.TotalTransfers > 2 { t.Errorf("expected <= 2 transfers with MaxTransfers=2, got %d", results2.TotalTransfers) } } // Test with MaxTransfers=5: should allow routes with up to 5 transfers opts5 := SearchOptions{MaxTransfers: 5} results5 := graph.FindRoute("s1", "s7", opts5) if results5 != nil { t.Logf("MaxTransfers=5: found route with %d transfers", results5.TotalTransfers) if results5.TotalTransfers > 5 { t.Errorf("expected <= 5 transfers with MaxTransfers=5, got %d", results5.TotalTransfers) } } else { t.Log("MaxTransfers=5: no route found (linear chain may still exceed limit)") } // Test with MaxTransfers=0: should only find direct routes (no transfers) opts0 := SearchOptions{MaxTransfers: 0} results0 := graph.FindRoute("s1", "s7", opts0) if results0 != nil { t.Logf("MaxTransfers=0: found route with %d transfers", results0.TotalTransfers) for _, leg := range results0.Legs { t.Logf(" Leg: %s -> %s (isTransfer=%v)", leg.From.Name, leg.To.Name, leg.IsTransfer) } if results0.TotalTransfers != 0 { t.Errorf("expected 0 transfers with MaxTransfers=0, got %d", results0.TotalTransfers) } } else { t.Log("MaxTransfers=0: no direct route s1->s7 found (only chain edges exist)") } }