shoc-backend/Api.SeaHavenIndustries.Tests/SentryPipelineContractTests.cs
Alexandre Brandizzi 6ceb274bfb
Some checks failed
Validate and deploy / Validate deployable source bundle (push) Has been cancelled
Validate and deploy / Deploy shoc-backend-dev through Terraform (push) Has been cancelled
Validate and deploy / Deploy shoc-backend-staging to Elastic Beanstalk (push) Has been cancelled
feat: add API Sentry tracing (SH-298) (#105)
* feat: add API Sentry tracing

* feat: activate Sentry deployment environments

* fix: allow Sentry-free design-time tooling

* fix: trace background jobs in Sentry

* feat(observability): identify and scrub Sentry transactions

* fix(observability): finish abandoned transactions
2026-09-04 14:11:32 -03:00

72 lines
3.7 KiB
C#

using FluentAssertions;
using Xunit;
namespace Api.SeaHavenIndustries.Tests;
public class SentryPipelineContractTests
{
private static string RepoRoot()
{
var directory = new DirectoryInfo(AppContext.BaseDirectory);
while (directory is not null
&& !File.Exists(Path.Combine(directory.FullName, "Api.SeaHavenIndustries", "Program.cs")))
{
directory = directory.Parent;
}
if (directory is null)
throw new InvalidOperationException("Could not locate the repository root from " + AppContext.BaseDirectory);
return directory.FullName;
}
[Fact]
public void Program_MiddlewareOrder_IsRouting_Authentication_SentryMetadata_Authorization()
{
var program = File.ReadAllText(Path.Combine(RepoRoot(), "Api.SeaHavenIndustries", "Program.cs"));
var routing = program.IndexOf("app.UseRouting()", StringComparison.Ordinal);
var authentication = program.IndexOf("app.UseAuthentication()", StringComparison.Ordinal);
var sentry = program.IndexOf("app.UseMiddleware<SentryRequestMetadataMiddleware>()", StringComparison.Ordinal);
var authorization = program.IndexOf("app.UseAuthorization()", StringComparison.Ordinal);
routing.Should().BeGreaterThan(-1, "UseRouting must be explicit");
authentication.Should().BeGreaterThan(-1);
sentry.Should().BeGreaterThan(-1);
authorization.Should().BeGreaterThan(-1);
routing.Should().BeLessThan(authentication, "routing must precede authentication");
authentication.Should().BeLessThan(sentry, "Sentry request metadata must be applied after authentication");
sentry.Should().BeLessThan(authorization, "Sentry request metadata must be applied before authorization");
}
[Fact]
public void Program_ConfiguresReleaseAndDefaultTags()
{
var program = File.ReadAllText(Path.Combine(RepoRoot(), "Api.SeaHavenIndustries", "Program.cs"));
program.Should().Contain("SentryObservability.ResolveRelease");
program.Should().Contain("options.Release = release");
program.Should().Contain("\"service\", SentryObservability.ServiceName");
program.Should().Contain("\"app.commit\"");
program.Should().Contain("MaxRequestBodySize = RequestSize.None");
program.Should().Contain("SetBeforeSend(SentryTelemetryScrubber.Scrub)");
program.Should().Contain("SetBeforeSendTransaction(SentryTelemetryScrubber.Scrub)");
}
[Theory]
[InlineData("Api.SeaHavenIndustries/Helper/VendorDocumentScanWorker.cs", "vendor-documents.scan-pending")]
[InlineData("Api.SeaHavenIndustries/HostedServices/WorkOrderReconciliationHostedService.cs", "workorders.reconciliation-trigger")]
[InlineData("Api.SeaHavenIndustries/HostedServices/WorkOrderReconciliationHostedService.cs", "workorders.reconciliation-run-pending")]
[InlineData("Api.SeaHavenIndustries/HostedServices/WorkOrderWeekRolledHostedService.cs", "workorders.week-rolled-job")]
[InlineData("Api.SeaHavenIndustries/HostedServices/PastDueCacheHostedService.cs", "workorders.past-due-cache-job")]
[InlineData("Api.SeaHavenIndustries/HostedServices/UpliftLifecycleHostedService.cs", "uplifts.lifecycle-sweep")]
public void Workers_UseSharedBackgroundTransactionHelper_WithStableNames(string relativePath, string transactionName)
{
var source = File.ReadAllText(Path.Combine(RepoRoot(), relativePath));
source.Should().Contain($"BeginBackgroundTransaction", "worker transactions must go through the shared helper");
source.Should().Contain($"\"{transactionName}\"");
source.Should().NotContain("PushScope", "scope pushing is owned by the shared helper");
}
}