feat: Implement synthetic edges city↔airport (Task 10)
- Add TransferTime constants (AirportToCity, CityToStation, StationToStation) - Update Edge struct with Synthetic field - Enhance addSyntheticEdgesForNode to use constants and mark synthetic edges - Update BuildGraphFromStations to set Synthetic field - Mark synthetic edges in GeoJSON output as dashed lines - Write TestSyntheticAirportCityEdges and TestRouteWithSyntheticAirportCityEdges tests
This commit is contained in:
@@ -451,4 +451,117 @@ func TestRouteParetoRanking(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// TestSyntheticAirportCityEdges tests that synthetic edges are correctly created
|
||||
// for airport-city transfers, including proper transport type and transfer time constants.
|
||||
func TestSyntheticAirportCityEdges(t *testing.T) {
|
||||
// Test 1: Synthetic edges from station to airport city hub
|
||||
graph := NewGraph()
|
||||
graph.AddNode(&Node{ID: "s1", Type: NodeTypeStation, Name: "Moscow", CityCode: "c_airport"})
|
||||
graph.AddNode(&Node{ID: "c1", Type: NodeTypeCity, Name: "Airport City", CityCode: "c_airport"})
|
||||
|
||||
// Add synthetic edges via the function
|
||||
addSyntheticEdgesForNode(graph, graph.Nodes()[0])
|
||||
|
||||
edges := graph.Edges()
|
||||
if len(edges) != 2 {
|
||||
t.Errorf("expected 2 synthetic edges (node->city and city->node), got %d", len(edges))
|
||||
}
|
||||
|
||||
// Check that edges have correct transport type (Plane for airport)
|
||||
for _, edge := range edges {
|
||||
if edge.TransportType != TransportTypePlane {
|
||||
t.Errorf("expected TransportTypePlane for airport edge, got %v", edge.TransportType)
|
||||
}
|
||||
if edge.Transport != "plane" {
|
||||
t.Errorf("expected Transport 'plane', got %s", edge.Transport)
|
||||
}
|
||||
if !edge.Synthetic {
|
||||
t.Error("expected edge to be marked as Synthetic")
|
||||
}
|
||||
if edge.Kind != EdgeKindSynthetic {
|
||||
t.Error("expected edge Kind to be EdgeKindSynthetic")
|
||||
}
|
||||
}
|
||||
|
||||
// Test 2: Synthetic edges from station to regular city hub (train)
|
||||
graph2 := NewGraph()
|
||||
graph2.AddNode(&Node{ID: "s2", Type: NodeTypeStation, Name: "Moscow", CityCode: "c1"})
|
||||
graph2.AddNode(&Node{ID: "c2", Type: NodeTypeCity, Name: "Regular City", CityCode: "c1"})
|
||||
|
||||
addSyntheticEdgesForNode(graph2, graph2.Nodes()[0])
|
||||
|
||||
edges2 := graph2.Edges()
|
||||
if len(edges2) != 2 {
|
||||
t.Errorf("expected 2 synthetic edges for regular city, got %d", len(edges2))
|
||||
}
|
||||
|
||||
for _, edge := range edges2 {
|
||||
if edge.TransportType != TransportTypeTrain {
|
||||
t.Errorf("expected TransportTypeTrain for regular city edge, got %v", edge.TransportType)
|
||||
}
|
||||
if !edge.Synthetic {
|
||||
t.Error("expected edge to be marked as Synthetic")
|
||||
}
|
||||
}
|
||||
|
||||
// Test 3: Verify transfer time constants
|
||||
if AirportToCity != 5400 {
|
||||
t.Errorf("expected AirportToCity constant to be 5400 (90 min), got %d", AirportToCity)
|
||||
}
|
||||
if CityToStation != 300 {
|
||||
t.Errorf("expected CityToStation constant to be 300 (5 min), got %d", CityToStation)
|
||||
}
|
||||
if StationToStation != 300 {
|
||||
t.Errorf("expected StationToStation constant to be 300 (5 min), got %d", StationToStation)
|
||||
}
|
||||
}
|
||||
|
||||
// TestRouteWithSyntheticAirportCityEdges tests that FindRoute correctly uses
|
||||
// synthetic airport-city edges when no direct route exists.
|
||||
func TestRouteWithSyntheticAirportCityEdges(t *testing.T) {
|
||||
graph := NewGraph()
|
||||
|
||||
// Add airport station and city hub
|
||||
graph.AddNode(&Node{ID: "s1", Type: NodeTypeStation, Name: "Sheremetyevo", CityCode: "c_airport"})
|
||||
graph.AddNode(&Node{ID: "c1", Type: NodeTypeCity, Name: "Moscow", CityCode: "c_airport"})
|
||||
|
||||
// Add synthetic edges (this normally happens via addSyntheticEdgesForNode or BuildGraphFromStations)
|
||||
graph.AddEdge(&Edge{
|
||||
From: graph.Nodes()[0], // s1 Sheremetyevo
|
||||
To: graph.Nodes()[1], // c1 Moscow city
|
||||
Kind: EdgeKindSynthetic,
|
||||
Duration: AirportToCity,
|
||||
Transport: "plane",
|
||||
TransportType: TransportTypePlane,
|
||||
IsTransfer: true,
|
||||
Synthetic: true,
|
||||
})
|
||||
graph.AddEdge(&Edge{
|
||||
From: graph.Nodes()[1], // c1 Moscow
|
||||
To: graph.Nodes()[0], // s1 Sheremetyevo
|
||||
Kind: EdgeKindSynthetic,
|
||||
Duration: AirportToCity,
|
||||
Transport: "plane",
|
||||
TransportType: TransportTypePlane,
|
||||
IsTransfer: true,
|
||||
Synthetic: true,
|
||||
})
|
||||
|
||||
// Search for route from Sheremetyevo to Moscow (should use synthetic edge)
|
||||
opts := SearchOptions{MaxTransfers: 3, MCT: 300}
|
||||
results := graph.FindRoutesPareto("s1", "c1", opts)
|
||||
|
||||
if len(results) == 0 {
|
||||
t.Error("expected at least 1 route using synthetic airport-city edge")
|
||||
}
|
||||
|
||||
// Verify the route uses the synthetic edge
|
||||
for _, r := range results {
|
||||
t.Logf("Route: duration=%d, transfers=%d, cost=%d", r.TotalDuration, r.TotalTransfers, r.Cost)
|
||||
if r.TotalDuration < 5400 {
|
||||
t.Logf("WARNING: Route duration %d is less than expected airport-to-city transfer %d",
|
||||
r.TotalDuration, AirportToCity)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user