Files
satellite-provider/SatelliteProvider.Tests/RouteResponseMapperTests.cs
T
Oleksandr Bezdieniezhnykh 23ab05766d [AZ-370] Refactor C17: status / point-type enums + AC RT2 update
Replaces bare strings with two enums in Common/Enums/:
  RegionStatus { Queued, Processing, Completed, Failed }
  RoutePointType { Start, End, Action, Intermediate }

Adds a Dapper EnumStringTypeHandler<T> (DataAccess/TypeHandlers/)
that round-trips enums to/from lowercase strings, registered once
at startup via DapperEnumTypeHandlers.RegisterAll(). DataAccess now
references Common (project ref) so entities can carry the enum types.

Sites converted: RegionService (5), RouteProcessingService (3),
RoutePointGraphBuilder (4), entity Status/PointType columns. Log
message and summary file format preserved via .ToLowerInvariant().

API JSON contract preserved by adding JsonStringEnumConverter with
JsonNamingPolicy.CamelCase to the http JSON options — single-word
enum members serialize to the same lowercase strings as before.

DTO renamed: Common.DTO.RegionStatus -> RegionStatusResponse to
free the RegionStatus name for the new enum (forced by the task's
explicit enum name); the renamed DTO has no public-API impact at
the JSON wire level. Stale doc references updated.

AC RT2 in _docs/00_problem/acceptance_criteria.md now lists all 4
point types (start/end/action/intermediate).

Tests: 171 / 171 unit + 5 / 5 smoke green (was 141 + 5; +30 new tests
covering type handler round-trip, set/parse, unknown-value rejection,
idempotent registration, and the AC RT2 doc check).

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-11 03:55:22 +03:00

104 lines
3.9 KiB
C#

using FluentAssertions;
using SatelliteProvider.Common.DTO;
using SatelliteProvider.Common.Enums;
using SatelliteProvider.DataAccess.Models;
using SatelliteProvider.Services.RouteManagement;
namespace SatelliteProvider.Tests;
public class RouteResponseMapperTests
{
private static RouteEntity BuildEntity(Guid id) => new()
{
Id = id,
Name = "demo route",
Description = "desc",
RegionSizeMeters = 500,
ZoomLevel = 18,
TotalDistanceMeters = 1234.56,
TotalPoints = 4,
RequestMaps = true,
MapsReady = false,
CsvFilePath = "/ready/route.csv",
SummaryFilePath = "/ready/route.txt",
StitchedImagePath = "/ready/route.jpg",
TilesZipPath = "/ready/route.zip",
CreatedAt = new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc),
UpdatedAt = new DateTime(2026, 1, 1, 0, 5, 0, DateTimeKind.Utc),
};
[Fact]
public void Map_FromDtoPoints_CopiesAllEntityFields()
{
var id = Guid.NewGuid();
var entity = BuildEntity(id);
var dtos = new List<RoutePointDto>
{
new() { Latitude = 1, Longitude = 2, PointType = RoutePointType.Start, SequenceNumber = 0, SegmentIndex = 0 },
new() { Latitude = 3, Longitude = 4, PointType = RoutePointType.End, SequenceNumber = 1, SegmentIndex = 1, DistanceFromPrevious = 100.0 },
};
var sut = new RouteResponseMapper();
var response = sut.Map(entity, dtos);
response.Id.Should().Be(id);
response.Name.Should().Be(entity.Name);
response.Description.Should().Be(entity.Description);
response.RegionSizeMeters.Should().Be(entity.RegionSizeMeters);
response.ZoomLevel.Should().Be(entity.ZoomLevel);
response.TotalDistanceMeters.Should().Be(entity.TotalDistanceMeters);
response.TotalPoints.Should().Be(entity.TotalPoints);
response.RequestMaps.Should().Be(entity.RequestMaps);
response.MapsReady.Should().Be(entity.MapsReady);
response.CsvFilePath.Should().Be(entity.CsvFilePath);
response.SummaryFilePath.Should().Be(entity.SummaryFilePath);
response.StitchedImagePath.Should().Be(entity.StitchedImagePath);
response.TilesZipPath.Should().Be(entity.TilesZipPath);
response.CreatedAt.Should().Be(entity.CreatedAt);
response.UpdatedAt.Should().Be(entity.UpdatedAt);
response.Points.Should().BeEquivalentTo(dtos);
}
[Fact]
public void Map_FromEntityPoints_ProjectsToDtosWithSameFields()
{
var id = Guid.NewGuid();
var entity = BuildEntity(id);
var pointEntities = new List<RoutePointEntity>
{
new() { Id = Guid.NewGuid(), RouteId = id, SequenceNumber = 0, Latitude = 1, Longitude = 2, PointType = RoutePointType.Start, SegmentIndex = 0 },
new() { Id = Guid.NewGuid(), RouteId = id, SequenceNumber = 1, Latitude = 3, Longitude = 4, PointType = RoutePointType.End, SegmentIndex = 1, DistanceFromPrevious = 100.0 },
};
var sut = new RouteResponseMapper();
var response = sut.Map(entity, pointEntities);
response.Points.Should().HaveCount(2);
response.Points[0].PointType.Should().Be(RoutePointType.Start);
response.Points[0].Latitude.Should().Be(1);
response.Points[1].PointType.Should().Be(RoutePointType.End);
response.Points[1].DistanceFromPrevious.Should().Be(100.0);
}
[Fact]
public void Map_NullEntity_Throws()
{
var sut = new RouteResponseMapper();
Action act = () => sut.Map(null!, new List<RoutePointDto>());
act.Should().Throw<ArgumentNullException>();
}
[Fact]
public void Map_NullPoints_Throws()
{
var sut = new RouteResponseMapper();
var entity = BuildEntity(Guid.NewGuid());
Action act = () => sut.Map(entity, (IEnumerable<RoutePointDto>)null!);
act.Should().Throw<ArgumentNullException>();
}
}