diff --git a/SatelliteProvider.Api/Program.cs b/SatelliteProvider.Api/Program.cs index 12d8825..1e7c86e 100644 --- a/SatelliteProvider.Api/Program.cs +++ b/SatelliteProvider.Api/Program.cs @@ -5,6 +5,8 @@ using Swashbuckle.AspNetCore.SwaggerGen; using SatelliteProvider.DataAccess; using SatelliteProvider.DataAccess.Repositories; using SatelliteProvider.Common.Configs; +using SatelliteProvider.Common.Interfaces; +using SatelliteProvider.Services; var builder = WebApplication.CreateBuilder(args); @@ -18,6 +20,10 @@ builder.Services.Configure(builder.Configuration.GetSection("P builder.Services.AddSingleton(sp => new TileRepository(connectionString)); builder.Services.AddSingleton(sp => new RegionRepository(connectionString)); +builder.Services.AddHttpClient(); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); + builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(c => { @@ -71,6 +77,9 @@ app.MapPost("/api/satellite/upload", UploadImage) .WithOpenApi(op => new(op) { Summary = "Upload image with metadata and save to /maps folder" }) .DisableAntiforgery(); +app.MapPost("/api/satellite/tiles/download", DownloadSingleTile) + .WithOpenApi(op => new(op) { Summary = "TEMPORARY: Download single tile at specified coordinates" }); + app.Run(); IResult GetSatelliteTilesByLatLon(double lat, double lon, double squareSideMeters) @@ -88,6 +97,52 @@ IResult UploadImage([FromForm] UploadImageRequest request) return Results.Ok(new SaveResult { Success = false }); } +async Task DownloadSingleTile([FromBody] DownloadTileRequest request, ITileService tileService, ILogger logger) +{ + try + { + logger.LogInformation("Downloading single tile at ({Lat}, {Lon}) with zoom level {Zoom}", + request.Latitude, request.Longitude, request.ZoomLevel); + + var tiles = await tileService.DownloadAndStoreTilesAsync( + request.Latitude, + request.Longitude, + 1.0, + request.ZoomLevel); + + if (tiles.Count == 0) + { + logger.LogWarning("No tiles were downloaded"); + return Results.NotFound(new { message = "No tiles were downloaded" }); + } + + var tile = tiles[0]; + logger.LogInformation("Tile downloaded successfully: {Id}", tile.Id); + + var response = new DownloadTileResponse + { + Id = tile.Id, + ZoomLevel = tile.ZoomLevel, + Latitude = tile.Latitude, + Longitude = tile.Longitude, + TileSizeMeters = tile.TileSizeMeters, + TileSizePixels = tile.TileSizePixels, + ImageType = tile.ImageType, + MapsVersion = tile.MapsVersion, + FilePath = tile.FilePath, + CreatedAt = tile.CreatedAt, + UpdatedAt = tile.UpdatedAt + }; + + return Results.Ok(response); + } + catch (Exception ex) + { + logger.LogError(ex, "Failed to download tile"); + return Results.Problem(detail: ex.Message, statusCode: 500); + } +} + public record GetSatelliteTilesResponse { public List Tiles { get; set; } = new(); @@ -135,6 +190,33 @@ public record SaveResult public string? Exception { get; set; } } +public record DownloadTileRequest +{ + [Required] + public double Latitude { get; set; } + + [Required] + public double Longitude { get; set; } + + [Required] + public int ZoomLevel { get; set; } = 20; +} + +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 class ParameterDescriptionFilter : IOperationFilter { public void Apply(OpenApiOperation operation, OperationFilterContext context) diff --git a/SatelliteProvider.Api/SatelliteProvider.Api.csproj b/SatelliteProvider.Api/SatelliteProvider.Api.csproj index c67e5b4..ca70730 100644 --- a/SatelliteProvider.Api/SatelliteProvider.Api.csproj +++ b/SatelliteProvider.Api/SatelliteProvider.Api.csproj @@ -16,6 +16,7 @@ + diff --git a/SatelliteProvider.Common/DTO/TileMetadata.cs b/SatelliteProvider.Common/DTO/TileMetadata.cs new file mode 100644 index 0000000..cba2cb7 --- /dev/null +++ b/SatelliteProvider.Common/DTO/TileMetadata.cs @@ -0,0 +1,17 @@ +namespace SatelliteProvider.Common.DTO; + +public class TileMetadata +{ + 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; } +} + diff --git a/SatelliteProvider.Common/Interfaces/ITileService.cs b/SatelliteProvider.Common/Interfaces/ITileService.cs new file mode 100644 index 0000000..38c5203 --- /dev/null +++ b/SatelliteProvider.Common/Interfaces/ITileService.cs @@ -0,0 +1,11 @@ +using SatelliteProvider.Common.DTO; + +namespace SatelliteProvider.Common.Interfaces; + +public interface ITileService +{ + Task> DownloadAndStoreTilesAsync(double latitude, double longitude, double sizeMeters, int zoomLevel, CancellationToken cancellationToken = default); + Task GetTileAsync(Guid id); + Task> GetTilesByRegionAsync(double latitude, double longitude, double sizeMeters, int zoomLevel); +} + diff --git a/SatelliteProvider.IntegrationTests/Dockerfile b/SatelliteProvider.IntegrationTests/Dockerfile new file mode 100644 index 0000000..fcc3caf --- /dev/null +++ b/SatelliteProvider.IntegrationTests/Dockerfile @@ -0,0 +1,19 @@ +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +WORKDIR /src +COPY ["SatelliteProvider.IntegrationTests/SatelliteProvider.IntegrationTests.csproj", "SatelliteProvider.IntegrationTests/"] +COPY ["SatelliteProvider.Common/SatelliteProvider.Common.csproj", "SatelliteProvider.Common/"] +COPY ["SatelliteProvider.DataAccess/SatelliteProvider.DataAccess.csproj", "SatelliteProvider.DataAccess/"] +COPY ["SatelliteProvider.Services/SatelliteProvider.Services.csproj", "SatelliteProvider.Services/"] +RUN dotnet restore "SatelliteProvider.IntegrationTests/SatelliteProvider.IntegrationTests.csproj" +COPY . . +WORKDIR "/src/SatelliteProvider.IntegrationTests" +RUN dotnet build "SatelliteProvider.IntegrationTests.csproj" -c Release -o /app/build + +FROM build AS publish +RUN dotnet publish "SatelliteProvider.IntegrationTests.csproj" -c Release -o /app/publish /p:UseAppHost=false + +FROM mcr.microsoft.com/dotnet/runtime:8.0 AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "SatelliteProvider.IntegrationTests.dll"] + diff --git a/SatelliteProvider.IntegrationTests/Program.cs b/SatelliteProvider.IntegrationTests/Program.cs new file mode 100644 index 0000000..d8127fc --- /dev/null +++ b/SatelliteProvider.IntegrationTests/Program.cs @@ -0,0 +1,189 @@ +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); + + 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"); + } +} + +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; } +} diff --git a/SatelliteProvider.IntegrationTests/SatelliteProvider.IntegrationTests.csproj b/SatelliteProvider.IntegrationTests/SatelliteProvider.IntegrationTests.csproj new file mode 100644 index 0000000..206b89a --- /dev/null +++ b/SatelliteProvider.IntegrationTests/SatelliteProvider.IntegrationTests.csproj @@ -0,0 +1,10 @@ + + + + Exe + net8.0 + enable + enable + + + diff --git a/SatelliteProvider.Services/GoogleMapsDownloader.cs b/SatelliteProvider.Services/GoogleMapsDownloader.cs index b811da9..e496573 100644 --- a/SatelliteProvider.Services/GoogleMapsDownloader.cs +++ b/SatelliteProvider.Services/GoogleMapsDownloader.cs @@ -11,17 +11,17 @@ using SixLabors.ImageSharp; namespace SatelliteProvider.Services; +public record DownloadedTileInfo(int X, int Y, int ZoomLevel, double Latitude, double Longitude, string FilePath, double TileSizeMeters); -public class GoogleMapsDownloader(ILogger logger, IOptions mapConfig, IHttpClientFactory httpClientFactory) +public class GoogleMapsDownloader(ILogger logger, IOptions mapConfig, IOptions storageConfig, IHttpClientFactory httpClientFactory) : ISatelliteDownloader { private const string TILE_URL_TEMPLATE = "https://mt{0}.google.com/vt/lyrs=s&x={1}&y={2}&z={3}&token={4}"; private const string USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/100.0.0.0 Safari/537.36"; private const int NUM_SERVERS = 4; + private const int TILE_SIZE_PIXELS = 256; private readonly string _apiKey = mapConfig.Value.ApiKey; - - private readonly string _satDirectory = Path.Combine(Directory.GetCurrentDirectory(), "maps"); - + private readonly string _tilesDirectory = storageConfig.Value.TilesDirectory; private record SessionResponse(string Session); @@ -45,6 +45,11 @@ public class GoogleMapsDownloader(ILogger logger, IOptions } public async Task GetTiles(GeoPoint centerGeoPoint, double radiusM, int zoomLevel, CancellationToken token = default) + { + await GetTilesWithMetadataAsync(centerGeoPoint, radiusM, zoomLevel, token); + } + + public async Task> GetTilesWithMetadataAsync(GeoPoint centerGeoPoint, double radiusM, int zoomLevel, CancellationToken token = default) { var (latMin, latMax, lonMin, lonMax) = GeoUtils.GetBoundingBox(centerGeoPoint, radiusM); @@ -52,6 +57,7 @@ public class GoogleMapsDownloader(ILogger logger, IOptions var (xMax, yMax) = GeoUtils.WorldToTilePos(new GeoPoint(latMin, lonMax), zoomLevel); var tilesToDownload = new ConcurrentQueue(); + var downloadedTiles = new ConcurrentBag(); var server = 0; var sessionToken = await GetSessionToken(); @@ -69,13 +75,14 @@ public class GoogleMapsDownloader(ILogger logger, IOptions for (int i = 0; i < NUM_SERVERS; i++) { - downloadTasks.Add(Task.Run(() => DownloadTilesWorker(tilesToDownload, token), token)); + downloadTasks.Add(Task.Run(() => DownloadTilesWorker(tilesToDownload, downloadedTiles, zoomLevel, token), token)); } await Task.WhenAll(downloadTasks); + return downloadedTiles.ToList(); } - private async Task DownloadTilesWorker(ConcurrentQueue tilesToDownload, CancellationToken token) + private async Task DownloadTilesWorker(ConcurrentQueue tilesToDownload, ConcurrentBag downloadedTiles, int zoomLevel, CancellationToken token) { using var httpClient = httpClientFactory.CreateClient(); @@ -84,12 +91,33 @@ public class GoogleMapsDownloader(ILogger logger, IOptions if (token.IsCancellationRequested) break; try { + Directory.CreateDirectory(_tilesDirectory); + + var timestamp = DateTime.UtcNow.ToString("yyyyMMddHHmmss"); + var fileName = $"tile_{tileInfo.Zoom}_{tileInfo.X}_{tileInfo.Y}_{timestamp}.jpg"; + var filePath = Path.Combine(_tilesDirectory, fileName); + httpClient.DefaultRequestHeaders.UserAgent.ParseAdd(USER_AGENT); var response = await httpClient.GetAsync(tileInfo.Url, token); response.EnsureSuccessStatusCode(); var tileData = await response.Content.ReadAsByteArrayAsync(token); using var tileImage = Image.Load(tileData); - await tileImage.SaveAsync(Path.Combine(_satDirectory, tileInfo.FileName), token); + await tileImage.SaveAsync(filePath, token); + + var tileCenter = GeoUtils.TileToWorldPos(tileInfo.X, tileInfo.Y, tileInfo.Zoom); + var tileSizeMeters = CalculateTileSizeInMeters(tileInfo.Zoom, tileCenter.Lat); + + var downloadedTile = new DownloadedTileInfo( + tileInfo.X, + tileInfo.Y, + tileInfo.Zoom, + tileCenter.Lat, + tileCenter.Lon, + filePath, + tileSizeMeters + ); + + downloadedTiles.Add(downloadedTile); } catch (HttpRequestException requestException) { @@ -101,4 +129,12 @@ public class GoogleMapsDownloader(ILogger logger, IOptions } } } + + private static double CalculateTileSizeInMeters(int zoomLevel, double latitude) + { + const double EARTH_CIRCUMFERENCE_METERS = 40075016.686; + var latRad = latitude * Math.PI / 180.0; + var metersPerPixel = (EARTH_CIRCUMFERENCE_METERS * Math.Cos(latRad)) / (Math.Pow(2, zoomLevel) * TILE_SIZE_PIXELS); + return metersPerPixel * TILE_SIZE_PIXELS; + } } \ No newline at end of file diff --git a/SatelliteProvider.Services/SatelliteProvider.Services.csproj b/SatelliteProvider.Services/SatelliteProvider.Services.csproj index 1155581..b7512db 100644 --- a/SatelliteProvider.Services/SatelliteProvider.Services.csproj +++ b/SatelliteProvider.Services/SatelliteProvider.Services.csproj @@ -16,6 +16,7 @@ + diff --git a/SatelliteProvider.Services/TileService.cs b/SatelliteProvider.Services/TileService.cs new file mode 100644 index 0000000..918bf2e --- /dev/null +++ b/SatelliteProvider.Services/TileService.cs @@ -0,0 +1,115 @@ +using Microsoft.Extensions.Logging; +using SatelliteProvider.Common.DTO; +using SatelliteProvider.Common.Interfaces; +using SatelliteProvider.DataAccess.Models; +using SatelliteProvider.DataAccess.Repositories; + +namespace SatelliteProvider.Services; + +public class TileService : ITileService +{ + private readonly GoogleMapsDownloader _downloader; + private readonly ITileRepository _tileRepository; + private readonly ILogger _logger; + + public TileService( + GoogleMapsDownloader downloader, + ITileRepository tileRepository, + ILogger logger) + { + _downloader = downloader; + _tileRepository = tileRepository; + _logger = logger; + } + + public async Task> DownloadAndStoreTilesAsync( + double latitude, + double longitude, + double sizeMeters, + int zoomLevel, + CancellationToken cancellationToken = default) + { + var existingTiles = await _tileRepository.GetTilesByRegionAsync(latitude, longitude, sizeMeters, zoomLevel); + var existingTilesList = existingTiles.ToList(); + + _logger.LogInformation("Found {Count} existing tiles for region", existingTilesList.Count); + + var centerPoint = new GeoPoint(latitude, longitude); + var downloadedTiles = await _downloader.GetTilesWithMetadataAsync(centerPoint, sizeMeters / 2, zoomLevel, cancellationToken); + + var result = new List(); + + foreach (var downloadedTile in downloadedTiles) + { + var existingTile = existingTilesList.FirstOrDefault(t => + Math.Abs(t.Latitude - downloadedTile.Latitude) < 0.0001 && + Math.Abs(t.Longitude - downloadedTile.Longitude) < 0.0001 && + t.ZoomLevel == downloadedTile.ZoomLevel); + + if (existingTile != null) + { + _logger.LogDebug("Reusing existing tile at ({Lat}, {Lon})", downloadedTile.Latitude, downloadedTile.Longitude); + result.Add(MapToMetadata(existingTile)); + } + else + { + var now = DateTime.UtcNow; + var tileEntity = new TileEntity + { + Id = Guid.NewGuid(), + ZoomLevel = downloadedTile.ZoomLevel, + Latitude = downloadedTile.Latitude, + Longitude = downloadedTile.Longitude, + TileSizeMeters = downloadedTile.TileSizeMeters, + TileSizePixels = 256, + ImageType = "jpg", + MapsVersion = $"downloaded_{now:yyyy-MM-dd}", + FilePath = downloadedTile.FilePath, + CreatedAt = now, + UpdatedAt = now + }; + + await _tileRepository.InsertAsync(tileEntity); + _logger.LogInformation("Saved new tile {Id} at ({Lat}, {Lon})", tileEntity.Id, tileEntity.Latitude, tileEntity.Longitude); + result.Add(MapToMetadata(tileEntity)); + } + } + + return result; + } + + public async Task GetTileAsync(Guid id) + { + var tile = await _tileRepository.GetByIdAsync(id); + return tile != null ? MapToMetadata(tile) : null; + } + + public async Task> GetTilesByRegionAsync( + double latitude, + double longitude, + double sizeMeters, + int zoomLevel) + { + var tiles = await _tileRepository.GetTilesByRegionAsync(latitude, longitude, sizeMeters, zoomLevel); + return tiles.Select(MapToMetadata); + } + + private static TileMetadata MapToMetadata(TileEntity entity) + { + return new TileMetadata + { + Id = entity.Id, + ZoomLevel = entity.ZoomLevel, + Latitude = entity.Latitude, + Longitude = entity.Longitude, + TileSizeMeters = entity.TileSizeMeters, + TileSizePixels = entity.TileSizePixels, + ImageType = entity.ImageType, + MapsVersion = entity.MapsVersion, + FilePath = entity.FilePath, + CreatedAt = entity.CreatedAt, + UpdatedAt = entity.UpdatedAt + }; + } +} + diff --git a/SatelliteProvider.sln b/SatelliteProvider.sln index e56bd89..58b7a16 100644 --- a/SatelliteProvider.sln +++ b/SatelliteProvider.sln @@ -11,6 +11,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SatelliteProvider.Tests", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SatelliteProvider.DataAccess", "SatelliteProvider.DataAccess\SatelliteProvider.DataAccess.csproj", "{8709915B-313D-4CFA-9E0D-0B312F3EA5C8}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SatelliteProvider.IntegrationTests", "SatelliteProvider.IntegrationTests\SatelliteProvider.IntegrationTests.csproj", "{938FC7B2-759F-4B8F-90A3-21F8AD15BB1F}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -37,5 +39,9 @@ Global {8709915B-313D-4CFA-9E0D-0B312F3EA5C8}.Debug|Any CPU.Build.0 = Debug|Any CPU {8709915B-313D-4CFA-9E0D-0B312F3EA5C8}.Release|Any CPU.ActiveCfg = Release|Any CPU {8709915B-313D-4CFA-9E0D-0B312F3EA5C8}.Release|Any CPU.Build.0 = Release|Any CPU + {938FC7B2-759F-4B8F-90A3-21F8AD15BB1F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {938FC7B2-759F-4B8F-90A3-21F8AD15BB1F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {938FC7B2-759F-4B8F-90A3-21F8AD15BB1F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {938FC7B2-759F-4B8F-90A3-21F8AD15BB1F}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/docker-compose.tests.yml b/docker-compose.tests.yml new file mode 100644 index 0000000..aa7fc55 --- /dev/null +++ b/docker-compose.tests.yml @@ -0,0 +1,13 @@ +services: + integration-tests: + build: + context: . + dockerfile: SatelliteProvider.IntegrationTests/Dockerfile + container_name: satellite-provider-integration-tests + environment: + - API_URL=http://api:8080 + depends_on: + api: + condition: service_started + restart: "no" +