mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-21 18:11:14 +00:00
1d89cd9997
AZ-353: Centralize 500 handling via GlobalExceptionHandler / AddProblemDetails / UseExceptionHandler. Sanitized ProblemDetails body carries a generic title, RFC9110 type link, and the request's TraceIdentifier as correlationId; the leaky exception message stays server-side in the ERR log entry. Strip per-endpoint try/catch (Exception) wrappers and the unused ILogger<Program> parameters they served. Preserve the typed ArgumentException catch in CreateRoute (AC-3). The handler maps BadHttpRequestException back to its framework-supplied StatusCode so model-binding / malformed-body failures stay 4xx instead of being promoted to 500. AZ-354: Extract CorsConfigurationValidator (pure static helpers) and wire it into Program.cs. Production with empty CorsConfig:AllowedOrigins and no CorsConfig:AllowAnyOrigin opt-in now throws InvalidOperationException at host startup. Development keeps the permissive default but logs a warning post-build. Adds the explicit CorsConfig:AllowAnyOrigin escape hatch. AZ-356: GetSatelliteTilesByMgrs and UploadImage now return Results.Problem(StatusCode 501) with ProblemDetails. Added .ProducesProblem(501) so swagger.json documents the not-implemented status. Tests: SatelliteProvider.Tests now references SatelliteProvider.Api (downward, idiomatic) so unit tests can reach the new helpers. +9 CorsConfigurationValidator unit tests, +3 GlobalExceptionHandler unit tests, +3 StubAndErrorContractTests integration tests (added to smoke + full suites). 58/58 unit + 5/5 smoke + 3/3 stub-contract pass. Code review verdict: PASS. Batch report: _docs/03_implementation/batch_08_report.md. Co-authored-by: Cursor <cursoragent@cursor.com>
90 lines
3.6 KiB
C#
90 lines
3.6 KiB
C#
using System.Net;
|
|
using System.Text;
|
|
|
|
namespace SatelliteProvider.IntegrationTests;
|
|
|
|
public static class StubAndErrorContractTests
|
|
{
|
|
public static async Task RunAll(HttpClient httpClient)
|
|
{
|
|
RouteTestHelpers.PrintTestHeader("Test: Stub endpoints + error contracts (AZ-356 / AZ-353)");
|
|
|
|
await StubMgrs_Returns501(httpClient);
|
|
await StubUpload_Returns501(httpClient);
|
|
await CreateRoute_InvalidPayload_Returns400_AZ353_AC3(httpClient);
|
|
|
|
Console.WriteLine("✓ Stub + error-contract tests: PASSED");
|
|
}
|
|
|
|
private static async Task StubMgrs_Returns501(HttpClient httpClient)
|
|
{
|
|
Console.WriteLine();
|
|
Console.WriteLine("AZ-356 AC-1: GET /api/satellite/tiles/mgrs returns 501");
|
|
|
|
var response = await httpClient.GetAsync("/api/satellite/tiles/mgrs?mgrs=33TWN1234567890&squareSideMeters=100");
|
|
var status = (int)response.StatusCode;
|
|
|
|
if (status != 501)
|
|
{
|
|
throw new Exception($"Expected 501 from /api/satellite/tiles/mgrs, got {status}");
|
|
}
|
|
|
|
var body = await response.Content.ReadAsStringAsync();
|
|
if (!body.Contains("Not implemented", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
throw new Exception($"Expected ProblemDetails body containing 'Not implemented', got: {body}");
|
|
}
|
|
|
|
Console.WriteLine($" ✓ /api/satellite/tiles/mgrs returns HTTP 501 with ProblemDetails");
|
|
}
|
|
|
|
private static async Task StubUpload_Returns501(HttpClient httpClient)
|
|
{
|
|
Console.WriteLine();
|
|
Console.WriteLine("AZ-356 AC-1: POST /api/satellite/upload returns 501");
|
|
|
|
using var multipart = new MultipartFormDataContent
|
|
{
|
|
{ new StringContent(DateTime.UtcNow.ToString("o")), "Timestamp" },
|
|
{ new StringContent("47.461747"), "Lat" },
|
|
{ new StringContent("37.647063"), "Lon" },
|
|
{ new StringContent("100"), "Height" },
|
|
{ new StringContent("35"), "FocalLength" },
|
|
{ new StringContent("23"), "SensorWidth" },
|
|
{ new StringContent("15.6"), "SensorHeight" },
|
|
};
|
|
var fakeImage = new ByteArrayContent(new byte[] { 0xFF, 0xD8, 0xFF, 0xD9 });
|
|
fakeImage.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("image/jpeg");
|
|
multipart.Add(fakeImage, "Image", "test.jpg");
|
|
|
|
var response = await httpClient.PostAsync("/api/satellite/upload", multipart);
|
|
var status = (int)response.StatusCode;
|
|
|
|
if (status != 501)
|
|
{
|
|
throw new Exception($"Expected 501 from /api/satellite/upload, got {status}");
|
|
}
|
|
|
|
Console.WriteLine($" ✓ /api/satellite/upload returns HTTP 501");
|
|
}
|
|
|
|
private static async Task CreateRoute_InvalidPayload_Returns400_AZ353_AC3(HttpClient httpClient)
|
|
{
|
|
Console.WriteLine();
|
|
Console.WriteLine("AZ-353 AC-3: POST /api/satellite/route with <2 points returns 400 (typed ArgumentException path preserved)");
|
|
|
|
var routeId = Guid.NewGuid();
|
|
var body = $"{{\"id\":\"{routeId}\",\"name\":\"too-short\",\"description\":\"\",\"regionSizeMeters\":500,\"zoomLevel\":18,\"requestMaps\":false,\"points\":[{{\"latitude\":47.46,\"longitude\":37.64}}]}}";
|
|
var content = new StringContent(body, Encoding.UTF8, "application/json");
|
|
var response = await httpClient.PostAsync("/api/satellite/route", content);
|
|
var status = (int)response.StatusCode;
|
|
|
|
if (status != 400)
|
|
{
|
|
throw new Exception($"Expected 400 for 1-point route (typed ArgumentException), got {status}");
|
|
}
|
|
|
|
Console.WriteLine($" ✓ 1-point route rejected with HTTP 400 (typed handling preserved)");
|
|
}
|
|
}
|