using FluentAssertions; using Microsoft.Extensions.Options; using SatelliteProvider.Common.Configs; using SatelliteProvider.Common.DTO; using SatelliteProvider.Services.RouteManagement; using SatelliteProvider.Tests.Fixtures; namespace SatelliteProvider.Tests; public class RouteValidatorTests { private static RouteValidator MakeValidator() => new(Options.Create(new ProcessingConfig())); private static CreateRouteRequest BuildValidRequest() { return new CreateRouteRequest { Id = Guid.NewGuid(), Name = "valid-route", Description = "test", RegionSizeMeters = 500, ZoomLevel = 18, Points = TestCoordinates.Route.Route01Points .Select(p => new RoutePoint { Latitude = p.Lat, Longitude = p.Lon }) .ToList(), }; } [Fact] public void Validate_ValidRequest_DoesNotThrow_AZ365_AC2() { var sut = MakeValidator(); var request = BuildValidRequest(); Action act = () => sut.Validate(request); act.Should().NotThrow(); } [Fact] public void Validate_FewerThanTwoPoints_Throws() { var sut = MakeValidator(); var request = BuildValidRequest(); request.Points = new List { new() { Latitude = 47.46, Longitude = 37.64 } }; Action act = () => sut.Validate(request); act.Should().Throw().WithMessage("*at least 2 points*"); } [Fact] public void Validate_RegionSizeOutOfRange_Throws() { var sut = MakeValidator(); var request = BuildValidRequest(); request.RegionSizeMeters = 50; Action act = () => sut.Validate(request); act.Should().Throw() .WithMessage("*Region size must be between 100 and 10000*"); } [Fact] public void Validate_BlankName_Throws() { var sut = MakeValidator(); var request = BuildValidRequest(); request.Name = " "; Action act = () => sut.Validate(request); act.Should().Throw().WithMessage("*Route name is required*"); } [Fact] public void Validate_GeofencePolygonZeroZero_Throws() { var sut = MakeValidator(); var request = BuildValidRequest(); request.Geofences = new Geofences { Polygons = new List { new() { NorthWest = new GeoPoint(0, 0), SouthEast = new GeoPoint(0, 0) }, }, }; Action act = () => sut.Validate(request); act.Should().Throw() .WithMessage("*coordinates cannot be (0,0)*"); } [Fact] public void Validate_GeofenceInvertedLatitudes_Throws() { var sut = MakeValidator(); var request = BuildValidRequest(); request.Geofences = new Geofences { Polygons = new List { new() { NorthWest = new GeoPoint(48.250, 37.370), SouthEast = new GeoPoint(48.280, 37.395), }, }, }; Action act = () => sut.Validate(request); act.Should().Throw().WithMessage("*northWest latitude*"); } [Fact] public void Validate_NullPolygonCorner_Throws() { var sut = MakeValidator(); var request = BuildValidRequest(); request.Geofences = new Geofences { Polygons = new List { new() { NorthWest = null, SouthEast = new GeoPoint(48.260, 37.390) }, }, }; Action act = () => sut.Validate(request); act.Should().Throw() .WithMessage("*polygon coordinates are required*"); } [Fact] public void Validate_OutOfRangeLatitude_Throws() { var sut = MakeValidator(); var request = BuildValidRequest(); request.Geofences = new Geofences { Polygons = new List { new() { NorthWest = new GeoPoint(95, 37.370), SouthEast = new GeoPoint(48.265, 37.395), }, }, }; Action act = () => sut.Validate(request); act.Should().Throw() .WithMessage("*coordinates must be valid*"); } [Fact] public void Validate_MultipleErrors_AggregatesIntoSingleException_AZ365_AC2() { var sut = MakeValidator(); var request = BuildValidRequest(); request.Name = ""; request.RegionSizeMeters = 50; request.Points = new List(); Action act = () => sut.Validate(request); act.Should().Throw() .Where(ex => ex.Message.Contains("at least 2 points") && ex.Message.Contains("Region size must be between 100 and 10000") && ex.Message.Contains("Route name is required"), "AZ-365 AC-2: validator aggregates all failures into a single ArgumentException"); } [Fact] public void Validate_NullRequest_Throws() { var sut = MakeValidator(); Action act = () => sut.Validate(null!); act.Should().Throw(); } }