using System.Net.Http.Json; using System.Text.Json; namespace SatelliteProvider.IntegrationTests; public static class RouteTestHelpers { private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true }; private static readonly JsonSerializerOptions JsonWriteOptions = new() { PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; public static void PrintTestHeader(string title) { Console.WriteLine(); Console.WriteLine(title); Console.WriteLine(new string('=', title.Length)); Console.WriteLine(); } public static void PrintSectionHeader(string title) { Console.WriteLine(title); Console.WriteLine(new string('-', title.Length)); } public static void PrintRouteCreationDetails(CreateRouteRequest request, bool includeGeofences = false) { Console.WriteLine($" Action Points: {request.Points.Count}"); if (includeGeofences && request.Geofences?.Polygons != null) { Console.WriteLine($" Geofence Regions: {request.Geofences.Polygons.Count}"); } Console.WriteLine($" Region Size: {request.RegionSizeMeters}m"); Console.WriteLine($" Zoom Level: {request.ZoomLevel}"); Console.WriteLine($" Request Maps: {request.RequestMaps}"); Console.WriteLine(); } public static void PrintRoutePoints(List points, int maxPoints = 0) { Console.WriteLine("Route Path:"); if (maxPoints > 0 && points.Count > maxPoints * 2) { for (int i = 0; i < maxPoints; i++) { Console.WriteLine($" {i + 1}. ({points[i].Lat}, {points[i].Lon})"); } Console.WriteLine(" ..."); for (int i = points.Count - maxPoints; i < points.Count; i++) { Console.WriteLine($" {i + 1}. ({points[i].Lat}, {points[i].Lon})"); } } else { for (int i = 0; i < points.Count; i++) { Console.WriteLine($" {i + 1}. ({points[i].Lat}, {points[i].Lon})"); } } Console.WriteLine(); } public static void PrintGeofences(List polygons) { Console.WriteLine("Geofence Regions:"); for (int i = 0; i < polygons.Count; i++) { var polygon = polygons[i]; Console.WriteLine($" {i + 1}. NW: ({polygon.NorthWest?.Lat}, {polygon.NorthWest?.Lon}) -> SE: ({polygon.SouthEast?.Lat}, {polygon.SouthEast?.Lon})"); } Console.WriteLine(); } public static void PrintRouteResponse(RouteResponseModel route) { 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(); } public static void PrintPointTypeDistribution(RouteResponseModel route) { 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(); } public static void PrintGeneratedFiles(RouteResponseModel route, int uniqueTileCount, bool includeZip = false) { var stitchedInfo = new FileInfo(route.StitchedImagePath!); Console.WriteLine("Files Generated:"); Console.WriteLine($" ✓ CSV: {Path.GetFileName(route.CsvFilePath)} ({uniqueTileCount} tiles)"); Console.WriteLine($" ✓ Summary: {Path.GetFileName(route.SummaryFilePath)}"); Console.WriteLine($" ✓ Stitched Map: {Path.GetFileName(route.StitchedImagePath)} ({stitchedInfo.Length / 1024:F2} KB)"); if (includeZip && !string.IsNullOrEmpty(route.TilesZipPath)) { var zipInfo = new FileInfo(route.TilesZipPath); Console.WriteLine($" ✓ Tiles ZIP: {Path.GetFileName(route.TilesZipPath)} ({zipInfo.Length / 1024:F2} KB)"); } Console.WriteLine(); } public static void PrintRouteSummary(RouteResponseModel route, int uniqueTileCount, int? geofenceCount = null, bool includeZip = false) { Console.WriteLine("Route Summary:"); Console.WriteLine($" Route ID: {route.Id}"); Console.WriteLine($" Total Points: {route.TotalPoints}"); Console.WriteLine($" Distance: {route.TotalDistanceMeters:F2}m"); if (geofenceCount.HasValue) { Console.WriteLine($" Geofence Regions: {geofenceCount.Value}"); } Console.WriteLine($" Unique Tiles: {uniqueTileCount}"); if (includeZip && !string.IsNullOrEmpty(route.TilesZipPath)) { var zipInfo = new FileInfo(route.TilesZipPath); Console.WriteLine($" ZIP File Size: {zipInfo.Length / 1024:F2} KB"); } Console.WriteLine($" Maps Ready: {route.MapsReady}"); Console.WriteLine(); } public static async Task CreateRoute(HttpClient httpClient, CreateRouteRequest request) { var response = await httpClient.PostAsJsonAsync("/api/satellite/route", request, JsonWriteOptions); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); throw new Exception($"Route creation failed: {response.StatusCode} - {errorContent}"); } var route = await response.Content.ReadFromJsonAsync(JsonOptions); if (route == null) { throw new Exception("No route data returned from API"); } return route; } public static async Task WaitForRouteReady( HttpClient httpClient, Guid routeId, 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(JsonOptions); if (currentRoute == null) { throw new Exception("No route returned"); } if (currentRoute.MapsReady) { Console.WriteLine($"✓ Route maps ready in approximately {attempt * pollInterval / 1000}s"); return currentRoute; } 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"); } } throw new Exception("Failed to wait for route ready"); } public static void ValidateFiles(RouteResponseModel route, bool includeZip = false) { if (string.IsNullOrEmpty(route.CsvFilePath)) { throw new Exception("CSV file path is null or empty"); } if (string.IsNullOrEmpty(route.SummaryFilePath)) { throw new Exception("Summary file path is null or empty"); } if (string.IsNullOrEmpty(route.StitchedImagePath)) { throw new Exception("Stitched image path is null or empty"); } if (!File.Exists(route.CsvFilePath)) { throw new Exception($"CSV file not found: {route.CsvFilePath}"); } if (!File.Exists(route.SummaryFilePath)) { throw new Exception($"Summary file not found: {route.SummaryFilePath}"); } if (!File.Exists(route.StitchedImagePath)) { throw new Exception($"Stitched image not found: {route.StitchedImagePath}"); } if (includeZip) { if (string.IsNullOrEmpty(route.TilesZipPath)) { throw new Exception("Tiles ZIP file path is null or empty"); } if (!File.Exists(route.TilesZipPath)) { throw new Exception($"Tiles ZIP file not found: {route.TilesZipPath}"); } } } public static void ValidatePointTypes(RouteResponseModel route, int expectedStart, int expectedEnd, int expectedAction) { 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"); if (startPoints != expectedStart) { throw new Exception($"Expected {expectedStart} start point(s), got {startPoints}"); } if (endPoints != expectedEnd) { throw new Exception($"Expected {expectedEnd} end point(s), got {endPoints}"); } if (actionPoints != expectedAction) { throw new Exception($"Expected {expectedAction} action point(s), got {actionPoints}"); } } public static void ValidateRequestMaps(RouteResponseModel route, bool shouldBeReady) { if (!route.RequestMaps) { throw new Exception("Expected RequestMaps to be true"); } if (route.MapsReady != shouldBeReady) { throw new Exception($"Expected MapsReady to be {shouldBeReady}, got {route.MapsReady}"); } } public static async Task GetUniqueTileCount(string csvFilePath) { var csvLines = await File.ReadAllLinesAsync(csvFilePath); return csvLines.Length - 1; } }