mirror of
https://github.com/azaion/satellite-provider.git
synced 2026-06-21 20:11:17 +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>
109 lines
3.3 KiB
C#
109 lines
3.3 KiB
C#
using FluentAssertions;
|
|
using SatelliteProvider.Api;
|
|
|
|
namespace SatelliteProvider.Tests;
|
|
|
|
public class CorsConfigurationValidatorTests
|
|
{
|
|
[Fact]
|
|
public void EnsureSafeForEnvironment_ProductionWithEmptyOriginsAndNoOptIn_Throws_AC1()
|
|
{
|
|
// Arrange
|
|
var allowedOrigins = Array.Empty<string>();
|
|
|
|
// Act
|
|
Action act = () => CorsConfigurationValidator.EnsureSafeForEnvironment(
|
|
allowedOrigins, allowAnyOrigin: false, environmentName: "Production");
|
|
|
|
// Assert
|
|
act.Should().Throw<InvalidOperationException>()
|
|
.WithMessage("*CorsConfig:AllowedOrigins*")
|
|
.WithMessage("*CorsConfig:AllowAnyOrigin*");
|
|
}
|
|
|
|
[Theory]
|
|
[InlineData("Development")]
|
|
[InlineData("Staging")]
|
|
[InlineData("Local")]
|
|
public void EnsureSafeForEnvironment_NonProductionWithEmptyOrigins_DoesNotThrow_AC2(string environmentName)
|
|
{
|
|
// Arrange
|
|
var allowedOrigins = Array.Empty<string>();
|
|
|
|
// Act
|
|
Action act = () => CorsConfigurationValidator.EnsureSafeForEnvironment(
|
|
allowedOrigins, allowAnyOrigin: false, environmentName);
|
|
|
|
// Assert
|
|
act.Should().NotThrow();
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureSafeForEnvironment_ProductionWithExplicitAllowAnyOrigin_DoesNotThrow_AC3()
|
|
{
|
|
// Arrange
|
|
var allowedOrigins = Array.Empty<string>();
|
|
|
|
// Act
|
|
Action act = () => CorsConfigurationValidator.EnsureSafeForEnvironment(
|
|
allowedOrigins, allowAnyOrigin: true, environmentName: "Production");
|
|
|
|
// Assert
|
|
act.Should().NotThrow();
|
|
}
|
|
|
|
[Fact]
|
|
public void EnsureSafeForEnvironment_ProductionWithNonEmptyOrigins_DoesNotThrow()
|
|
{
|
|
// Arrange
|
|
var allowedOrigins = new[] { "https://example.com" };
|
|
|
|
// Act
|
|
Action act = () => CorsConfigurationValidator.EnsureSafeForEnvironment(
|
|
allowedOrigins, allowAnyOrigin: false, environmentName: "Production");
|
|
|
|
// Assert
|
|
act.Should().NotThrow();
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldUsePermissivePolicy_NonEmptyOriginsAndNoOptIn_ReturnsFalse()
|
|
{
|
|
// Assert
|
|
CorsConfigurationValidator.ShouldUsePermissivePolicy(
|
|
new[] { "https://example.com" }, allowAnyOrigin: false).Should().BeFalse();
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldUsePermissivePolicy_EmptyOriginsAndNoOptIn_ReturnsTrue()
|
|
{
|
|
// Assert
|
|
CorsConfigurationValidator.ShouldUsePermissivePolicy(
|
|
Array.Empty<string>(), allowAnyOrigin: false).Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldUsePermissivePolicy_ExplicitOptIn_ReturnsTrueRegardlessOfOrigins()
|
|
{
|
|
// Assert
|
|
CorsConfigurationValidator.ShouldUsePermissivePolicy(
|
|
new[] { "https://example.com" }, allowAnyOrigin: true).Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldWarnAboutPermissiveDefault_EmptyOriginsAndNoOptIn_ReturnsTrue()
|
|
{
|
|
// Assert
|
|
CorsConfigurationValidator.ShouldWarnAboutPermissiveDefault(
|
|
Array.Empty<string>(), allowAnyOrigin: false).Should().BeTrue();
|
|
}
|
|
|
|
[Fact]
|
|
public void ShouldWarnAboutPermissiveDefault_ExplicitOptIn_ReturnsFalse()
|
|
{
|
|
// Assert
|
|
CorsConfigurationValidator.ShouldWarnAboutPermissiveDefault(
|
|
Array.Empty<string>(), allowAnyOrigin: true).Should().BeFalse();
|
|
}
|
|
}
|