mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-22 14:51:14 +00:00
[AZ-371] Refactor C18: magic numbers to ProcessingConfig/MapConfig
Promotes 8 operational levers into config keys with defaults that match the prior source literals byte-for-byte: ProcessingConfig: RegionProcessingTimeoutSeconds (300), RouteProcessingPollIntervalSeconds (5), MaxRoutePointSpacingMeters (200), LatLonTolerance (0.0001). MapConfig: TileSizePixels (256), AllowedZoomLevels ([15..19]), RetryBaseDelaySeconds (1), RetryMaxDelaySeconds (30). Sites updated: RegionService, RouteProcessingService, RoutePointGraphBuilder, RouteValidator, RouteService 4-arg ctor, RouteImageRenderer, GoogleMapsDownloaderV2, TileService. Closes LF-2 by forwarding HttpContext.RequestAborted from GetTileByLatLon into the downloader. appsettings.json gains the 8 new keys at default values. Tests: 141 / 141 unit + 5 / 5 smoke green. New ConfigDefaultsTests pins defaults to original literals; new TileService unit test asserts CT identity from caller to downloader (AZ-371 AC-3). Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,62 @@
|
||||
using FluentAssertions;
|
||||
using SatelliteProvider.Common.Configs;
|
||||
|
||||
namespace SatelliteProvider.Tests;
|
||||
|
||||
// AZ-371 / C18 — verifies the config defaults preserve the original literal values
|
||||
// that lived in source code prior to the magic-numbers-to-config refactor.
|
||||
public class ConfigDefaultsTests
|
||||
{
|
||||
[Fact]
|
||||
public void ProcessingConfig_RegionProcessingTimeout_PreservesOriginal_AZ371_AC2()
|
||||
{
|
||||
// Assert
|
||||
new ProcessingConfig().RegionProcessingTimeoutSeconds.Should().Be(300, "RegionService used TimeSpan.FromMinutes(5) before AZ-371");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessingConfig_RouteProcessingPollInterval_PreservesOriginal_AZ371_AC2()
|
||||
{
|
||||
// Assert
|
||||
new ProcessingConfig().RouteProcessingPollIntervalSeconds.Should().Be(5, "RouteProcessingService polled every 5s before AZ-371");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessingConfig_MaxRoutePointSpacingMeters_PreservesOriginal_AZ371_AC2()
|
||||
{
|
||||
// Assert
|
||||
new ProcessingConfig().MaxRoutePointSpacingMeters.Should().Be(200.0, "RoutePointGraphBuilder used 200m spacing before AZ-371");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void ProcessingConfig_LatLonTolerance_PreservesOriginal_AZ371_AC2()
|
||||
{
|
||||
// Assert
|
||||
new ProcessingConfig().LatLonTolerance.Should().Be(0.0001, "RouteValidator and GoogleMapsDownloaderV2 used 0.0001 before AZ-371");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapConfig_TileSizePixels_PreservesOriginal_AZ371_AC2()
|
||||
{
|
||||
// Assert
|
||||
new MapConfig().TileSizePixels.Should().Be(256, "TileService and GoogleMapsDownloaderV2 used 256 px before AZ-371");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapConfig_AllowedZoomLevels_PreservesOriginal_AZ371_AC2()
|
||||
{
|
||||
// Assert
|
||||
new MapConfig().AllowedZoomLevels.Should().Equal(15, 16, 17, 18, 19);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void MapConfig_RetryDelays_PreserveOriginal_AZ371_AC2()
|
||||
{
|
||||
// Arrange
|
||||
var cfg = new MapConfig();
|
||||
|
||||
// Assert
|
||||
cfg.RetryBaseDelaySeconds.Should().Be(1);
|
||||
cfg.RetryMaxDelaySeconds.Should().Be(30);
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,9 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Caching.Memory;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using SatelliteProvider.Common.Configs;
|
||||
using SatelliteProvider.Common.DTO;
|
||||
using SatelliteProvider.Common.Interfaces;
|
||||
using SatelliteProvider.DataAccess.Models;
|
||||
@@ -65,7 +67,8 @@ public class InfrastructureTests
|
||||
var logger = NullLogger<TileService>.Instance;
|
||||
|
||||
// Act
|
||||
var service = new TileService(downloader, tileRepo, cache, logger);
|
||||
var mapConfig = Options.Create(new MapConfig { Service = "GoogleMaps", ApiKey = "" });
|
||||
var service = new TileService(downloader, tileRepo, cache, mapConfig, logger);
|
||||
|
||||
// Assert
|
||||
service.Should().NotBeNull();
|
||||
|
||||
@@ -36,7 +36,8 @@ public class RegionServiceTests : IDisposable
|
||||
Mock<ITileService> tileService)
|
||||
{
|
||||
var storage = Options.Create(new StorageConfig { ReadyDirectory = _readyDir, TilesDirectory = "/tiles" });
|
||||
return new RegionService(regionRepo.Object, queue.Object, tileService.Object, storage, NullLogger<RegionService>.Instance);
|
||||
var processing = Options.Create(new ProcessingConfig());
|
||||
return new RegionService(regionRepo.Object, queue.Object, tileService.Object, storage, processing, NullLogger<RegionService>.Instance);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
|
||||
@@ -17,7 +17,8 @@ public class RouteImageRendererTests
|
||||
{
|
||||
loggerMock = new Mock<ILogger<RouteImageRenderer>>();
|
||||
var storageOptions = Options.Create(new StorageConfig());
|
||||
return new RouteImageRenderer(storageOptions, loggerMock.Object);
|
||||
var mapOptions = Options.Create(new MapConfig { Service = "GoogleMaps", ApiKey = "" });
|
||||
return new RouteImageRenderer(storageOptions, mapOptions, loggerMock.Object);
|
||||
}
|
||||
|
||||
private static void VerifyWarningLogged(Mock<ILogger<RouteImageRenderer>> loggerMock, string substringInState)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SatelliteProvider.Common.Configs;
|
||||
using SatelliteProvider.Common.DTO;
|
||||
using SatelliteProvider.Common.Utils;
|
||||
using SatelliteProvider.Services.RouteManagement;
|
||||
@@ -8,13 +10,18 @@ namespace SatelliteProvider.Tests;
|
||||
|
||||
public class RoutePointGraphBuilderTests
|
||||
{
|
||||
private static readonly ProcessingConfig DefaultProcessingConfig = new();
|
||||
|
||||
private static RoutePointGraphBuilder MakeBuilder() =>
|
||||
new(Options.Create(new ProcessingConfig()));
|
||||
|
||||
private static List<RoutePoint> ToRoutePoints(IEnumerable<(double Lat, double Lon)> points) =>
|
||||
points.Select(p => new RoutePoint { Latitude = p.Lat, Longitude = p.Lon }).ToList();
|
||||
|
||||
[Fact]
|
||||
public void Build_TwoUserPoints_FirstIsStart_LastIsEnd_BetweenAreIntermediate()
|
||||
{
|
||||
var sut = new RoutePointGraphBuilder();
|
||||
var sut = MakeBuilder();
|
||||
var input = ToRoutePoints(TestCoordinates.Route.Route01Points);
|
||||
|
||||
var graph = sut.Build(input);
|
||||
@@ -28,7 +35,7 @@ public class RoutePointGraphBuilderTests
|
||||
[Fact]
|
||||
public void Build_ConsecutivePointsRespectMaxSpacing()
|
||||
{
|
||||
var sut = new RoutePointGraphBuilder();
|
||||
var sut = MakeBuilder();
|
||||
var input = ToRoutePoints(TestCoordinates.Route.Route01Points);
|
||||
|
||||
var graph = sut.Build(input);
|
||||
@@ -40,15 +47,15 @@ public class RoutePointGraphBuilderTests
|
||||
var distance = GeoUtils.CalculateDistance(
|
||||
new GeoPoint(prev.Latitude, prev.Longitude),
|
||||
new GeoPoint(cur.Latitude, cur.Longitude));
|
||||
distance.Should().BeLessThanOrEqualTo(RoutePointGraphBuilder.MaxPointSpacingMeters + 0.5,
|
||||
$"point {i - 1}→{i} must be ≤{RoutePointGraphBuilder.MaxPointSpacingMeters}m");
|
||||
distance.Should().BeLessThanOrEqualTo(DefaultProcessingConfig.MaxRoutePointSpacingMeters + 0.5,
|
||||
$"point {i - 1}→{i} must be ≤{DefaultProcessingConfig.MaxRoutePointSpacingMeters}m");
|
||||
}
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void Build_TenPointRoute_HasOneStartOneEndAndEightAction()
|
||||
{
|
||||
var sut = new RoutePointGraphBuilder();
|
||||
var sut = MakeBuilder();
|
||||
var input = ToRoutePoints(TestCoordinates.Route.Route04Points);
|
||||
|
||||
var graph = sut.Build(input);
|
||||
@@ -62,7 +69,7 @@ public class RoutePointGraphBuilderTests
|
||||
[Fact]
|
||||
public void Build_TotalDistanceEqualsSumOfHaversineSegments()
|
||||
{
|
||||
var sut = new RoutePointGraphBuilder();
|
||||
var sut = MakeBuilder();
|
||||
var input = ToRoutePoints(TestCoordinates.Route.Route01Points);
|
||||
|
||||
var graph = sut.Build(input);
|
||||
@@ -83,7 +90,7 @@ public class RoutePointGraphBuilderTests
|
||||
[Fact]
|
||||
public void Build_SequenceNumbersAreContiguousAndStartAtZero()
|
||||
{
|
||||
var sut = new RoutePointGraphBuilder();
|
||||
var sut = MakeBuilder();
|
||||
var input = ToRoutePoints(TestCoordinates.Route.Route04Points);
|
||||
|
||||
var graph = sut.Build(input);
|
||||
@@ -95,7 +102,7 @@ public class RoutePointGraphBuilderTests
|
||||
[Fact]
|
||||
public void Build_FirstPointHasNullDistanceFromPrevious()
|
||||
{
|
||||
var sut = new RoutePointGraphBuilder();
|
||||
var sut = MakeBuilder();
|
||||
var input = ToRoutePoints(TestCoordinates.Route.Route01Points);
|
||||
|
||||
var graph = sut.Build(input);
|
||||
@@ -107,7 +114,7 @@ public class RoutePointGraphBuilderTests
|
||||
[Fact]
|
||||
public void Build_FewerThanTwoPoints_Throws()
|
||||
{
|
||||
var sut = new RoutePointGraphBuilder();
|
||||
var sut = MakeBuilder();
|
||||
var input = new List<RoutePoint> { new() { Latitude = 47.46, Longitude = 37.64 } };
|
||||
|
||||
Action act = () => sut.Build(input);
|
||||
@@ -118,7 +125,7 @@ public class RoutePointGraphBuilderTests
|
||||
[Fact]
|
||||
public void Build_NullInput_Throws()
|
||||
{
|
||||
var sut = new RoutePointGraphBuilder();
|
||||
var sut = MakeBuilder();
|
||||
|
||||
Action act = () => sut.Build(null!);
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Moq;
|
||||
using SatelliteProvider.Common.Configs;
|
||||
using SatelliteProvider.Common.DTO;
|
||||
using SatelliteProvider.Common.Interfaces;
|
||||
using SatelliteProvider.Common.Utils;
|
||||
@@ -17,7 +19,7 @@ public class RouteServiceTests
|
||||
Mock<IRouteRepository> routeRepo,
|
||||
Mock<IRegionService> regionService)
|
||||
{
|
||||
return new RouteService(routeRepo.Object, regionService.Object, NullLogger<RouteService>.Instance);
|
||||
return new RouteService(routeRepo.Object, regionService.Object, Options.Create(new ProcessingConfig()), NullLogger<RouteService>.Instance);
|
||||
}
|
||||
|
||||
private static CreateRouteRequest BuildRequest(IEnumerable<(double Lat, double Lon)> points, double regionSize = 500, int zoom = 18, bool requestMaps = false, Geofences? geofences = null)
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
using FluentAssertions;
|
||||
using Microsoft.Extensions.Options;
|
||||
using SatelliteProvider.Common.Configs;
|
||||
using SatelliteProvider.Common.DTO;
|
||||
using SatelliteProvider.Services.RouteManagement;
|
||||
using SatelliteProvider.Tests.Fixtures;
|
||||
@@ -7,6 +9,9 @@ namespace SatelliteProvider.Tests;
|
||||
|
||||
public class RouteValidatorTests
|
||||
{
|
||||
private static RouteValidator MakeValidator() =>
|
||||
new(Options.Create(new ProcessingConfig()));
|
||||
|
||||
private static CreateRouteRequest BuildValidRequest()
|
||||
{
|
||||
return new CreateRouteRequest
|
||||
@@ -25,7 +30,7 @@ public class RouteValidatorTests
|
||||
[Fact]
|
||||
public void Validate_ValidRequest_DoesNotThrow_AZ365_AC2()
|
||||
{
|
||||
var sut = new RouteValidator();
|
||||
var sut = MakeValidator();
|
||||
var request = BuildValidRequest();
|
||||
|
||||
Action act = () => sut.Validate(request);
|
||||
@@ -36,7 +41,7 @@ public class RouteValidatorTests
|
||||
[Fact]
|
||||
public void Validate_FewerThanTwoPoints_Throws()
|
||||
{
|
||||
var sut = new RouteValidator();
|
||||
var sut = MakeValidator();
|
||||
var request = BuildValidRequest();
|
||||
request.Points = new List<RoutePoint> { new() { Latitude = 47.46, Longitude = 37.64 } };
|
||||
|
||||
@@ -48,7 +53,7 @@ public class RouteValidatorTests
|
||||
[Fact]
|
||||
public void Validate_RegionSizeOutOfRange_Throws()
|
||||
{
|
||||
var sut = new RouteValidator();
|
||||
var sut = MakeValidator();
|
||||
var request = BuildValidRequest();
|
||||
request.RegionSizeMeters = 50;
|
||||
|
||||
@@ -61,7 +66,7 @@ public class RouteValidatorTests
|
||||
[Fact]
|
||||
public void Validate_BlankName_Throws()
|
||||
{
|
||||
var sut = new RouteValidator();
|
||||
var sut = MakeValidator();
|
||||
var request = BuildValidRequest();
|
||||
request.Name = " ";
|
||||
|
||||
@@ -73,7 +78,7 @@ public class RouteValidatorTests
|
||||
[Fact]
|
||||
public void Validate_GeofencePolygonZeroZero_Throws()
|
||||
{
|
||||
var sut = new RouteValidator();
|
||||
var sut = MakeValidator();
|
||||
var request = BuildValidRequest();
|
||||
request.Geofences = new Geofences
|
||||
{
|
||||
@@ -92,7 +97,7 @@ public class RouteValidatorTests
|
||||
[Fact]
|
||||
public void Validate_GeofenceInvertedLatitudes_Throws()
|
||||
{
|
||||
var sut = new RouteValidator();
|
||||
var sut = MakeValidator();
|
||||
var request = BuildValidRequest();
|
||||
request.Geofences = new Geofences
|
||||
{
|
||||
@@ -114,7 +119,7 @@ public class RouteValidatorTests
|
||||
[Fact]
|
||||
public void Validate_NullPolygonCorner_Throws()
|
||||
{
|
||||
var sut = new RouteValidator();
|
||||
var sut = MakeValidator();
|
||||
var request = BuildValidRequest();
|
||||
request.Geofences = new Geofences
|
||||
{
|
||||
@@ -133,7 +138,7 @@ public class RouteValidatorTests
|
||||
[Fact]
|
||||
public void Validate_OutOfRangeLatitude_Throws()
|
||||
{
|
||||
var sut = new RouteValidator();
|
||||
var sut = MakeValidator();
|
||||
var request = BuildValidRequest();
|
||||
request.Geofences = new Geofences
|
||||
{
|
||||
@@ -156,7 +161,7 @@ public class RouteValidatorTests
|
||||
[Fact]
|
||||
public void Validate_MultipleErrors_AggregatesIntoSingleException_AZ365_AC2()
|
||||
{
|
||||
var sut = new RouteValidator();
|
||||
var sut = MakeValidator();
|
||||
var request = BuildValidRequest();
|
||||
request.Name = "";
|
||||
request.RegionSizeMeters = 50;
|
||||
@@ -175,7 +180,7 @@ public class RouteValidatorTests
|
||||
[Fact]
|
||||
public void Validate_NullRequest_Throws()
|
||||
{
|
||||
var sut = new RouteValidator();
|
||||
var sut = MakeValidator();
|
||||
|
||||
Action act = () => sut.Validate(null!);
|
||||
|
||||
|
||||
@@ -24,6 +24,7 @@ public class TileServiceTests
|
||||
downloader.Object,
|
||||
tileRepo.Object,
|
||||
cache ?? new MemoryCache(new MemoryCacheOptions()),
|
||||
Options.Create(new MapConfig { Service = "GoogleMaps", ApiKey = "" }),
|
||||
NullLogger<TileService>.Instance);
|
||||
}
|
||||
|
||||
@@ -372,6 +373,28 @@ public class TileServiceTests
|
||||
tileRepo.Verify(r => r.InsertAsync(It.IsAny<TileEntity>()), Times.Once);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DownloadAndStoreSingleTileAsync_ForwardsCancellationTokenToDownloader_AZ371_AC3()
|
||||
{
|
||||
// Arrange
|
||||
const int zoom = 18;
|
||||
var downloader = new Mock<ISatelliteDownloader>();
|
||||
var tileRepo = new Mock<ITileRepository>();
|
||||
CancellationToken capturedToken = default;
|
||||
downloader
|
||||
.Setup(d => d.DownloadSingleTileAsync(It.IsAny<double>(), It.IsAny<double>(), zoom, It.IsAny<CancellationToken>()))
|
||||
.Callback<double, double, int, CancellationToken>((_, _, _, ct) => capturedToken = ct)
|
||||
.ReturnsAsync(new DownloadedTileInfoV2(1, 2, zoom, 0, 0, "p.jpg", 100.0));
|
||||
var service = BuildService(downloader, tileRepo);
|
||||
using var cts = new CancellationTokenSource();
|
||||
|
||||
// Act
|
||||
await service.DownloadAndStoreSingleTileAsync(0, 0, zoom, cts.Token);
|
||||
|
||||
// Assert
|
||||
capturedToken.Should().Be(cts.Token, "AZ-371 AC-3: caller-supplied CT must reach the downloader");
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task DownloadAndStoreSingleTileAsync_DownloaderThrows_DoesNotInsert_AZ311_AC2b()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user