diff --git a/SatelliteProvider.Api/appsettings.json b/SatelliteProvider.Api/appsettings.json index a22cc21..d2280c2 100644 --- a/SatelliteProvider.Api/appsettings.json +++ b/SatelliteProvider.Api/appsettings.json @@ -33,7 +33,7 @@ }, "ProcessingConfig": { "MaxConcurrentDownloads": 4, - "MaxConcurrentRegions": 3, + "MaxConcurrentRegions": 20, "DefaultZoomLevel": 20, "QueueCapacity": 1000, "DelayBetweenRequestsMs": 50, diff --git a/SatelliteProvider.IntegrationTests/BasicRouteTests.cs b/SatelliteProvider.IntegrationTests/BasicRouteTests.cs new file mode 100644 index 0000000..07606ec --- /dev/null +++ b/SatelliteProvider.IntegrationTests/BasicRouteTests.cs @@ -0,0 +1,140 @@ +using System.Net.Http.Json; + +namespace SatelliteProvider.IntegrationTests; + +public static class BasicRouteTests +{ + public static async Task RunSimpleRouteTest(HttpClient httpClient) + { + RouteTestHelpers.PrintSectionHeader("Test: Create Simple Route with Two Points"); + + 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 + { + new() { Lat = 48.276067180586544, Lon = 37.38445758819581 }, + new() { Lat = 48.27074009522731, Lon = 37.374029159545906 } + } + }; + + Console.WriteLine($"Creating route with 2 points:"); + Console.WriteLine($" Start: ({request.Points[0].Lat}, {request.Points[0].Lon})"); + Console.WriteLine($" End: ({request.Points[1].Lat}, {request.Points[1].Lon})"); + Console.WriteLine($" Region Size: {request.RegionSizeMeters}m"); + Console.WriteLine($" Zoom Level: {request.ZoomLevel}"); + Console.WriteLine(); + + var route = await RouteTestHelpers.CreateRoute(httpClient, request); + + 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(); + + RouteTestHelpers.PrintPointTypeDistribution(route); + + RouteTestHelpers.ValidatePointTypes(route, 1, 1, 0); + + 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(new System.Text.Json.JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }); + + 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) + { + RouteTestHelpers.PrintTestHeader("Test: Route with Region Processing and Full Map Stitching (Service-Level)"); + + 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 + { + new() { Lat = 48.276067180586544, Lon = 37.38445758819581 }, + new() { Lat = 48.27074009522731, Lon = 37.374029159545906 } + } + }; + + Console.WriteLine("Step 1: Creating route with RequestMaps=true"); + Console.WriteLine($" Start: ({request.Points[0].Lat}, {request.Points[0].Lon})"); + Console.WriteLine($" End: ({request.Points[1].Lat}, {request.Points[1].Lon})"); + Console.WriteLine($" Region Size: {request.RegionSizeMeters}m"); + Console.WriteLine($" Zoom Level: {request.ZoomLevel}"); + Console.WriteLine($" Request Maps: {request.RequestMaps}"); + Console.WriteLine(); + + var route = await RouteTestHelpers.CreateRoute(httpClient, request); + + RouteTestHelpers.PrintRouteResponse(route); + RouteTestHelpers.ValidateRequestMaps(route, false); + + Console.WriteLine("Step 2: Waiting for route maps to be ready"); + Console.WriteLine(" (Service is processing regions SEQUENTIALLY to avoid API throttling)"); + Console.WriteLine(); + + var finalRoute = await RouteTestHelpers.WaitForRouteReady(httpClient, routeId, 180, 3000); + + Console.WriteLine(); + Console.WriteLine("Step 3: Verifying generated files"); + + RouteTestHelpers.ValidateFiles(finalRoute); + + var uniqueTileCount = await RouteTestHelpers.GetUniqueTileCount(finalRoute.CsvFilePath!); + + RouteTestHelpers.PrintGeneratedFiles(finalRoute, uniqueTileCount); + RouteTestHelpers.PrintRouteSummary(finalRoute, uniqueTileCount); + + Console.WriteLine("✓ Route with Region Processing and Stitching Test: PASSED"); + } +} + diff --git a/SatelliteProvider.IntegrationTests/ComplexRouteTests.cs b/SatelliteProvider.IntegrationTests/ComplexRouteTests.cs new file mode 100644 index 0000000..1b4e0a3 --- /dev/null +++ b/SatelliteProvider.IntegrationTests/ComplexRouteTests.cs @@ -0,0 +1,166 @@ +namespace SatelliteProvider.IntegrationTests; + +public static class ComplexRouteTests +{ + public static async Task RunComplexRouteWithStitching(HttpClient httpClient) + { + RouteTestHelpers.PrintTestHeader("Test: Complex Route with 10 Points + 2 Geofences - Region Processing and Stitching"); + + var routeId = Guid.NewGuid(); + var request = new CreateRouteRequest + { + Id = routeId, + Name = "Complex Route with 10 Points + 2 Geofences", + Description = "Test route with 10 action points and 2 geofence regions for complex map stitching", + RegionSizeMeters = 300.0, + ZoomLevel = 18, + RequestMaps = true, + Points = new List + { + new() { Lat = 48.276067180586544, Lon = 37.38445758819581 }, + new() { Lat = 48.27074009522731, Lon = 37.374029159545906 }, + new() { Lat = 48.263312668696855, Lon = 37.37707614898682 }, + new() { Lat = 48.26539817051818, Lon = 37.36587524414063 }, + new() { Lat = 48.25851283439989, Lon = 37.35952377319337 }, + new() { Lat = 48.254426906081555, Lon = 37.374801635742195 }, + new() { Lat = 48.25914140977405, Lon = 37.39068031311036 }, + new() { Lat = 48.25354110233028, Lon = 37.401752471923835 }, + new() { Lat = 48.25902712391726, Lon = 37.416257858276374 }, + new() { Lat = 48.26828345053738, Lon = 37.402009963989265 } + }, + }; + + Console.WriteLine("Step 1: Creating complex route with RequestMaps=true and 2 geofences"); + Console.WriteLine($" Action Points: {request.Points.Count}"); + Console.WriteLine($" NO Geofences Regions"); + RouteTestHelpers.PrintRouteCreationDetails(request); + RouteTestHelpers.PrintRoutePoints(request.Points); + + var route = await RouteTestHelpers.CreateRoute(httpClient, request); + + RouteTestHelpers.PrintRouteResponse(route); + RouteTestHelpers.PrintPointTypeDistribution(route); + + RouteTestHelpers.ValidatePointTypes(route, 1, 1, 8); + RouteTestHelpers.ValidateRequestMaps(route, false); + + Console.WriteLine("Step 2: Waiting for complex route maps to be ready"); + Console.WriteLine(" (Processing route point regions + 2 geofence regions SEQUENTIALLY to avoid API throttling)"); + Console.WriteLine(" (This will take several minutes as each region is processed one at a time)"); + Console.WriteLine(); + + var finalRoute = await RouteTestHelpers.WaitForRouteReady(httpClient, routeId, 240, 3000); + + Console.WriteLine(); + Console.WriteLine("Step 3: Verifying generated files"); + + RouteTestHelpers.ValidateFiles(finalRoute); + + var uniqueTileCount = await RouteTestHelpers.GetUniqueTileCount(finalRoute.CsvFilePath!); + var stitchedInfo = new FileInfo(finalRoute.StitchedImagePath!); + + RouteTestHelpers.PrintGeneratedFiles(finalRoute, uniqueTileCount); + RouteTestHelpers.PrintRouteSummary(finalRoute, uniqueTileCount, 2); + + if (uniqueTileCount < 10) + { + throw new Exception($"Expected at least 10 unique tiles for complex route with geofences, got {uniqueTileCount}"); + } + + if (stitchedInfo.Length < 1024) + { + throw new Exception($"Stitched image seems too small: {stitchedInfo.Length} bytes"); + } + + Console.WriteLine("✓ Complex Route with 10 Points + 2 Geofences Test: PASSED"); + } + + public static async Task RunComplexRouteWithStitchingAndGeofences(HttpClient httpClient) + { + RouteTestHelpers.PrintTestHeader("Test: Complex Route with 10 Points + 2 Geofences - Region Processing and Stitching"); + + var routeId = Guid.NewGuid(); + var request = new CreateRouteRequest + { + Id = routeId, + Name = "Complex Route with 10 Points + 2 Geofences", + Description = "Test route with 10 action points and 2 geofence regions for complex map stitching", + RegionSizeMeters = 300.0, + ZoomLevel = 18, + RequestMaps = true, + Points = new List + { + new() { Lat = 48.276067180586544, Lon = 37.38445758819581 }, + new() { Lat = 48.27074009522731, Lon = 37.374029159545906 }, + new() { Lat = 48.263312668696855, Lon = 37.37707614898682 }, + new() { Lat = 48.26539817051818, Lon = 37.36587524414063 }, + new() { Lat = 48.25851283439989, Lon = 37.35952377319337 }, + new() { Lat = 48.254426906081555, Lon = 37.374801635742195 }, + new() { Lat = 48.25914140977405, Lon = 37.39068031311036 }, + new() { Lat = 48.25354110233028, Lon = 37.401752471923835 }, + new() { Lat = 48.25902712391726, Lon = 37.416257858276374 }, + new() { Lat = 48.26828345053738, Lon = 37.402009963989265 } + }, + Geofences = new GeofencesInput + { + Polygons = new List + { + new() + { + NorthWest = new GeoPointInput { Lat = 48.28022277841604, Lon = 37.37548828125001 }, + SouthEast = new GeoPointInput { Lat = 48.2720540660028, Lon = 37.3901653289795 } + }, + new() + { + NorthWest = new GeoPointInput { Lat = 48.2614270732573, Lon = 37.35239982604981 }, + SouthEast = new GeoPointInput { Lat = 48.24988342757033, Lon = 37.37943649291993 } + } + } + } + }; + + Console.WriteLine("Step 1: Creating complex route with RequestMaps=true and 2 geofences"); + RouteTestHelpers.PrintRouteCreationDetails(request, true); + RouteTestHelpers.PrintGeofences(request.Geofences.Polygons); + RouteTestHelpers.PrintRoutePoints(request.Points); + + var route = await RouteTestHelpers.CreateRoute(httpClient, request); + + RouteTestHelpers.PrintRouteResponse(route); + RouteTestHelpers.PrintPointTypeDistribution(route); + + RouteTestHelpers.ValidatePointTypes(route, 1, 1, 8); + RouteTestHelpers.ValidateRequestMaps(route, false); + + Console.WriteLine("Step 2: Waiting for complex route maps to be ready"); + Console.WriteLine(" (Processing route point regions + 2 geofence regions SEQUENTIALLY to avoid API throttling)"); + Console.WriteLine(" (This will take several minutes as each region is processed one at a time)"); + Console.WriteLine(); + + var finalRoute = await RouteTestHelpers.WaitForRouteReady(httpClient, routeId, 240, 3000); + + Console.WriteLine(); + Console.WriteLine("Step 3: Verifying generated files"); + + RouteTestHelpers.ValidateFiles(finalRoute); + + var uniqueTileCount = await RouteTestHelpers.GetUniqueTileCount(finalRoute.CsvFilePath!); + var stitchedInfo = new FileInfo(finalRoute.StitchedImagePath!); + + RouteTestHelpers.PrintGeneratedFiles(finalRoute, uniqueTileCount); + RouteTestHelpers.PrintRouteSummary(finalRoute, uniqueTileCount, 2); + + if (uniqueTileCount < 10) + { + throw new Exception($"Expected at least 10 unique tiles for complex route with geofences, got {uniqueTileCount}"); + } + + if (stitchedInfo.Length < 1024) + { + throw new Exception($"Stitched image seems too small: {stitchedInfo.Length} bytes"); + } + + Console.WriteLine("✓ Complex Route with 10 Points + 2 Geofences Test: PASSED"); + } +} + diff --git a/SatelliteProvider.IntegrationTests/ExtendedRouteTests.cs b/SatelliteProvider.IntegrationTests/ExtendedRouteTests.cs new file mode 100644 index 0000000..a53efa8 --- /dev/null +++ b/SatelliteProvider.IntegrationTests/ExtendedRouteTests.cs @@ -0,0 +1,182 @@ +namespace SatelliteProvider.IntegrationTests; + +public static class ExtendedRouteTests +{ + public static async Task RunExtendedRouteEast(HttpClient httpClient) + { + RouteTestHelpers.PrintTestHeader("Test: Extended Route with 20 Points - 10km East"); + + var routeId = Guid.NewGuid(); + var request = new CreateRouteRequest + { + Id = routeId, + Name = "Extended Route with 20 Points (10km East)", + Description = "Test route with 20 action points, located 10km east of the complex route", + RegionSizeMeters = 300.0, + ZoomLevel = 18, + RequestMaps = true, + Points = new List + { + new() { Lat = 48.276067180586544, Lon = 37.51945758819581 }, + new() { Lat = 48.27074009522731, Lon = 37.509029159545906 }, + new() { Lat = 48.263312668696855, Lon = 37.51207614898682 }, + new() { Lat = 48.26539817051818, Lon = 37.50087524414063 }, + new() { Lat = 48.25851283439989, Lon = 37.49452377319337 }, + new() { Lat = 48.254426906081555, Lon = 37.509801635742195 }, + new() { Lat = 48.25914140977405, Lon = 37.52568031311036 }, + new() { Lat = 48.25354110233028, Lon = 37.536752471923835 }, + new() { Lat = 48.25902712391726, Lon = 37.551257858276374 }, + new() { Lat = 48.26828345053738, Lon = 37.537009963989265 }, + new() { Lat = 48.27421563182974, Lon = 37.52345758819581 }, + new() { Lat = 48.26889854647051, Lon = 37.513029159545906 }, + new() { Lat = 48.26147111993905, Lon = 37.51607614898682 }, + new() { Lat = 48.26355662176038, Lon = 37.50487524414063 }, + new() { Lat = 48.25667128564209, Lon = 37.49852377319337 }, + new() { Lat = 48.25258535732375, Lon = 37.513801635742195 }, + new() { Lat = 48.25729986101625, Lon = 37.52968031311036 }, + new() { Lat = 48.25169955357248, Lon = 37.540752471923835 }, + new() { Lat = 48.25718557515946, Lon = 37.555257858276374 }, + new() { Lat = 48.26644190177958, Lon = 37.541009963989265 } + } + }; + + Console.WriteLine("Step 1: Creating extended route with RequestMaps=true"); + RouteTestHelpers.PrintRouteCreationDetails(request); + Console.WriteLine($" Location: ~10km east of original complex route"); + Console.WriteLine(); + RouteTestHelpers.PrintRoutePoints(request.Points, 5); + + var route = await RouteTestHelpers.CreateRoute(httpClient, request); + + RouteTestHelpers.PrintRouteResponse(route); + RouteTestHelpers.PrintPointTypeDistribution(route); + + RouteTestHelpers.ValidatePointTypes(route, 1, 1, 18); + RouteTestHelpers.ValidateRequestMaps(route, false); + + Console.WriteLine("Step 2: Waiting for extended route maps to be ready"); + Console.WriteLine(" (Processing regions SEQUENTIALLY to avoid API throttling)"); + Console.WriteLine(" (This will take several minutes for 20 action points)"); + Console.WriteLine(); + + var finalRoute = await RouteTestHelpers.WaitForRouteReady(httpClient, routeId, 360, 3000); + + Console.WriteLine(); + Console.WriteLine("Step 3: Verifying generated files"); + + RouteTestHelpers.ValidateFiles(finalRoute); + + var uniqueTileCount = await RouteTestHelpers.GetUniqueTileCount(finalRoute.CsvFilePath!); + var stitchedInfo = new FileInfo(finalRoute.StitchedImagePath!); + + RouteTestHelpers.PrintGeneratedFiles(finalRoute, uniqueTileCount); + RouteTestHelpers.PrintRouteSummary(finalRoute, uniqueTileCount); + + if (uniqueTileCount < 20) + { + throw new Exception($"Expected at least 20 unique tiles for extended route, got {uniqueTileCount}"); + } + + if (stitchedInfo.Length < 1024) + { + throw new Exception($"Stitched image seems too small: {stitchedInfo.Length} bytes"); + } + + Console.WriteLine("✓ Extended Route with 20 Points (10km East) Test: PASSED"); + } + + public static async Task RunRouteWithTilesZipTest(HttpClient httpClient) + { + RouteTestHelpers.PrintTestHeader("Test: Route with Tiles ZIP File Creation"); + + var routeId = Guid.NewGuid(); + var request = new CreateRouteRequest + { + Id = routeId, + Name = "Route with Tiles ZIP", + Description = "Test route with tiles zip file creation", + RegionSizeMeters = 500.0, + ZoomLevel = 18, + RequestMaps = true, + CreateTilesZip = true, + Points = new List + { + new() { Lat = 48.276067180586544, Lon = 37.38445758819581 }, + new() { Lat = 48.27074009522731, Lon = 37.374029159545906 } + } + }; + + Console.WriteLine($"Creating route with 2 points:"); + Console.WriteLine($" Start: ({request.Points[0].Lat}, {request.Points[0].Lon})"); + Console.WriteLine($" End: ({request.Points[1].Lat}, {request.Points[1].Lon})"); + Console.WriteLine($" Region Size: {request.RegionSizeMeters}m"); + Console.WriteLine($" Zoom Level: {request.ZoomLevel}"); + Console.WriteLine($" Request Maps: {request.RequestMaps}"); + Console.WriteLine($" Create Tiles ZIP: {request.CreateTilesZip}"); + Console.WriteLine(); + + var route = await RouteTestHelpers.CreateRoute(httpClient, request); + + 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($" Request Maps: {route.RequestMaps}"); + Console.WriteLine($" Maps Ready: {route.MapsReady}"); + Console.WriteLine(); + + RouteTestHelpers.ValidateRequestMaps(route, false); + + Console.WriteLine("Step 2: Waiting for route maps and zip file to be ready"); + Console.WriteLine(" (Service is processing regions SEQUENTIALLY to avoid API throttling)"); + Console.WriteLine(); + + var finalRoute = await RouteTestHelpers.WaitForRouteReady(httpClient, routeId, 180, 3000); + + Console.WriteLine(); + Console.WriteLine("Step 3: Verifying generated files including ZIP"); + + RouteTestHelpers.ValidateFiles(finalRoute, true); + + var uniqueTileCount = await RouteTestHelpers.GetUniqueTileCount(finalRoute.CsvFilePath!); + var zipInfo = new FileInfo(finalRoute.TilesZipPath!); + + RouteTestHelpers.PrintGeneratedFiles(finalRoute, uniqueTileCount, true); + + Console.WriteLine("Verifying ZIP file contents:"); + using (var zipArchive = System.IO.Compression.ZipFile.OpenRead(finalRoute.TilesZipPath!)) + { + Console.WriteLine($" ZIP contains {zipArchive.Entries.Count} files"); + + if (zipArchive.Entries.Count == 0) + { + throw new Exception("ZIP file is empty"); + } + + if (zipArchive.Entries.Count != uniqueTileCount) + { + throw new Exception($"ZIP contains {zipArchive.Entries.Count} files but CSV lists {uniqueTileCount} tiles"); + } + + var firstEntry = zipArchive.Entries[0]; + Console.WriteLine($" First entry: {firstEntry.Name} ({firstEntry.Length} bytes)"); + + if (firstEntry.Length == 0) + { + throw new Exception("First entry in ZIP is empty"); + } + } + + Console.WriteLine(); + RouteTestHelpers.PrintRouteSummary(finalRoute, uniqueTileCount, null, true); + + if (zipInfo.Length < 1024) + { + throw new Exception($"ZIP file seems too small: {zipInfo.Length} bytes"); + } + + Console.WriteLine("✓ Route with Tiles ZIP File Test: PASSED"); + } +} + diff --git a/SatelliteProvider.IntegrationTests/Program.cs b/SatelliteProvider.IntegrationTests/Program.cs index 06f9ee4..a8f52d3 100644 --- a/SatelliteProvider.IntegrationTests/Program.cs +++ b/SatelliteProvider.IntegrationTests/Program.cs @@ -32,15 +32,15 @@ class Program await RegionTests.RunRegionProcessingTest_500m_Zoom18(httpClient); - await RouteTests.RunSimpleRouteTest(httpClient); + await BasicRouteTests.RunSimpleRouteTest(httpClient); - await RouteTests.RunRouteWithRegionProcessingAndStitching(httpClient); + await BasicRouteTests.RunRouteWithRegionProcessingAndStitching(httpClient); - await RouteTests.RunRouteWithTilesZipTest(httpClient); + await ExtendedRouteTests.RunRouteWithTilesZipTest(httpClient); - await RouteTests.RunComplexRouteWithStitching(httpClient); - await RouteTests.RunComplexRouteWithStitchingAndGeofences(httpClient); - await RouteTests.RunExtendedRouteEast(httpClient); + await ComplexRouteTests.RunComplexRouteWithStitching(httpClient); + await ComplexRouteTests.RunComplexRouteWithStitchingAndGeofences(httpClient); + await ExtendedRouteTests.RunExtendedRouteEast(httpClient); Console.WriteLine(); Console.WriteLine("========================="); diff --git a/SatelliteProvider.IntegrationTests/RouteTestHelpers.cs b/SatelliteProvider.IntegrationTests/RouteTestHelpers.cs new file mode 100644 index 0000000..61f000b --- /dev/null +++ b/SatelliteProvider.IntegrationTests/RouteTestHelpers.cs @@ -0,0 +1,297 @@ +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; + } +} + diff --git a/SatelliteProvider.IntegrationTests/RouteTests.cs b/SatelliteProvider.IntegrationTests/RouteTests.cs deleted file mode 100644 index 5feb252..0000000 --- a/SatelliteProvider.IntegrationTests/RouteTests.cs +++ /dev/null @@ -1,1208 +0,0 @@ -using System.Net.Http.Json; -using System.Text.Json; - -namespace SatelliteProvider.IntegrationTests; - -public static class RouteTests -{ - private static readonly JsonSerializerOptions JsonOptions = new() - { - PropertyNameCaseInsensitive = true - }; - - private static readonly JsonSerializerOptions JsonWriteOptions = new() - { - PropertyNamingPolicy = JsonNamingPolicy.CamelCase - }; - - 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 - { - new() { Lat = 48.276067180586544, Lon = 37.38445758819581 }, - new() { Lat = 48.27074009522731, Lon = 37.374029159545906 } - } - }; - - Console.WriteLine($"Creating route with 2 points:"); - Console.WriteLine($" Start: ({request.Points[0].Lat}, {request.Points[0].Lon})"); - Console.WriteLine($" End: ({request.Points[1].Lat}, {request.Points[1].Lon})"); - Console.WriteLine($" Region Size: {request.RegionSizeMeters}m"); - Console.WriteLine($" Zoom Level: {request.ZoomLevel}"); - Console.WriteLine(); - - var response = await httpClient.PostAsJsonAsync("/api/satellite/route", request, JsonWriteOptions); - - 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(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(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 - { - new() { Lat = 48.276067180586544, Lon = 37.38445758819581 }, - new() { Lat = 48.27074009522731, Lon = 37.374029159545906 } - } - }; - - Console.WriteLine("Step 1: Creating route with RequestMaps=true"); - Console.WriteLine($" Start: ({request.Points[0].Lat}, {request.Points[0].Lon})"); - Console.WriteLine($" End: ({request.Points[1].Lat}, {request.Points[1].Lon})"); - 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, JsonWriteOptions); - - if (!routeResponse.IsSuccessStatusCode) - { - var errorContent = await routeResponse.Content.ReadAsStringAsync(); - throw new Exception($"Route creation failed: {routeResponse.StatusCode} - {errorContent}"); - } - - var route = await routeResponse.Content.ReadFromJsonAsync(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(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 + 2 Geofences - Region Processing and Stitching"); - Console.WriteLine("======================================================================================"); - Console.WriteLine(); - - var routeId = Guid.NewGuid(); - var request = new CreateRouteRequest - { - Id = routeId, - Name = "Complex Route with 10 Points + 2 Geofences", - Description = "Test route with 10 action points and 2 geofence regions for complex map stitching", - RegionSizeMeters = 300.0, - ZoomLevel = 18, - RequestMaps = true, - Points = new List - { - new() { Lat = 48.276067180586544, Lon = 37.38445758819581 }, - new() { Lat = 48.27074009522731, Lon = 37.374029159545906 }, - new() { Lat = 48.263312668696855, Lon = 37.37707614898682 }, - new() { Lat = 48.26539817051818, Lon = 37.36587524414063 }, - new() { Lat = 48.25851283439989, Lon = 37.35952377319337 }, - new() { Lat = 48.254426906081555, Lon = 37.374801635742195 }, - new() { Lat = 48.25914140977405, Lon = 37.39068031311036 }, - new() { Lat = 48.25354110233028, Lon = 37.401752471923835 }, - new() { Lat = 48.25902712391726, Lon = 37.416257858276374 }, - new() { Lat = 48.26828345053738, Lon = 37.402009963989265 } - }, - }; - - Console.WriteLine("Step 1: Creating complex route with RequestMaps=true and 2 geofences"); - Console.WriteLine($" Action Points: {request.Points.Count}"); - Console.WriteLine($" NO Geofences Regions"); - 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].Lat}, {request.Points[i].Lon})"); - } - Console.WriteLine(); - - var routeResponse = await httpClient.PostAsJsonAsync("/api/satellite/route", request, JsonWriteOptions); - - if (!routeResponse.IsSuccessStatusCode) - { - var errorContent = await routeResponse.Content.ReadAsStringAsync(); - throw new Exception($"Route creation failed: {routeResponse.StatusCode} - {errorContent}"); - } - - var route = await routeResponse.Content.ReadFromJsonAsync(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 route point regions + 2 geofence 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(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($" Geofence Regions: 2"); - 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 with geofences, got {uniqueTileCount}"); - } - - if (stitchedInfo.Length < 1024) - { - throw new Exception($"Stitched image seems too small: {stitchedInfo.Length} bytes"); - } - - Console.WriteLine("✓ Complex Route with 10 Points + 2 Geofences Test: PASSED"); - } - - public static async Task RunComplexRouteWithStitchingAndGeofences(HttpClient httpClient) - { - Console.WriteLine(); - Console.WriteLine("Test: Complex Route with 10 Points + 2 Geofences - Region Processing and Stitching"); - Console.WriteLine("======================================================================================"); - Console.WriteLine(); - - var routeId = Guid.NewGuid(); - var request = new CreateRouteRequest - { - Id = routeId, - Name = "Complex Route with 10 Points + 2 Geofences", - Description = "Test route with 10 action points and 2 geofence regions for complex map stitching", - RegionSizeMeters = 300.0, - ZoomLevel = 18, - RequestMaps = true, - Points = new List - { - new() { Lat = 48.276067180586544, Lon = 37.38445758819581 }, - new() { Lat = 48.27074009522731, Lon = 37.374029159545906 }, - new() { Lat = 48.263312668696855, Lon = 37.37707614898682 }, - new() { Lat = 48.26539817051818, Lon = 37.36587524414063 }, - new() { Lat = 48.25851283439989, Lon = 37.35952377319337 }, - new() { Lat = 48.254426906081555, Lon = 37.374801635742195 }, - new() { Lat = 48.25914140977405, Lon = 37.39068031311036 }, - new() { Lat = 48.25354110233028, Lon = 37.401752471923835 }, - new() { Lat = 48.25902712391726, Lon = 37.416257858276374 }, - new() { Lat = 48.26828345053738, Lon = 37.402009963989265 } - }, - Geofences = new GeofencesInput - { - Polygons = new List - { - new() - { - NorthWest = new GeoPointInput { Lat = 48.28022277841604, Lon = 37.37548828125001 }, - SouthEast = new GeoPointInput { Lat = 48.2720540660028, Lon = 37.3901653289795 } - }, - new() - { - NorthWest = new GeoPointInput { Lat = 48.2614270732573, Lon = 37.35239982604981 }, - SouthEast = new GeoPointInput { Lat = 48.24988342757033, Lon = 37.37943649291993 } - } - } - } - }; - - Console.WriteLine("Step 1: Creating complex route with RequestMaps=true and 2 geofences"); - Console.WriteLine($" Action Points: {request.Points.Count}"); - 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(); - Console.WriteLine("Geofence Regions:"); - for (int i = 0; i < request.Geofences.Polygons.Count; i++) - { - var polygon = request.Geofences.Polygons[i]; - Console.WriteLine($" {i + 1}. NW: ({polygon.NorthWest?.Lat}, {polygon.NorthWest?.Lon}) -> SE: ({polygon.SouthEast?.Lat}, {polygon.SouthEast?.Lon})"); - } - Console.WriteLine(); - Console.WriteLine("Route Path:"); - for (int i = 0; i < request.Points.Count; i++) - { - Console.WriteLine($" {i + 1}. ({request.Points[i].Lat}, {request.Points[i].Lon})"); - } - Console.WriteLine(); - - var routeResponse = await httpClient.PostAsJsonAsync("/api/satellite/route", request, JsonWriteOptions); - - if (!routeResponse.IsSuccessStatusCode) - { - var errorContent = await routeResponse.Content.ReadAsStringAsync(); - throw new Exception($"Route creation failed: {routeResponse.StatusCode} - {errorContent}"); - } - - var route = await routeResponse.Content.ReadFromJsonAsync(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 route point regions + 2 geofence 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(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($" Geofence Regions: 2"); - 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 with geofences, got {uniqueTileCount}"); - } - - if (stitchedInfo.Length < 1024) - { - throw new Exception($"Stitched image seems too small: {stitchedInfo.Length} bytes"); - } - - Console.WriteLine("✓ Complex Route with 10 Points + 2 Geofences Test: PASSED"); - } - - public static async Task RunExtendedRouteEast(HttpClient httpClient) - { - Console.WriteLine(); - Console.WriteLine("Test: Extended Route with 20 Points - 10km East"); - Console.WriteLine("================================================="); - Console.WriteLine(); - - var routeId = Guid.NewGuid(); - var request = new CreateRouteRequest - { - Id = routeId, - Name = "Extended Route with 20 Points (10km East)", - Description = "Test route with 20 action points, located 10km east of the complex route", - RegionSizeMeters = 300.0, - ZoomLevel = 18, - RequestMaps = true, - Points = new List - { - new() { Lat = 48.276067180586544, Lon = 37.51945758819581 }, - new() { Lat = 48.27074009522731, Lon = 37.509029159545906 }, - new() { Lat = 48.263312668696855, Lon = 37.51207614898682 }, - new() { Lat = 48.26539817051818, Lon = 37.50087524414063 }, - new() { Lat = 48.25851283439989, Lon = 37.49452377319337 }, - new() { Lat = 48.254426906081555, Lon = 37.509801635742195 }, - new() { Lat = 48.25914140977405, Lon = 37.52568031311036 }, - new() { Lat = 48.25354110233028, Lon = 37.536752471923835 }, - new() { Lat = 48.25902712391726, Lon = 37.551257858276374 }, - new() { Lat = 48.26828345053738, Lon = 37.537009963989265 }, - new() { Lat = 48.27421563182974, Lon = 37.52345758819581 }, - new() { Lat = 48.26889854647051, Lon = 37.513029159545906 }, - new() { Lat = 48.26147111993905, Lon = 37.51607614898682 }, - new() { Lat = 48.26355662176038, Lon = 37.50487524414063 }, - new() { Lat = 48.25667128564209, Lon = 37.49852377319337 }, - new() { Lat = 48.25258535732375, Lon = 37.513801635742195 }, - new() { Lat = 48.25729986101625, Lon = 37.52968031311036 }, - new() { Lat = 48.25169955357248, Lon = 37.540752471923835 }, - new() { Lat = 48.25718557515946, Lon = 37.555257858276374 }, - new() { Lat = 48.26644190177958, Lon = 37.541009963989265 } - } - }; - - Console.WriteLine("Step 1: Creating extended 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($" Location: ~10km east of original complex route"); - Console.WriteLine(); - Console.WriteLine("Route Path (first 5 and last 5 points):"); - for (int i = 0; i < Math.Min(5, request.Points.Count); i++) - { - Console.WriteLine($" {i + 1}. ({request.Points[i].Lat}, {request.Points[i].Lon})"); - } - Console.WriteLine(" ..."); - for (int i = Math.Max(5, request.Points.Count - 5); i < request.Points.Count; i++) - { - Console.WriteLine($" {i + 1}. ({request.Points[i].Lat}, {request.Points[i].Lon})"); - } - Console.WriteLine(); - - var routeResponse = await httpClient.PostAsJsonAsync("/api/satellite/route", request, JsonWriteOptions); - - if (!routeResponse.IsSuccessStatusCode) - { - var errorContent = await routeResponse.Content.ReadAsStringAsync(); - throw new Exception($"Route creation failed: {routeResponse.StatusCode} - {errorContent}"); - } - - var route = await routeResponse.Content.ReadFromJsonAsync(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 != 18) - { - throw new Exception($"Expected 18 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 extended route maps to be ready"); - Console.WriteLine(" (Processing regions SEQUENTIALLY to avoid API throttling)"); - Console.WriteLine(" (This will take several minutes for 20 action points)"); - Console.WriteLine(); - - RouteResponseModel? finalRoute = null; - int maxAttempts = 360; - 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) - { - finalRoute = currentRoute; - Console.WriteLine($"✓ Extended 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: Extended 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 < 20) - { - throw new Exception($"Expected at least 20 unique tiles for extended route, got {uniqueTileCount}"); - } - - if (stitchedInfo.Length < 1024) - { - throw new Exception($"Stitched image seems too small: {stitchedInfo.Length} bytes"); - } - - Console.WriteLine("✓ Extended Route with 20 Points (10km East) Test: PASSED"); - } - - public static async Task RunRouteWithTilesZipTest(HttpClient httpClient) - { - Console.WriteLine(); - Console.WriteLine("Test: Route with Tiles ZIP File Creation"); - Console.WriteLine("========================================="); - Console.WriteLine(); - - var routeId = Guid.NewGuid(); - var request = new CreateRouteRequest - { - Id = routeId, - Name = "Route with Tiles ZIP", - Description = "Test route with tiles zip file creation", - RegionSizeMeters = 500.0, - ZoomLevel = 18, - RequestMaps = true, - CreateTilesZip = true, - Points = new List - { - new() { Lat = 48.276067180586544, Lon = 37.38445758819581 }, - new() { Lat = 48.27074009522731, Lon = 37.374029159545906 } - } - }; - - Console.WriteLine($"Creating route with 2 points:"); - Console.WriteLine($" Start: ({request.Points[0].Lat}, {request.Points[0].Lon})"); - Console.WriteLine($" End: ({request.Points[1].Lat}, {request.Points[1].Lon})"); - Console.WriteLine($" Region Size: {request.RegionSizeMeters}m"); - Console.WriteLine($" Zoom Level: {request.ZoomLevel}"); - Console.WriteLine($" Request Maps: {request.RequestMaps}"); - Console.WriteLine($" Create Tiles ZIP: {request.CreateTilesZip}"); - Console.WriteLine(); - - var response = await httpClient.PostAsJsonAsync("/api/satellite/route", request, JsonWriteOptions); - - 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(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($" 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 and zip file 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(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 including ZIP"); - - 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 (string.IsNullOrEmpty(finalRoute.TilesZipPath)) - { - throw new Exception("Tiles ZIP file 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}"); - } - - if (!File.Exists(finalRoute.TilesZipPath)) - { - throw new Exception($"Tiles ZIP file not found: {finalRoute.TilesZipPath}"); - } - - var csvLines = await File.ReadAllLinesAsync(finalRoute.CsvFilePath); - var uniqueTileCount = csvLines.Length - 1; - - var stitchedInfo = new FileInfo(finalRoute.StitchedImagePath); - var zipInfo = new FileInfo(finalRoute.TilesZipPath); - - 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($" ✓ Tiles ZIP: {Path.GetFileName(finalRoute.TilesZipPath)} ({zipInfo.Length / 1024:F2} KB)"); - Console.WriteLine(); - - Console.WriteLine("Verifying ZIP file contents:"); - using (var zipArchive = System.IO.Compression.ZipFile.OpenRead(finalRoute.TilesZipPath)) - { - Console.WriteLine($" ZIP contains {zipArchive.Entries.Count} files"); - - if (zipArchive.Entries.Count == 0) - { - throw new Exception("ZIP file is empty"); - } - - if (zipArchive.Entries.Count != uniqueTileCount) - { - throw new Exception($"ZIP contains {zipArchive.Entries.Count} files but CSV lists {uniqueTileCount} tiles"); - } - - var firstEntry = zipArchive.Entries[0]; - Console.WriteLine($" First entry: {firstEntry.Name} ({firstEntry.Length} bytes)"); - - if (firstEntry.Length == 0) - { - throw new Exception("First entry in ZIP is empty"); - } - } - - 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($" ZIP File Size: {zipInfo.Length / 1024:F2} KB"); - Console.WriteLine($" Maps Ready: {finalRoute.MapsReady}"); - Console.WriteLine(); - - if (zipInfo.Length < 1024) - { - throw new Exception($"ZIP file seems too small: {zipInfo.Length} bytes"); - } - - Console.WriteLine("✓ Route with Tiles ZIP File Test: PASSED"); - } -} -