Files
satellite-provider/SatelliteProvider.IntegrationTests/RouteTests.cs
T
Anton Martynenko f8798cd3d3 more complex route
2025-11-01 17:24:59 +01:00

507 lines
20 KiB
C#

using System.Net.Http.Json;
using System.Text.Json;
namespace SatelliteProvider.IntegrationTests;
public static class RouteTests
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
PropertyNameCaseInsensitive = true
};
public static async Task RunSimpleRouteTest(HttpClient httpClient)
{
Console.WriteLine("Test: Create Simple Route with Two Points");
Console.WriteLine("-----------------------------------------");
var routeId = Guid.NewGuid();
var request = new CreateRouteRequest
{
Id = routeId,
Name = "Simple Test Route",
Description = "Test route with 2 points",
RegionSizeMeters = 500.0,
ZoomLevel = 18,
Points = new List<RoutePointInput>
{
new() { Latitude = 48.276067180586544, Longitude = 37.38445758819581 },
new() { Latitude = 48.27074009522731, Longitude = 37.374029159545906 }
}
};
Console.WriteLine($"Creating route with 2 points:");
Console.WriteLine($" Start: ({request.Points[0].Latitude}, {request.Points[0].Longitude})");
Console.WriteLine($" End: ({request.Points[1].Latitude}, {request.Points[1].Longitude})");
Console.WriteLine($" Region Size: {request.RegionSizeMeters}m");
Console.WriteLine($" Zoom Level: {request.ZoomLevel}");
Console.WriteLine();
var response = await httpClient.PostAsJsonAsync("/api/satellite/route", request);
if (!response.IsSuccessStatusCode)
{
var errorContent = await response.Content.ReadAsStringAsync();
throw new Exception($"API returned error status {response.StatusCode}: {errorContent}");
}
var route = await response.Content.ReadFromJsonAsync<RouteResponseModel>(JsonOptions);
if (route == null)
{
throw new Exception("No route data returned from API");
}
Console.WriteLine("Route Details:");
Console.WriteLine($" ID: {route.Id}");
Console.WriteLine($" Name: {route.Name}");
Console.WriteLine($" Total Points: {route.TotalPoints}");
Console.WriteLine($" Total Distance: {route.TotalDistanceMeters:F2}m");
Console.WriteLine();
var startPoints = route.Points.Count(p => p.PointType == "start");
var endPoints = route.Points.Count(p => p.PointType == "end");
var intermediatePoints = route.Points.Count(p => p.PointType == "intermediate");
Console.WriteLine("Point Types:");
Console.WriteLine($" Start points: {startPoints}");
Console.WriteLine($" Intermediate points: {intermediatePoints}");
Console.WriteLine($" End points: {endPoints}");
Console.WriteLine();
if (startPoints != 1)
{
throw new Exception($"Expected 1 start point, got {startPoints}");
}
if (endPoints != 1)
{
throw new Exception($"Expected 1 end point, got {endPoints}");
}
Console.WriteLine("Point spacing validation:");
for (int i = 1; i < route.Points.Count; i++)
{
var point = route.Points[i];
if (point.DistanceFromPrevious.HasValue)
{
if (point.DistanceFromPrevious.Value > 200.0)
{
throw new Exception($"Point {i} is {point.DistanceFromPrevious.Value:F2}m from previous, exceeds 200m limit");
}
Console.WriteLine($" Point {i} ({point.PointType}): {point.DistanceFromPrevious.Value:F2}m from previous");
}
}
Console.WriteLine();
Console.WriteLine("Retrieving route by ID...");
var getResponse = await httpClient.GetAsync($"/api/satellite/route/{routeId}");
if (!getResponse.IsSuccessStatusCode)
{
throw new Exception($"Failed to retrieve route: {getResponse.StatusCode}");
}
var retrievedRoute = await getResponse.Content.ReadFromJsonAsync<RouteResponseModel>(JsonOptions);
if (retrievedRoute == null || retrievedRoute.Id != routeId)
{
throw new Exception("Retrieved route does not match created route");
}
Console.WriteLine($"✓ Route retrieved successfully");
Console.WriteLine($"✓ Retrieved {retrievedRoute.Points.Count} points");
Console.WriteLine();
Console.WriteLine("✓ Route created successfully");
Console.WriteLine("✓ All point spacing validated (≤ 200m)");
Console.WriteLine();
Console.WriteLine("Simple Route Test: PASSED");
}
public static async Task RunRouteWithRegionProcessingAndStitching(HttpClient httpClient)
{
Console.WriteLine();
Console.WriteLine("Test: Route with Region Processing and Full Map Stitching (Service-Level)");
Console.WriteLine("===========================================================================");
Console.WriteLine();
var routeId = Guid.NewGuid();
var request = new CreateRouteRequest
{
Id = routeId,
Name = "Route with Region Processing",
Description = "Test route that processes regions for all points and stitches a full map",
RegionSizeMeters = 300.0,
ZoomLevel = 18,
RequestMaps = true,
Points = new List<RoutePointInput>
{
new() { Latitude = 48.276067180586544, Longitude = 37.38445758819581 },
new() { Latitude = 48.27074009522731, Longitude = 37.374029159545906 }
}
};
Console.WriteLine("Step 1: Creating route with RequestMaps=true");
Console.WriteLine($" Start: ({request.Points[0].Latitude}, {request.Points[0].Longitude})");
Console.WriteLine($" End: ({request.Points[1].Latitude}, {request.Points[1].Longitude})");
Console.WriteLine($" Region Size: {request.RegionSizeMeters}m");
Console.WriteLine($" Zoom Level: {request.ZoomLevel}");
Console.WriteLine($" Request Maps: {request.RequestMaps}");
Console.WriteLine();
var routeResponse = await httpClient.PostAsJsonAsync("/api/satellite/route", request);
if (!routeResponse.IsSuccessStatusCode)
{
var errorContent = await routeResponse.Content.ReadAsStringAsync();
throw new Exception($"Route creation failed: {routeResponse.StatusCode} - {errorContent}");
}
var route = await routeResponse.Content.ReadFromJsonAsync<RouteResponseModel>(JsonOptions);
if (route == null)
{
throw new Exception("No route data returned");
}
Console.WriteLine($"✓ Route created with {route.TotalPoints} points");
Console.WriteLine($" Distance: {route.TotalDistanceMeters:F2}m");
Console.WriteLine($" Request Maps: {route.RequestMaps}");
Console.WriteLine($" Maps Ready: {route.MapsReady}");
Console.WriteLine();
if (!route.RequestMaps)
{
throw new Exception("Expected RequestMaps to be true");
}
if (route.MapsReady)
{
throw new Exception("Expected MapsReady to be false initially");
}
Console.WriteLine("Step 2: Waiting for route maps to be ready");
Console.WriteLine(" (Service is processing regions SEQUENTIALLY to avoid API throttling)");
Console.WriteLine();
RouteResponseModel? finalRoute = null;
int maxAttempts = 180;
int pollInterval = 3000;
for (int attempt = 0; attempt < maxAttempts; attempt++)
{
await Task.Delay(pollInterval);
var getResponse = await httpClient.GetAsync($"/api/satellite/route/{routeId}");
if (!getResponse.IsSuccessStatusCode)
{
throw new Exception($"Failed to get route status: {getResponse.StatusCode}");
}
var currentRoute = await getResponse.Content.ReadFromJsonAsync<RouteResponseModel>(JsonOptions);
if (currentRoute == null)
{
throw new Exception("No route returned");
}
if (currentRoute.MapsReady)
{
finalRoute = currentRoute;
Console.WriteLine($"✓ Route maps ready in approximately {attempt * pollInterval / 1000}s");
break;
}
if (attempt % 5 == 0)
{
Console.WriteLine($" Waiting... (attempt {attempt + 1}/{maxAttempts})");
}
if (attempt == maxAttempts - 1)
{
throw new Exception($"Timeout: Route maps did not become ready in {maxAttempts * pollInterval / 1000}s");
}
}
if (finalRoute == null)
{
throw new Exception("Final route is null");
}
Console.WriteLine();
Console.WriteLine("Step 3: Verifying generated files");
if (string.IsNullOrEmpty(finalRoute.CsvFilePath))
{
throw new Exception("CSV file path is null or empty");
}
if (string.IsNullOrEmpty(finalRoute.SummaryFilePath))
{
throw new Exception("Summary file path is null or empty");
}
if (string.IsNullOrEmpty(finalRoute.StitchedImagePath))
{
throw new Exception("Stitched image path is null or empty");
}
if (!File.Exists(finalRoute.CsvFilePath))
{
throw new Exception($"CSV file not found: {finalRoute.CsvFilePath}");
}
if (!File.Exists(finalRoute.SummaryFilePath))
{
throw new Exception($"Summary file not found: {finalRoute.SummaryFilePath}");
}
if (!File.Exists(finalRoute.StitchedImagePath))
{
throw new Exception($"Stitched image not found: {finalRoute.StitchedImagePath}");
}
var csvLines = await File.ReadAllLinesAsync(finalRoute.CsvFilePath);
var uniqueTileCount = csvLines.Length - 1;
var stitchedInfo = new FileInfo(finalRoute.StitchedImagePath);
Console.WriteLine("Files Generated:");
Console.WriteLine($" ✓ CSV: {Path.GetFileName(finalRoute.CsvFilePath)} ({uniqueTileCount} tiles)");
Console.WriteLine($" ✓ Summary: {Path.GetFileName(finalRoute.SummaryFilePath)}");
Console.WriteLine($" ✓ Stitched Map: {Path.GetFileName(finalRoute.StitchedImagePath)} ({stitchedInfo.Length / 1024:F2} KB)");
Console.WriteLine();
Console.WriteLine("Route Summary:");
Console.WriteLine($" Route ID: {finalRoute.Id}");
Console.WriteLine($" Route Points: {finalRoute.TotalPoints}");
Console.WriteLine($" Distance: {finalRoute.TotalDistanceMeters:F2}m");
Console.WriteLine($" Unique Tiles: {uniqueTileCount}");
Console.WriteLine($" Maps Ready: {finalRoute.MapsReady}");
Console.WriteLine();
Console.WriteLine("✓ Route with Region Processing and Stitching Test: PASSED");
}
public static async Task RunComplexRouteWithStitching(HttpClient httpClient)
{
Console.WriteLine();
Console.WriteLine("Test: Complex Route with 10 Points - Region Processing and Stitching");
Console.WriteLine("=======================================================================");
Console.WriteLine();
var routeId = Guid.NewGuid();
var request = new CreateRouteRequest
{
Id = routeId,
Name = "Complex Route with 10 Points",
Description = "Test route with 10 action points for complex map stitching",
RegionSizeMeters = 300.0,
ZoomLevel = 18,
RequestMaps = true,
Points = new List<RoutePointInput>
{
new() { Latitude = 48.276067180586544, Longitude = 37.38445758819581 },
new() { Latitude = 48.27074009522731, Longitude = 37.374029159545906 },
new() { Latitude = 48.263312668696855, Longitude = 37.37707614898682 },
new() { Latitude = 48.26539817051818, Longitude = 37.36587524414063 },
new() { Latitude = 48.25851283439989, Longitude = 37.35952377319337 },
new() { Latitude = 48.254426906081555, Longitude = 37.374801635742195 },
new() { Latitude = 48.25914140977405, Longitude = 37.39068031311036 },
new() { Latitude = 48.25354110233028, Longitude = 37.401752471923835 },
new() { Latitude = 48.25902712391726, Longitude = 37.416257858276374 },
new() { Latitude = 48.26828345053738, Longitude = 37.402009963989265 }
}
};
Console.WriteLine("Step 1: Creating complex route with RequestMaps=true");
Console.WriteLine($" Action Points: {request.Points.Count}");
Console.WriteLine($" Region Size: {request.RegionSizeMeters}m");
Console.WriteLine($" Zoom Level: {request.ZoomLevel}");
Console.WriteLine($" Request Maps: {request.RequestMaps}");
Console.WriteLine();
Console.WriteLine("Route Path:");
for (int i = 0; i < request.Points.Count; i++)
{
Console.WriteLine($" {i + 1}. ({request.Points[i].Latitude}, {request.Points[i].Longitude})");
}
Console.WriteLine();
var routeResponse = await httpClient.PostAsJsonAsync("/api/satellite/route", request);
if (!routeResponse.IsSuccessStatusCode)
{
var errorContent = await routeResponse.Content.ReadAsStringAsync();
throw new Exception($"Route creation failed: {routeResponse.StatusCode} - {errorContent}");
}
var route = await routeResponse.Content.ReadFromJsonAsync<RouteResponseModel>(JsonOptions);
if (route == null)
{
throw new Exception("No route data returned");
}
Console.WriteLine($"✓ Route created with {route.TotalPoints} total points");
Console.WriteLine($" Distance: {route.TotalDistanceMeters:F2}m");
Console.WriteLine($" Request Maps: {route.RequestMaps}");
Console.WriteLine($" Maps Ready: {route.MapsReady}");
Console.WriteLine();
var startPoints = route.Points.Count(p => p.PointType == "start");
var endPoints = route.Points.Count(p => p.PointType == "end");
var actionPoints = route.Points.Count(p => p.PointType == "action");
var intermediatePoints = route.Points.Count(p => p.PointType == "intermediate");
Console.WriteLine("Point Type Distribution:");
Console.WriteLine($" Start: {startPoints}");
Console.WriteLine($" Action: {actionPoints}");
Console.WriteLine($" Intermediate: {intermediatePoints}");
Console.WriteLine($" End: {endPoints}");
Console.WriteLine();
if (startPoints != 1)
{
throw new Exception($"Expected 1 start point, got {startPoints}");
}
if (endPoints != 1)
{
throw new Exception($"Expected 1 end point, got {endPoints}");
}
if (actionPoints != 8)
{
throw new Exception($"Expected 8 action points (excluding start/end), got {actionPoints}");
}
if (!route.RequestMaps)
{
throw new Exception("Expected RequestMaps to be true");
}
if (route.MapsReady)
{
throw new Exception("Expected MapsReady to be false initially");
}
Console.WriteLine("Step 2: Waiting for complex route maps to be ready");
Console.WriteLine(" (Processing 56 regions SEQUENTIALLY to avoid API throttling)");
Console.WriteLine(" (This will take several minutes as each region is processed one at a time)");
Console.WriteLine();
RouteResponseModel? finalRoute = null;
int maxAttempts = 240;
int pollInterval = 3000;
for (int attempt = 0; attempt < maxAttempts; attempt++)
{
await Task.Delay(pollInterval);
var getResponse = await httpClient.GetAsync($"/api/satellite/route/{routeId}");
if (!getResponse.IsSuccessStatusCode)
{
throw new Exception($"Failed to get route status: {getResponse.StatusCode}");
}
var currentRoute = await getResponse.Content.ReadFromJsonAsync<RouteResponseModel>(JsonOptions);
if (currentRoute == null)
{
throw new Exception("No route returned");
}
if (currentRoute.MapsReady)
{
finalRoute = currentRoute;
Console.WriteLine($"✓ Complex route maps ready in approximately {attempt * pollInterval / 1000}s");
break;
}
if (attempt % 10 == 0)
{
Console.WriteLine($" Waiting... (attempt {attempt + 1}/{maxAttempts})");
}
if (attempt == maxAttempts - 1)
{
throw new Exception($"Timeout: Complex route maps did not become ready in {maxAttempts * pollInterval / 1000}s");
}
}
if (finalRoute == null)
{
throw new Exception("Final route is null");
}
Console.WriteLine();
Console.WriteLine("Step 3: Verifying generated files");
if (string.IsNullOrEmpty(finalRoute.CsvFilePath))
{
throw new Exception("CSV file path is null or empty");
}
if (string.IsNullOrEmpty(finalRoute.SummaryFilePath))
{
throw new Exception("Summary file path is null or empty");
}
if (string.IsNullOrEmpty(finalRoute.StitchedImagePath))
{
throw new Exception("Stitched image path is null or empty");
}
if (!File.Exists(finalRoute.CsvFilePath))
{
throw new Exception($"CSV file not found: {finalRoute.CsvFilePath}");
}
if (!File.Exists(finalRoute.SummaryFilePath))
{
throw new Exception($"Summary file not found: {finalRoute.SummaryFilePath}");
}
if (!File.Exists(finalRoute.StitchedImagePath))
{
throw new Exception($"Stitched image not found: {finalRoute.StitchedImagePath}");
}
var csvLines = await File.ReadAllLinesAsync(finalRoute.CsvFilePath);
var uniqueTileCount = csvLines.Length - 1;
var stitchedInfo = new FileInfo(finalRoute.StitchedImagePath);
Console.WriteLine("Files Generated:");
Console.WriteLine($" ✓ CSV: {Path.GetFileName(finalRoute.CsvFilePath)} ({uniqueTileCount} tiles)");
Console.WriteLine($" ✓ Summary: {Path.GetFileName(finalRoute.SummaryFilePath)}");
Console.WriteLine($" ✓ Stitched Map: {Path.GetFileName(finalRoute.StitchedImagePath)} ({stitchedInfo.Length / 1024:F2} KB)");
Console.WriteLine();
Console.WriteLine("Route Summary:");
Console.WriteLine($" Route ID: {finalRoute.Id}");
Console.WriteLine($" Total Points: {finalRoute.TotalPoints}");
Console.WriteLine($" Action Points: {actionPoints}");
Console.WriteLine($" Distance: {finalRoute.TotalDistanceMeters:F2}m");
Console.WriteLine($" Unique Tiles: {uniqueTileCount}");
Console.WriteLine($" Maps Ready: {finalRoute.MapsReady}");
Console.WriteLine();
if (uniqueTileCount < 10)
{
throw new Exception($"Expected at least 10 unique tiles for complex route, got {uniqueTileCount}");
}
if (stitchedInfo.Length < 1024)
{
throw new Exception($"Stitched image seems too small: {stitchedInfo.Length} bytes");
}
Console.WriteLine("✓ Complex Route with 10 Points Test: PASSED");
}
}