using System.Net.Http.Json; using System.Text.Json; namespace SatelliteProvider.IntegrationTests; class Program { private static readonly JsonSerializerOptions JsonOptions = new() { PropertyNameCaseInsensitive = true }; static async Task Main(string[] args) { var apiUrl = Environment.GetEnvironmentVariable("API_URL") ?? "http://api:8080"; Console.WriteLine("Starting Integration Tests"); Console.WriteLine("========================="); Console.WriteLine($"API URL: {apiUrl}"); Console.WriteLine(); using var httpClient = new HttpClient { BaseAddress = new Uri(apiUrl), Timeout = TimeSpan.FromSeconds(60) }; try { Console.WriteLine("Waiting for API to be ready..."); await WaitForApiReady(httpClient); Console.WriteLine("✓ API is ready"); Console.WriteLine(); await RunSingleTileDownloadTest(httpClient); await RunRegionProcessingTest(httpClient); Console.WriteLine(); Console.WriteLine("========================="); Console.WriteLine("All tests completed successfully!"); return 0; } catch (Exception ex) { Console.WriteLine(); Console.WriteLine("❌ Integration tests failed"); Console.WriteLine($"Error: {ex.Message}"); Console.WriteLine($"Stack trace: {ex.StackTrace}"); return 1; } } static async Task WaitForApiReady(HttpClient httpClient, int maxRetries = 30) { for (int i = 0; i < maxRetries; i++) { try { var response = await httpClient.GetAsync("/"); if (response.IsSuccessStatusCode || response.StatusCode == System.Net.HttpStatusCode.NotFound) { return; } } catch { } Console.WriteLine($" Attempt {i + 1}/{maxRetries} - waiting 2 seconds..."); await Task.Delay(2000); } throw new Exception("API did not become ready in time"); } static async Task RunSingleTileDownloadTest(HttpClient httpClient) { Console.WriteLine("Test: Download Single Tile at Coordinates 47.461747, 37.647063"); Console.WriteLine("------------------------------------------------------------------"); const double latitude = 47.461747; const double longitude = 37.647063; const int zoomLevel = 18; Console.WriteLine($"Downloading tile at coordinates ({latitude}, {longitude}) with zoom level {zoomLevel}"); var request = new DownloadTileRequest { Latitude = latitude, Longitude = longitude, ZoomLevel = zoomLevel }; var response = await httpClient.PostAsJsonAsync("/api/satellite/tiles/download", request); if (!response.IsSuccessStatusCode) { var errorContent = await response.Content.ReadAsStringAsync(); throw new Exception($"API returned error status {response.StatusCode}: {errorContent}"); } var tile = await response.Content.ReadFromJsonAsync(JsonOptions); if (tile == null) { throw new Exception("No tile data returned from API"); } Console.WriteLine(); Console.WriteLine("Tile Details:"); Console.WriteLine($" ID: {tile.Id}"); Console.WriteLine($" Zoom Level: {tile.ZoomLevel}"); Console.WriteLine($" Latitude: {tile.Latitude}"); Console.WriteLine($" Longitude: {tile.Longitude}"); Console.WriteLine($" Tile Size (meters): {tile.TileSizeMeters:F2}"); Console.WriteLine($" Tile Size (pixels): {tile.TileSizePixels}"); Console.WriteLine($" Image Type: {tile.ImageType}"); Console.WriteLine($" Maps Version: {tile.MapsVersion}"); Console.WriteLine($" File Path: {tile.FilePath}"); Console.WriteLine($" Created At: {tile.CreatedAt:yyyy-MM-dd HH:mm:ss}"); if (tile.ZoomLevel != zoomLevel) { throw new Exception($"Expected zoom level {zoomLevel}, got {tile.ZoomLevel}"); } if (string.IsNullOrEmpty(tile.FilePath)) { throw new Exception("File path is empty"); } if (tile.TileSizePixels != 256) { throw new Exception($"Expected tile size 256 pixels, got {tile.TileSizePixels}"); } if (tile.ImageType != "jpg") { throw new Exception($"Expected image type 'jpg', got '{tile.ImageType}'"); } Console.WriteLine(); Console.WriteLine("✓ Tile downloaded successfully"); Console.WriteLine("✓ Tile metadata validated"); Console.WriteLine(); Console.WriteLine("Testing tile reuse (downloading same tile again)..."); var response2 = await httpClient.PostAsJsonAsync("/api/satellite/tiles/download", request); if (!response2.IsSuccessStatusCode) { var errorContent = await response2.Content.ReadAsStringAsync(); throw new Exception($"Second download failed with status {response2.StatusCode}: {errorContent}"); } var tile2 = await response2.Content.ReadFromJsonAsync(JsonOptions); if (tile2 == null) { throw new Exception("No tile data returned from second download"); } Console.WriteLine($"✓ Second download returned tile ID: {tile2.Id}"); Console.WriteLine("✓ Tile reuse functionality working"); Console.WriteLine(); Console.WriteLine("Single Tile Download Test: PASSED"); } static async Task RunRegionProcessingTest(HttpClient httpClient) { Console.WriteLine(); Console.WriteLine("Test: Region Processing at Coordinates 47.461747, 37.647063"); Console.WriteLine("------------------------------------------------------------------"); const double latitude = 47.461747; const double longitude = 37.647063; const double sizeMeters = 200; const int zoomLevel = 18; var regionId = Guid.NewGuid(); Console.WriteLine($"Requesting region: ID={regionId}"); Console.WriteLine($" Coordinates: ({latitude}, {longitude})"); Console.WriteLine($" Size: {sizeMeters}m"); Console.WriteLine($" Zoom Level: {zoomLevel}"); Console.WriteLine(); var requestRegion = new RequestRegionRequest { Id = regionId, Latitude = latitude, Longitude = longitude, SizeMeters = sizeMeters, ZoomLevel = zoomLevel }; var requestResponse = await httpClient.PostAsJsonAsync("/api/satellite/request", requestRegion); if (!requestResponse.IsSuccessStatusCode) { var errorContent = await requestResponse.Content.ReadAsStringAsync(); throw new Exception($"Region request failed with status {requestResponse.StatusCode}: {errorContent}"); } var initialStatus = await requestResponse.Content.ReadFromJsonAsync(JsonOptions); if (initialStatus == null) { throw new Exception("No status returned from region request"); } Console.WriteLine($"✓ Region queued successfully"); Console.WriteLine($" Initial Status: {initialStatus.Status}"); Console.WriteLine(); Console.WriteLine("Polling for region status updates..."); RegionStatusResponse? finalStatus = null; int maxAttempts = 30; for (int i = 0; i < maxAttempts; i++) { await Task.Delay(1000); var statusResponse = await httpClient.GetAsync($"/api/satellite/region/{regionId}"); if (!statusResponse.IsSuccessStatusCode) { var errorContent = await statusResponse.Content.ReadAsStringAsync(); throw new Exception($"Status check failed with status {statusResponse.StatusCode}: {errorContent}"); } var status = await statusResponse.Content.ReadFromJsonAsync(JsonOptions); if (status == null) { throw new Exception("No status returned"); } Console.WriteLine($" Attempt {i + 1}: Status = {status.Status}"); if (status.Status == "completed" || status.Status == "failed") { finalStatus = status; break; } } if (finalStatus == null) { throw new Exception("Region processing did not complete in time"); } Console.WriteLine(); Console.WriteLine("Region Processing Results:"); Console.WriteLine($" Status: {finalStatus.Status}"); Console.WriteLine($" Tiles Downloaded: {finalStatus.TilesDownloaded}"); Console.WriteLine($" Tiles Reused: {finalStatus.TilesReused}"); Console.WriteLine($" CSV File: {finalStatus.CsvFilePath}"); Console.WriteLine($" Summary File: {finalStatus.SummaryFilePath}"); Console.WriteLine($" Created At: {finalStatus.CreatedAt:yyyy-MM-dd HH:mm:ss}"); Console.WriteLine($" Updated At: {finalStatus.UpdatedAt:yyyy-MM-dd HH:mm:ss}"); if (finalStatus.Status != "completed") { throw new Exception($"Expected status 'completed', got '{finalStatus.Status}'"); } if (string.IsNullOrEmpty(finalStatus.CsvFilePath)) { throw new Exception("CSV file path is empty"); } if (string.IsNullOrEmpty(finalStatus.SummaryFilePath)) { throw new Exception("Summary file path is empty"); } Console.WriteLine(); Console.WriteLine("✓ Region processed successfully"); Console.WriteLine("✓ CSV and summary files created"); Console.WriteLine(); Console.WriteLine("Region Processing Test: PASSED"); } } public record DownloadTileRequest { public double Latitude { get; set; } public double Longitude { get; set; } public int ZoomLevel { get; set; } } public record DownloadTileResponse { public Guid Id { get; set; } public int ZoomLevel { get; set; } public double Latitude { get; set; } public double Longitude { get; set; } public double TileSizeMeters { get; set; } public int TileSizePixels { get; set; } public string ImageType { get; set; } = string.Empty; public string? MapsVersion { get; set; } public string FilePath { get; set; } = string.Empty; public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } } public record RequestRegionRequest { public Guid Id { get; set; } public double Latitude { get; set; } public double Longitude { get; set; } public double SizeMeters { get; set; } public int ZoomLevel { get; set; } } public record RegionStatusResponse { public Guid Id { get; set; } public string Status { get; set; } = string.Empty; public string? CsvFilePath { get; set; } public string? SummaryFilePath { get; set; } public int TilesDownloaded { get; set; } public int TilesReused { get; set; } public DateTime CreatedAt { get; set; } public DateTime UpdatedAt { get; set; } }