From be0b6164058b96750062cdd12265f6b167cc7c41 Mon Sep 17 00:00:00 2001 From: Martin Othamar Date: Tue, 28 May 2024 12:21:33 +0200 Subject: [PATCH] Tests for telemetry enriching middleware, make ASP.NET Core spans root, misc improvements (#659) --- AppLibDotnet.sln | 7 + .../Extensions/ServiceCollectionExtensions.cs | 98 ++++++++++++- .../TelemetryEnrichingMiddleware.cs | 19 +-- .../Configuration/AppSettings.cs | 6 + src/Altinn.App.Core/Features/Telemetry.cs | 18 ++- .../Features/TelemetryActivityExtensions.cs | 33 ++++- .../Altinn.App.Api.Tests.csproj | 1 + .../CustomWebApplicationFactory.cs | 71 +++++++--- ...Should_Always_Be_A_Root_Trace.verified.txt | 48 +++++++ ...ave_Root_AspNetCore_Trace_Org.verified.txt | 45 ++++++ ...ve_Root_AspNetCore_Trace_User.verified.txt | 48 +++++++ .../TelemetryEnrichingMiddlewareTests.cs | 112 +++++++++++++++ .../Mocks/PepWithPDPAuthorizationMockSI.cs | 2 +- test/Altinn.App.Api.Tests/Program.cs | 4 + .../Utils/PrincipalUtil.cs | 6 +- .../Altinn.App.Common.Tests.csproj | 43 ++++++ .../TelemetryDITests.cs | 38 +++++ .../TelemetrySink.cs | 131 +++++++++--------- .../Altinn.App.Core.Tests.csproj | 1 + .../Email/EmailNotificationClientTests.cs | 2 +- .../Sms/SmsNotificationClientTests.cs | 2 +- .../Implementation/AppResourcesSITests.cs | 2 +- .../Implementation/EventsClientTest.cs | 3 +- ...ation_SuccessfulCallToStorage.verified.txt | 2 +- .../Implementation/InstanceClientTests.cs | 2 +- .../Authorization/AuthorizationClientTests.cs | 2 +- .../Clients/Storage/DataClientTests.cs | 2 +- .../Internal/App/AppMedataTest.cs | 2 +- .../Auth/AuthorizationServiceTests.cs | 2 +- .../Internal/Patch/PatchServiceTests.cs | 2 +- .../Internal/Pdf/PdfServiceTests.cs | 4 +- .../Internal/Process/ProcessEngineTest.cs | 2 +- .../Internal/Process/ProcessReaderTests.cs | 4 +- .../Process/TestUtils/ProcessTestUtils.cs | 3 +- 34 files changed, 645 insertions(+), 122 deletions(-) create mode 100644 test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.Should_Always_Be_A_Root_Trace.verified.txt create mode 100644 test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.Should_Have_Root_AspNetCore_Trace_Org.verified.txt create mode 100644 test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.Should_Have_Root_AspNetCore_Trace_User.verified.txt create mode 100644 test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.cs create mode 100644 test/Altinn.App.Common.Tests/Altinn.App.Common.Tests.csproj create mode 100644 test/Altinn.App.Common.Tests/TelemetryDITests.cs rename test/{Altinn.App.Core.Tests/Mocks => Altinn.App.Common.Tests}/TelemetrySink.cs (58%) diff --git a/AppLibDotnet.sln b/AppLibDotnet.sln index 826edaf71..9330287d6 100644 --- a/AppLibDotnet.sln +++ b/AppLibDotnet.sln @@ -18,6 +18,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7AD5FADE-607 EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Altinn.App.Core", "src\Altinn.App.Core\Altinn.App.Core.csproj", "{1745B251-BD5C-43B7-BA7D-9C4BFAB37535}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Altinn.App.Common.Tests", "test\Altinn.App.Common.Tests\Altinn.App.Common.Tests.csproj", "{D5838692-2703-4E42-8802-6E1FA7F1B50B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -40,6 +42,10 @@ Global {1745B251-BD5C-43B7-BA7D-9C4BFAB37535}.Debug|Any CPU.Build.0 = Debug|Any CPU {1745B251-BD5C-43B7-BA7D-9C4BFAB37535}.Release|Any CPU.ActiveCfg = Release|Any CPU {1745B251-BD5C-43B7-BA7D-9C4BFAB37535}.Release|Any CPU.Build.0 = Release|Any CPU + {D5838692-2703-4E42-8802-6E1FA7F1B50B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D5838692-2703-4E42-8802-6E1FA7F1B50B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D5838692-2703-4E42-8802-6E1FA7F1B50B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D5838692-2703-4E42-8802-6E1FA7F1B50B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -49,6 +55,7 @@ Global {17D7DCE9-7797-4BC1-B448-D0529FD6FB3D} = {6C8EB054-1747-4BAC-A637-754F304BCAFA} {2FD56505-1DB2-4AE1-8911-E076E535EAC6} = {6C8EB054-1747-4BAC-A637-754F304BCAFA} {1745B251-BD5C-43B7-BA7D-9C4BFAB37535} = {7AD5FADE-607F-4D5F-8511-6647D0C1AA1C} + {D5838692-2703-4E42-8802-6E1FA7F1B50B} = {6C8EB054-1747-4BAC-A637-754F304BCAFA} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4584C6E1-D5B4-40B1-A8C4-CF4620EB0896} diff --git a/src/Altinn.App.Api/Extensions/ServiceCollectionExtensions.cs b/src/Altinn.App.Api/Extensions/ServiceCollectionExtensions.cs index dde2c8e0f..ba6f55b53 100644 --- a/src/Altinn.App.Api/Extensions/ServiceCollectionExtensions.cs +++ b/src/Altinn.App.Api/Extensions/ServiceCollectionExtensions.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using Altinn.App.Api.Controllers; using Altinn.App.Api.Helpers; using Altinn.App.Api.Infrastructure.Filters; @@ -15,6 +16,8 @@ using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.FeatureManagement; using Microsoft.IdentityModel.Tokens; +using OpenTelemetry; +using OpenTelemetry.Context.Propagation; using OpenTelemetry.Logs; using OpenTelemetry.Metrics; using OpenTelemetry.Resources; @@ -148,6 +151,14 @@ IWebHostEnvironment env services.AddHostedService(); services.AddSingleton(); + // This bit of code makes ASP.NET Core spans always root. + // Depending on infrastructure used and how the application is exposed/called, + // it might be a good idea to be in control of the root span (and therefore the size, baggage etch) + // Taken from: https://github.com/open-telemetry/opentelemetry-dotnet-contrib/issues/1773 + _ = Sdk.SuppressInstrumentation; // Just to trigger static constructor. The static constructor in Sdk initializes Propagators.DefaultTextMapPropagator which we depend on below + Sdk.SetDefaultTextMapPropagator(new OtelPropagator(Propagators.DefaultTextMapPropagator)); + DistributedContextPropagator.Current = new AspNetCorePropagator(); + var appInsightsConnectionString = GetAppInsightsConnectionStringForOtel(config, env); services @@ -232,7 +243,7 @@ private sealed class TelemetryInitialization( MeterProvider meterProvider ) : IHostedService { - public Task StartAsync(CancellationToken cancellationToken) + public async Task StartAsync(CancellationToken cancellationToken) { // This codepath for initialization is here only because it makes it a lot easier to // query the metrics from Prometheus using 'increase' without the appearance of a "missed" sample. @@ -244,21 +255,100 @@ public Task StartAsync(CancellationToken cancellationToken) telemetry.Init(); try { - if (!meterProvider.ForceFlush(10_000)) + var task = Task.Factory.StartNew( + () => + { + if (!meterProvider.ForceFlush(10_000)) + { + logger.LogInformation("Failed to flush metrics after 10 seconds"); + } + }, + cancellationToken, + // Long running to avoid doing this blocking on a "normal" thread pool thread + TaskCreationOptions.LongRunning | TaskCreationOptions.DenyChildAttach, + TaskScheduler.Default + ); + if (await Task.WhenAny(task, Task.Delay(500, cancellationToken)) != task) { - logger.LogWarning("Failed to flush metrics after 10 seconds"); + logger.LogInformation( + "Tried to flush metrics within 0.5 seconds but it was taking too long, proceeding with startup" + ); } } catch (Exception ex) { + if (ex is OperationCanceledException) + return; logger.LogWarning(ex, "Failed to flush metrics"); } - return Task.CompletedTask; } public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } + internal sealed class OtelPropagator : TextMapPropagator + { + private readonly TextMapPropagator _inner; + + public OtelPropagator(TextMapPropagator inner) => _inner = inner; + + public override ISet Fields => _inner.Fields; + + public override PropagationContext Extract( + PropagationContext context, + T carrier, + Func> getter + ) + { + if (carrier is HttpRequest) + return default; + return _inner.Extract(context, carrier, getter); + } + + public override void Inject(PropagationContext context, T carrier, Action setter) => + _inner.Inject(context, carrier, setter); + } + + internal sealed class AspNetCorePropagator : DistributedContextPropagator + { + private readonly DistributedContextPropagator _inner; + + public AspNetCorePropagator() => _inner = CreateDefaultPropagator(); + + public override IReadOnlyCollection Fields => _inner.Fields; + + public override IEnumerable>? ExtractBaggage( + object? carrier, + PropagatorGetterCallback? getter + ) + { + if (carrier is IHeaderDictionary) + return null; + + return _inner.ExtractBaggage(carrier, getter); + } + + public override void ExtractTraceIdAndState( + object? carrier, + PropagatorGetterCallback? getter, + out string? traceId, + out string? traceState + ) + { + if (carrier is IHeaderDictionary) + { + traceId = null; + traceState = null; + return; + } + + _inner.ExtractTraceIdAndState(carrier, getter, out traceId, out traceState); + } + + public override void Inject(Activity? activity, object? carrier, PropagatorSetterCallback? setter) => + _inner.Inject(activity, carrier, setter); + } + private static void AddAuthorizationPolicies(IServiceCollection services) { services.AddAuthorization(options => diff --git a/src/Altinn.App.Api/Infrastructure/Middleware/TelemetryEnrichingMiddleware.cs b/src/Altinn.App.Api/Infrastructure/Middleware/TelemetryEnrichingMiddleware.cs index f5641ff95..01edcfb01 100644 --- a/src/Altinn.App.Api/Infrastructure/Middleware/TelemetryEnrichingMiddleware.cs +++ b/src/Altinn.App.Api/Infrastructure/Middleware/TelemetryEnrichingMiddleware.cs @@ -7,10 +7,7 @@ namespace Altinn.App.Api.Infrastructure.Middleware; -/// -/// Middleware for adding telemetry to the request. -/// -public class TelemetryEnrichingMiddleware +internal sealed class TelemetryEnrichingMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; @@ -41,6 +38,10 @@ static TelemetryEnrichingMiddleware() } } }, + { + AltinnCoreClaimTypes.AuthenticateMethod, + static (claim, activity) => activity.SetAuthenticationMethod(claim.Value) + }, { AltinnCoreClaimTypes.AuthenticationLevel, static (claim, activity) => @@ -50,7 +51,9 @@ static TelemetryEnrichingMiddleware() activity.SetAuthenticationLevel(result); } } - } + }, + { AltinnCoreClaimTypes.Org, static (claim, activity) => activity.SetOrganisationName(claim.Value) }, + { AltinnCoreClaimTypes.OrgNumber, static (claim, activity) => activity.SetOrganisationNumber(claim.Value) }, }; ClaimActions = actions.ToFrozenDictionary(); @@ -73,8 +76,8 @@ public TelemetryEnrichingMiddleware(RequestDelegate next, ILoggerThe HTTP context. public async Task InvokeAsync(HttpContext context) { - var trace = context.Features.Get(); - if (trace is null) + var activity = context.Features.Get()?.Activity; + if (activity is null) { await _next(context); return; @@ -82,8 +85,6 @@ public async Task InvokeAsync(HttpContext context) try { - var activity = trace.Activity; - foreach (var claim in context.User.Claims) { if (ClaimActions.TryGetValue(claim.Type, out var action)) diff --git a/src/Altinn.App.Core/Configuration/AppSettings.cs b/src/Altinn.App.Core/Configuration/AppSettings.cs index 4ada89a59..5f98b5783 100644 --- a/src/Altinn.App.Core/Configuration/AppSettings.cs +++ b/src/Altinn.App.Core/Configuration/AppSettings.cs @@ -215,5 +215,11 @@ public class AppSettings /// Enable the functionality to run expression validation in backend /// public bool ExpressionValidation { get; set; } = false; + + /// + /// Enables OpenTelemetry as a substitute for Application Insights SDK + /// Improves instrumentation throughout the Altinn app libraries. + /// + public bool UseOpenTelemetry { get; set; } } } diff --git a/src/Altinn.App.Core/Features/Telemetry.cs b/src/Altinn.App.Core/Features/Telemetry.cs index 110a01cb6..1bbe6c2d0 100644 --- a/src/Altinn.App.Core/Features/Telemetry.cs +++ b/src/Altinn.App.Core/Features/Telemetry.cs @@ -117,7 +117,7 @@ public static class Labels /// /// Label for the party ID of the instance owner. /// - public static readonly string InstanceOwnerPartyId = "instance.owner_party_id"; + public static readonly string InstanceOwnerPartyId = "instance.owner.party.id"; /// /// Label for the guid that identifies the instance. @@ -147,12 +147,22 @@ public static class Labels /// /// Label for the ID of the party. /// - public const string UserPartyId = "user.party_id"; + public const string UserPartyId = "user.party.id"; + + /// + /// Label for the authentication method of the user. + /// + public const string UserAuthenticationMethod = "user.authentication.method"; /// /// Label for the authentication level of the user. /// - public const string UserAuthenticationLevel = "user.authentication_level"; + public const string UserAuthenticationLevel = "user.authentication.level"; + + /// + /// Label for the organisation name. + /// + public const string OrganisationName = "organisation.name"; /// /// Label for the organisation number. @@ -166,7 +176,7 @@ internal static class InternalLabels internal const string Type = "type"; internal const string AuthorizationAction = "authorization.action"; internal const string AuthorizerAction = "authorization.authorizer.action"; - internal const string AuthorizerTaskId = "authorization.authorizer.task_id"; + internal const string AuthorizerTaskId = "authorization.authorizer.task.id"; internal const string ValidatorType = "validator.type"; internal const string ValidatorSource = "validator.source"; } diff --git a/src/Altinn.App.Core/Features/TelemetryActivityExtensions.cs b/src/Altinn.App.Core/Features/TelemetryActivityExtensions.cs index ad8079ea9..50220fe91 100644 --- a/src/Altinn.App.Core/Features/TelemetryActivityExtensions.cs +++ b/src/Altinn.App.Core/Features/TelemetryActivityExtensions.cs @@ -55,6 +55,21 @@ public static Activity SetUsername(this Activity activity, string? username) return activity; } + /// + /// Sets the user authentication method as a tag/attribute on the activity/span + /// + /// Activity + /// Authentication method + /// Activity + public static Activity SetAuthenticationMethod(this Activity activity, string? authenticationMethod) + { + if (!string.IsNullOrWhiteSpace(authenticationMethod)) + { + activity.SetTag(Labels.UserAuthenticationMethod, authenticationMethod); + } + return activity; + } + /// /// Sets the user authentication level as a tag/attribute on the activity/span /// @@ -217,7 +232,23 @@ public static Activity SetTaskId(this Activity activity, string? taskId) } /// - /// Sets the Process Task ID as a tag/attribute on the activity/span + /// Sets the Organisation name as a tag/attribute on the activity/span + /// + /// Activity + /// Organisation name + /// Activity + public static Activity SetOrganisationName(this Activity activity, string? organisationName) + { + if (!string.IsNullOrWhiteSpace(organisationName)) + { + activity.SetTag(Labels.OrganisationName, organisationName); + } + + return activity; + } + + /// + /// Sets the Organisation number as a tag/attribute on the activity/span /// /// Activity /// Organisation number diff --git a/test/Altinn.App.Api.Tests/Altinn.App.Api.Tests.csproj b/test/Altinn.App.Api.Tests/Altinn.App.Api.Tests.csproj index d472686d2..f66ebf7c9 100644 --- a/test/Altinn.App.Api.Tests/Altinn.App.Api.Tests.csproj +++ b/test/Altinn.App.Api.Tests/Altinn.App.Api.Tests.csproj @@ -28,6 +28,7 @@ + diff --git a/test/Altinn.App.Api.Tests/CustomWebApplicationFactory.cs b/test/Altinn.App.Api.Tests/CustomWebApplicationFactory.cs index 5c8c64def..b1435f869 100644 --- a/test/Altinn.App.Api.Tests/CustomWebApplicationFactory.cs +++ b/test/Altinn.App.Api.Tests/CustomWebApplicationFactory.cs @@ -1,3 +1,4 @@ +using System.Diagnostics; using System.Net; using System.Net.Http.Headers; using System.Text.Json; @@ -12,6 +13,8 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; using Moq; +using OpenTelemetry; +using OpenTelemetry.Context.Propagation; using Xunit.Abstractions; namespace Altinn.App.Api.Tests; @@ -27,9 +30,12 @@ public class ApiTestBase protected readonly ITestOutputHelper _outputHelper; private readonly WebApplicationFactory _factory; + protected IServiceProvider Services { get; private set; } + public ApiTestBase(WebApplicationFactory factory, ITestOutputHelper outputHelper) { _factory = factory; + Services = _factory.Services; _outputHelper = outputHelper; } @@ -45,38 +51,71 @@ public HttpClient GetRootedClient(string org, string app, int userId, int? party /// Gets a client that adds appsettings from the specified org/app /// test application under TestData/Apps to the service collection. /// - public HttpClient GetRootedClient(string org, string app) + public HttpClient GetRootedClient(string org, string app, bool includeTraceContext = false) { string appRootPath = TestData.GetApplicationDirectory(org, app); string appSettingsPath = Path.Join(appRootPath, "appsettings.json"); - var client = _factory - .WithWebHostBuilder(builder => - { - var configuration = new ConfigurationBuilder().AddJsonFile(appSettingsPath).Build(); + var factory = _factory.WithWebHostBuilder(builder => + { + var configuration = new ConfigurationBuilder().AddJsonFile(appSettingsPath).Build(); - configuration.GetSection("AppSettings:AppBasePath").Value = appRootPath; - IConfigurationSection appSettingSection = configuration.GetSection("AppSettings"); + configuration.GetSection("AppSettings:AppBasePath").Value = appRootPath; + IConfigurationSection appSettingSection = configuration.GetSection("AppSettings"); - builder.ConfigureLogging(ConfigureFakeLogging); + builder.ConfigureLogging(logging => ConfigureFakeLogging(logging, _outputHelper)); - builder.ConfigureServices(services => services.Configure(appSettingSection)); - builder.ConfigureTestServices(services => OverrideServicesForAllTests(services)); - builder.ConfigureTestServices(OverrideServicesForThisTest); - builder.ConfigureTestServices(ConfigureFakeHttpClientHandler); - }) - .CreateClient(new WebApplicationFactoryClientOptions() { AllowAutoRedirect = false }); + builder.ConfigureServices(services => services.Configure(appSettingSection)); + builder.ConfigureTestServices(services => OverrideServicesForAllTests(services)); + builder.ConfigureTestServices(OverrideServicesForThisTest); + builder.ConfigureTestServices(ConfigureFakeHttpClientHandler); + }); + Services = factory.Services; + + var client = includeTraceContext + ? factory.CreateDefaultClient(new DiagnosticHandler()) + : factory.CreateClient(new WebApplicationFactoryClientOptions() { AllowAutoRedirect = false }); return client; } - private void ConfigureFakeLogging(ILoggingBuilder builder) + private sealed class DiagnosticHandler : DelegatingHandler + { + protected override Task SendAsync( + HttpRequestMessage request, + CancellationToken cancellationToken + ) + { + var activity = Activity.Current; + var propagator = Propagators.DefaultTextMapPropagator; + if (activity is not null) + { + propagator.Inject( + new PropagationContext(activity.Context, Baggage.Current), + request.Headers, + (c, k, v) => c.TryAddWithoutValidation(k, v) + ); + } + return base.SendAsync(request, cancellationToken); + } + } + + public static void ConfigureFakeLogging(ILoggingBuilder builder, ITestOutputHelper? outputHelper = null) { builder .ClearProviders() .AddFakeLogging(options => { - options.OutputSink = (message) => _outputHelper.WriteLine(message); + options.OutputSink = message => outputHelper?.WriteLine(message); + if (outputHelper is null) + { + options.FilteredLevels = new HashSet + { + LogLevel.Warning, + LogLevel.Error, + LogLevel.Critical + }; + } options.OutputFormatter = log => $""" [{ShortLogLevel(log.Level)}] {log.Category}: diff --git a/test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.Should_Always_Be_A_Root_Trace.verified.txt b/test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.Should_Always_Be_A_Root_Trace.verified.txt new file mode 100644 index 000000000..6adfaa848 --- /dev/null +++ b/test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.Should_Always_Be_A_Root_Trace.verified.txt @@ -0,0 +1,48 @@ +{ + Activities: [ + { + ActivityName: GET {org}/{app}/api/v1/applicationmetadata, + Tags: [ + { + server.address: localhost + }, + { + http.request.method: GET + }, + { + url.scheme: http + }, + { + url.path: /tdd/contributer-restriction/api/v1/applicationmetadata + }, + { + network.protocol.version: 1.1 + }, + { + user.id: 10 + }, + { + user.party.id: Scrubbed + }, + { + user.name: User10 + }, + { + user.authentication.method: Mock + }, + { + user.authentication.level: 4 + }, + { + http.route: {org}/{app}/api/v1/applicationmetadata + }, + { + http.response.status_code: 200 + } + ], + IdFormat: W3C, + Kind: Server + } + ], + Metrics: [] +} \ No newline at end of file diff --git a/test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.Should_Have_Root_AspNetCore_Trace_Org.verified.txt b/test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.Should_Have_Root_AspNetCore_Trace_Org.verified.txt new file mode 100644 index 000000000..0b45cc20e --- /dev/null +++ b/test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.Should_Have_Root_AspNetCore_Trace_Org.verified.txt @@ -0,0 +1,45 @@ +{ + Activities: [ + { + ActivityName: GET {org}/{app}/api/v1/applicationmetadata, + Tags: [ + { + server.address: localhost + }, + { + http.request.method: GET + }, + { + url.scheme: http + }, + { + url.path: /tdd/contributer-restriction/api/v1/applicationmetadata + }, + { + network.protocol.version: 1.1 + }, + { + organisation.name: Guid_1 + }, + { + organisation.number: 160694123 + }, + { + user.authentication.method: Mock + }, + { + user.authentication.level: 4 + }, + { + http.route: {org}/{app}/api/v1/applicationmetadata + }, + { + http.response.status_code: 200 + } + ], + IdFormat: W3C, + Kind: Server + } + ], + Metrics: [] +} \ No newline at end of file diff --git a/test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.Should_Have_Root_AspNetCore_Trace_User.verified.txt b/test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.Should_Have_Root_AspNetCore_Trace_User.verified.txt new file mode 100644 index 000000000..6adfaa848 --- /dev/null +++ b/test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.Should_Have_Root_AspNetCore_Trace_User.verified.txt @@ -0,0 +1,48 @@ +{ + Activities: [ + { + ActivityName: GET {org}/{app}/api/v1/applicationmetadata, + Tags: [ + { + server.address: localhost + }, + { + http.request.method: GET + }, + { + url.scheme: http + }, + { + url.path: /tdd/contributer-restriction/api/v1/applicationmetadata + }, + { + network.protocol.version: 1.1 + }, + { + user.id: 10 + }, + { + user.party.id: Scrubbed + }, + { + user.name: User10 + }, + { + user.authentication.method: Mock + }, + { + user.authentication.level: 4 + }, + { + http.route: {org}/{app}/api/v1/applicationmetadata + }, + { + http.response.status_code: 200 + } + ], + IdFormat: W3C, + Kind: Server + } + ], + Metrics: [] +} \ No newline at end of file diff --git a/test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.cs b/test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.cs new file mode 100644 index 000000000..db0386145 --- /dev/null +++ b/test/Altinn.App.Api.Tests/Middleware/TelemetryEnrichingMiddlewareTests.cs @@ -0,0 +1,112 @@ +using System.Net.Http.Headers; +using Altinn.App.Api.Tests.Mocks; +using Altinn.App.Api.Tests.Utils; +using Altinn.App.Common.Tests; +using Altinn.App.Core.Features; +using Microsoft.AspNetCore.Mvc.Testing; +using Microsoft.Extensions.DependencyInjection; +using Xunit.Abstractions; + +namespace Altinn.App.Api.Tests.Middleware; + +public class TelemetryEnrichingMiddlewareTests : ApiTestBase, IClassFixture> +{ + public TelemetryEnrichingMiddlewareTests(WebApplicationFactory factory, ITestOutputHelper outputHelper) + : base(factory, outputHelper) { } + + private (TelemetrySink Telemetry, Func Request) AnalyzeTelemetry( + string token, + bool includeTraceContext = false + ) + { + this.OverrideServicesForThisTest = (services) => + { + services.AddTelemetrySink( + shouldAlsoListenToActivities: (_, source) => source.Name == "Microsoft.AspNetCore" + ); + }; + + string org = "tdd"; + string app = "contributer-restriction"; + + HttpClient client = GetRootedClient(org, app, includeTraceContext); + var telemetry = this.Services.GetRequiredService(); + + client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token); + + return (telemetry, async () => await client.GetStringAsync($"/{org}/{app}/api/v1/applicationmetadata")); + } + + [Fact] + public async Task Should_Have_Root_AspNetCore_Trace_Org() + { + var org = Guid.NewGuid().ToString(); + string token = PrincipalUtil.GetOrgToken(org, "160694123", 4); + + var (telemetry, request) = AnalyzeTelemetry(token); + await request(); + var activities = telemetry.CapturedActivities; + var activity = Assert.Single( + activities, + a => a.TagObjects.Any(t => t.Key == Telemetry.Labels.OrganisationName && (t.Value as string) == org) + ); + Assert.True(activity.IsAllDataRequested); + Assert.True(activity.Recorded); + Assert.Equal("Microsoft.AspNetCore", activity.Source.Name); + Assert.Null(activity.Parent); + Assert.Null(activity.ParentId); + Assert.Equal(default, activity.ParentSpanId); + + await Verify(telemetry.GetSnapshot(activity)); + } + + [Fact] + public async Task Should_Have_Root_AspNetCore_Trace_User() + { + var partyId = Random.Shared.Next(); + var principal = PrincipalUtil.GetUserPrincipal(10, partyId, 4); + var token = JwtTokenMock.GenerateToken(principal, new TimeSpan(1, 1, 1)); + + var (telemetry, request) = AnalyzeTelemetry(token); + await request(); + var activities = telemetry.CapturedActivities; + var activity = Assert.Single( + activities, + a => a.TagObjects.Any(t => t.Key == Telemetry.Labels.UserPartyId && (t.Value as int?) == partyId) + ); + Assert.True(activity.IsAllDataRequested); + Assert.True(activity.Recorded); + Assert.Equal("Microsoft.AspNetCore", activity.Source.Name); + Assert.Null(activity.Parent); + Assert.Null(activity.ParentId); + Assert.Equal(default, activity.ParentSpanId); + await Verify(telemetry.GetSnapshot(activity)).ScrubMember(Telemetry.Labels.UserPartyId); + } + + [Fact] + public async Task Should_Always_Be_A_Root_Trace() + { + var partyId = Random.Shared.Next(); + var principal = PrincipalUtil.GetUserPrincipal(10, partyId, 4); + var token = JwtTokenMock.GenerateToken(principal, new TimeSpan(1, 1, 1)); + + var (telemetry, request) = AnalyzeTelemetry(token, includeTraceContext: true); + using (var parentActivity = telemetry.Object.ActivitySource.StartActivity("TestParentActivity")) + { + Assert.NotNull(parentActivity); + await request(); + } + var activities = telemetry.CapturedActivities; + var activity = Assert.Single( + activities, + a => a.TagObjects.Any(t => t.Key == Telemetry.Labels.UserPartyId && (t.Value as int?) == partyId) + ); + Assert.True(activity.IsAllDataRequested); + Assert.True(activity.Recorded); + Assert.Equal("Microsoft.AspNetCore", activity.Source.Name); + Assert.Null(activity.Parent); + Assert.Null(activity.ParentId); + Assert.Equal(default, activity.ParentSpanId); + await Verify(telemetry.GetSnapshot(activity)).ScrubMember(Telemetry.Labels.UserPartyId); + } +} diff --git a/test/Altinn.App.Api.Tests/Mocks/PepWithPDPAuthorizationMockSI.cs b/test/Altinn.App.Api.Tests/Mocks/PepWithPDPAuthorizationMockSI.cs index 57480e308..f5ec05c64 100644 --- a/test/Altinn.App.Api.Tests/Mocks/PepWithPDPAuthorizationMockSI.cs +++ b/test/Altinn.App.Api.Tests/Mocks/PepWithPDPAuthorizationMockSI.cs @@ -17,7 +17,7 @@ namespace Altinn.App.Api.Tests.Mocks { - public class PepWithPDPAuthorizationMockSI : Common.PEP.Interfaces.IPDP + public class PepWithPDPAuthorizationMockSI : Altinn.Common.PEP.Interfaces.IPDP { private readonly IInstanceClient _instanceClient; diff --git a/test/Altinn.App.Api.Tests/Program.cs b/test/Altinn.App.Api.Tests/Program.cs index 9374508cf..a8361a986 100644 --- a/test/Altinn.App.Api.Tests/Program.cs +++ b/test/Altinn.App.Api.Tests/Program.cs @@ -1,5 +1,6 @@ using Altinn.App.Api.Extensions; using Altinn.App.Api.Helpers; +using Altinn.App.Api.Tests; using Altinn.App.Api.Tests.Data; using Altinn.App.Api.Tests.Mocks; using Altinn.App.Api.Tests.Mocks.Authentication; @@ -36,10 +37,13 @@ } ); +ApiTestBase.ConfigureFakeLogging(builder.Logging); + builder.Configuration.AddJsonFile( Path.Join(TestData.GetTestDataRootDirectory(), "apps", "tdd", "contributer-restriction", "appsettings.json") ); builder.Configuration.GetSection("MetricsSettings:Enabled").Value = "false"; +builder.Configuration.GetSection("AppSettings:UseOpenTelemetry").Value = "true"; ConfigureServices(builder.Services, builder.Configuration); ConfigureMockServices(builder.Services, builder.Configuration); diff --git a/test/Altinn.App.Api.Tests/Utils/PrincipalUtil.cs b/test/Altinn.App.Api.Tests/Utils/PrincipalUtil.cs index 9ecbec6f2..c2162fe26 100644 --- a/test/Altinn.App.Api.Tests/Utils/PrincipalUtil.cs +++ b/test/Altinn.App.Api.Tests/Utils/PrincipalUtil.cs @@ -23,13 +23,15 @@ public static ClaimsPrincipal GetUserPrincipal(int? userId, int? partyId, int au ); if (userId > 0) { - claims.Add(new Claim(AltinnCoreClaimTypes.UserId, userId.ToString()!, ClaimValueTypes.String, issuer)); + claims.Add( + new Claim(AltinnCoreClaimTypes.UserId, userId.Value.ToString(), ClaimValueTypes.String, issuer) + ); } if (partyId > 0) { claims.Add( - new Claim(AltinnCoreClaimTypes.PartyID, userId.ToString()!, ClaimValueTypes.Integer32, issuer) + new Claim(AltinnCoreClaimTypes.PartyID, partyId.Value.ToString(), ClaimValueTypes.Integer32, issuer) ); } diff --git a/test/Altinn.App.Common.Tests/Altinn.App.Common.Tests.csproj b/test/Altinn.App.Common.Tests/Altinn.App.Common.Tests.csproj new file mode 100644 index 000000000..261873f17 --- /dev/null +++ b/test/Altinn.App.Common.Tests/Altinn.App.Common.Tests.csproj @@ -0,0 +1,43 @@ + + + + net8.0 + enable + enable + + false + true + + $(NoWarn);CS1591;CS0618 + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + diff --git a/test/Altinn.App.Common.Tests/TelemetryDITests.cs b/test/Altinn.App.Common.Tests/TelemetryDITests.cs new file mode 100644 index 000000000..21b1f2b6c --- /dev/null +++ b/test/Altinn.App.Common.Tests/TelemetryDITests.cs @@ -0,0 +1,38 @@ +using FluentAssertions; +using Microsoft.Extensions.DependencyInjection; + +namespace Altinn.App.Common.Tests; + +public class TelemetryDITests +{ + [Theory] + [InlineData(true)] + [InlineData(false)] + public void TelemetryFake_Is_Disposed(bool materialize) + { + using var scope = TelemetrySink.CreateScope(); + scope.IsDisposed.Should().BeFalse(); + + var services = new ServiceCollection(); + services.AddTelemetrySink(); + var sp = services.BuildServiceProvider( + new ServiceProviderOptions { ValidateOnBuild = true, ValidateScopes = true, } + ); + + if (materialize) + { + var fake = sp.GetRequiredService(); + fake.IsDisposed.Should().BeFalse(); + scope.IsDisposed.Should().BeFalse(); + sp.Dispose(); + fake.IsDisposed.Should().BeTrue(); + scope.IsDisposed.Should().BeTrue(); + } + else + { + scope.IsDisposed.Should().BeFalse(); + sp.Dispose(); + scope.IsDisposed.Should().BeFalse(); + } + } +} diff --git a/test/Altinn.App.Core.Tests/Mocks/TelemetrySink.cs b/test/Altinn.App.Common.Tests/TelemetrySink.cs similarity index 58% rename from test/Altinn.App.Core.Tests/Mocks/TelemetrySink.cs rename to test/Altinn.App.Common.Tests/TelemetrySink.cs index 94b87b905..dd0596137 100644 --- a/test/Altinn.App.Core.Tests/Mocks/TelemetrySink.cs +++ b/test/Altinn.App.Common.Tests/TelemetrySink.cs @@ -7,46 +7,87 @@ using FluentAssertions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using static Altinn.App.Core.Tests.Mocks.TelemetrySink; +using static Altinn.App.Common.Tests.TelemetrySink; -namespace Altinn.App.Core.Tests.Mocks; +namespace Altinn.App.Common.Tests; -internal sealed record TelemetrySink : IDisposable +public static class TelemetryDI { - internal bool IsDisposed { get; private set; } + public static IServiceCollection AddTelemetrySink( + this IServiceCollection services, + string org = "ttd", + string name = "test", + string version = "v1", + Func? shouldAlsoListenToActivities = null, + Func? shouldAlsoListenToMetrics = null + ) + { + var telemetryRegistration = services.FirstOrDefault(s => s.ServiceType == typeof(Telemetry)); + if (telemetryRegistration is not null) + services.Remove(telemetryRegistration); + + services.AddSingleton(sp => new TelemetrySink( + sp, + org, + name, + version, + telemetry: null, + shouldAlsoListenToActivities, + shouldAlsoListenToMetrics + )); + services.AddSingleton(sp => sp.GetRequiredService().Object); + + return services; + } +} + +public sealed record TelemetrySink : IDisposable +{ + public bool IsDisposed { get; private set; } - internal static ConcurrentDictionary Scopes { get; } = []; + public static ConcurrentDictionary Scopes { get; } = []; - internal Telemetry Object { get; } + public Telemetry Object { get; } - internal ActivityListener ActivityListener { get; } + public ActivityListener ActivityListener { get; } - internal MeterListener MeterListener { get; } + public MeterListener MeterListener { get; } private readonly ConcurrentBag _activities = []; private readonly ConcurrentDictionary> _metricValues = []; - internal readonly record struct MetricMeasurement(long Value, IReadOnlyDictionary Tags); + public readonly record struct MetricMeasurement(long Value, IReadOnlyDictionary Tags); + + public IEnumerable CapturedActivities => _activities; - internal IEnumerable CapturedActivities => _activities; + public IReadOnlyDictionary> CapturedMetrics => _metricValues; - internal IReadOnlyDictionary> CapturedMetrics => _metricValues; + public TelemetrySnapshot GetSnapshot() => new(CapturedActivities, CapturedMetrics); - internal TelemetrySnapshot GetSnapshot() => new(CapturedActivities, CapturedMetrics); + public TelemetrySnapshot GetSnapshot(Activity activity) => + new([activity], new Dictionary>()); - internal TelemetrySink(string org = "ttd", string name = "test", string version = "v1") + public TelemetrySink( + IServiceProvider? serviceProvider = null, + string org = "ttd", + string name = "test", + string version = "v1", + Telemetry? telemetry = null, + Func? shouldAlsoListenToActivities = null, + Func? shouldAlsoListenToMetrics = null + ) { var appId = new AppIdentifier(org, name); var options = new AppSettings { AppVersion = version, }; - Object = new Telemetry(appId, Options.Create(options)); + Object = telemetry ?? new Telemetry(appId, Options.Create(options)); ActivityListener = new ActivityListener() { ShouldListenTo = (activitySource) => { var sameSource = ReferenceEquals(activitySource, Object.ActivitySource); - return sameSource; + return sameSource || (shouldAlsoListenToActivities?.Invoke(serviceProvider!, activitySource) ?? false); }, Sample = (ref ActivityCreationOptions options) => ActivitySamplingResult.AllDataAndRecorded, @@ -62,7 +103,13 @@ internal TelemetrySink(string org = "ttd", string name = "test", string version InstrumentPublished = (instrument, listener) => { var sameSource = ReferenceEquals(instrument.Meter, Object.Meter); - if (!sameSource) + if ( + !sameSource + && ( + shouldAlsoListenToMetrics is null + || !shouldAlsoListenToMetrics(serviceProvider!, instrument.Meter) + ) + ) { return; } @@ -108,14 +155,14 @@ public void Dispose() } } - internal sealed class Scope : IDisposable + public sealed class Scope : IDisposable { - public bool IsDisposed { get; internal set; } + public bool IsDisposed { get; set; } public void Dispose() => Scopes.TryRemove(this, out _).Should().BeTrue(); } - internal static Scope CreateScope() + public static Scope CreateScope() { var scope = new Scope(); Scopes.TryAdd(scope, default).Should().BeTrue(); @@ -123,7 +170,7 @@ internal static Scope CreateScope() } } -internal class TelemetrySnapshot( +public class TelemetrySnapshot( IEnumerable? activities, IReadOnlyDictionary>? metrics ) @@ -142,47 +189,3 @@ internal class TelemetrySnapshot( ?.Select(m => new KeyValuePair>(m.Key, m.Value)) .Where(x => x.Value.Count != 0); } - -internal static class TelemetryDI -{ - internal static IServiceCollection AddTelemetrySink(this IServiceCollection services) - { - services.AddSingleton(_ => new TelemetrySink()); - services.AddSingleton(sp => sp.GetRequiredService().Object); - return services; - } -} - -public class TelemetryDITests -{ - [Theory] - [InlineData(true)] - [InlineData(false)] - public void TelemetryFake_Is_Disposed(bool materialize) - { - using var scope = TelemetrySink.CreateScope(); - scope.IsDisposed.Should().BeFalse(); - - var services = new ServiceCollection(); - services.AddTelemetrySink(); - var sp = services.BuildServiceProvider( - new ServiceProviderOptions { ValidateOnBuild = true, ValidateScopes = true, } - ); - - if (materialize) - { - var fake = sp.GetRequiredService(); - fake.IsDisposed.Should().BeFalse(); - scope.IsDisposed.Should().BeFalse(); - sp.Dispose(); - fake.IsDisposed.Should().BeTrue(); - scope.IsDisposed.Should().BeTrue(); - } - else - { - scope.IsDisposed.Should().BeFalse(); - sp.Dispose(); - scope.IsDisposed.Should().BeFalse(); - } - } -} diff --git a/test/Altinn.App.Core.Tests/Altinn.App.Core.Tests.csproj b/test/Altinn.App.Core.Tests/Altinn.App.Core.Tests.csproj index 93956770d..bb7c20f0b 100644 --- a/test/Altinn.App.Core.Tests/Altinn.App.Core.Tests.csproj +++ b/test/Altinn.App.Core.Tests/Altinn.App.Core.Tests.csproj @@ -60,6 +60,7 @@ + diff --git a/test/Altinn.App.Core.Tests/Features/Notifications/Email/EmailNotificationClientTests.cs b/test/Altinn.App.Core.Tests/Features/Notifications/Email/EmailNotificationClientTests.cs index fa61a88f8..71874d8e1 100644 --- a/test/Altinn.App.Core.Tests/Features/Notifications/Email/EmailNotificationClientTests.cs +++ b/test/Altinn.App.Core.Tests/Features/Notifications/Email/EmailNotificationClientTests.cs @@ -4,13 +4,13 @@ namespace Altinn.App.Core.Tests.Features.Notifications.Email; using System.Net.Http; using System.Text; using System.Text.Json; +using Altinn.App.Common.Tests; using Altinn.App.Core.Configuration; using Altinn.App.Core.Features; using Altinn.App.Core.Features.Notifications.Email; using Altinn.App.Core.Internal.App; using Altinn.App.Core.Models; using Altinn.App.Core.Models.Notifications.Email; -using Altinn.App.Core.Tests.Mocks; using Altinn.Common.AccessTokenClient.Services; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; diff --git a/test/Altinn.App.Core.Tests/Features/Notifications/Sms/SmsNotificationClientTests.cs b/test/Altinn.App.Core.Tests/Features/Notifications/Sms/SmsNotificationClientTests.cs index 8e8de71b1..fa16226a1 100644 --- a/test/Altinn.App.Core.Tests/Features/Notifications/Sms/SmsNotificationClientTests.cs +++ b/test/Altinn.App.Core.Tests/Features/Notifications/Sms/SmsNotificationClientTests.cs @@ -4,13 +4,13 @@ namespace Altinn.App.Core.Tests.Features.Notifications.Sms; using System.Net.Http; using System.Text; using System.Text.Json; +using Altinn.App.Common.Tests; using Altinn.App.Core.Configuration; using Altinn.App.Core.Features; using Altinn.App.Core.Features.Notifications.Sms; using Altinn.App.Core.Internal.App; using Altinn.App.Core.Models; using Altinn.App.Core.Models.Notifications.Sms; -using Altinn.App.Core.Tests.Mocks; using Altinn.Common.AccessTokenClient.Services; using FluentAssertions; using Microsoft.Extensions.DependencyInjection; diff --git a/test/Altinn.App.Core.Tests/Implementation/AppResourcesSITests.cs b/test/Altinn.App.Core.Tests/Implementation/AppResourcesSITests.cs index 72f63d3e1..0d650e99c 100644 --- a/test/Altinn.App.Core.Tests/Implementation/AppResourcesSITests.cs +++ b/test/Altinn.App.Core.Tests/Implementation/AppResourcesSITests.cs @@ -1,9 +1,9 @@ #nullable disable +using Altinn.App.Common.Tests; using Altinn.App.Core.Configuration; using Altinn.App.Core.Implementation; using Altinn.App.Core.Internal.App; using Altinn.App.Core.Models; -using Altinn.App.Core.Tests.Mocks; using Altinn.Platform.Storage.Interface.Models; using FluentAssertions; using Microsoft.Extensions.Logging.Abstractions; diff --git a/test/Altinn.App.Core.Tests/Implementation/EventsClientTest.cs b/test/Altinn.App.Core.Tests/Implementation/EventsClientTest.cs index bf2ef0863..84e49f365 100644 --- a/test/Altinn.App.Core.Tests/Implementation/EventsClientTest.cs +++ b/test/Altinn.App.Core.Tests/Implementation/EventsClientTest.cs @@ -1,13 +1,12 @@ #nullable disable using System.Net; using System.Text.Json; +using Altinn.App.Common.Tests; using Altinn.App.Core.Configuration; -using Altinn.App.Core.Features; using Altinn.App.Core.Helpers; using Altinn.App.Core.Infrastructure.Clients.Events; using Altinn.App.Core.Internal.App; using Altinn.App.Core.Models; -using Altinn.App.Core.Tests.Mocks; using Altinn.Common.AccessTokenClient.Services; using Altinn.Platform.Storage.Interface.Models; using Microsoft.AspNetCore.Http; diff --git a/test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.AddCompleteConfirmation_SuccessfulCallToStorage.verified.txt b/test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.AddCompleteConfirmation_SuccessfulCallToStorage.verified.txt index 7fe13848a..ea382db8c 100644 --- a/test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.AddCompleteConfirmation_SuccessfulCallToStorage.verified.txt +++ b/test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.AddCompleteConfirmation_SuccessfulCallToStorage.verified.txt @@ -7,7 +7,7 @@ instance.guid: Guid_1 }, { - instance.owner_party_id: 1337 + instance.owner.party.id: 1337 } ], IdFormat: W3C diff --git a/test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs b/test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs index 8e1c471fb..2a5c332d5 100644 --- a/test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs +++ b/test/Altinn.App.Core.Tests/Implementation/InstanceClientTests.cs @@ -1,11 +1,11 @@ #nullable disable using System.Net; using System.Text; +using Altinn.App.Common.Tests; using Altinn.App.Core.Configuration; using Altinn.App.Core.Helpers; using Altinn.App.Core.Infrastructure.Clients.Storage; using Altinn.App.Core.Models; -using Altinn.App.Core.Tests.Mocks; using Altinn.Platform.Storage.Interface.Models; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; diff --git a/test/Altinn.App.Core.Tests/Infrastructure/Clients/Authorization/AuthorizationClientTests.cs b/test/Altinn.App.Core.Tests/Infrastructure/Clients/Authorization/AuthorizationClientTests.cs index 00e762c56..900150b28 100644 --- a/test/Altinn.App.Core.Tests/Infrastructure/Clients/Authorization/AuthorizationClientTests.cs +++ b/test/Altinn.App.Core.Tests/Infrastructure/Clients/Authorization/AuthorizationClientTests.cs @@ -1,9 +1,9 @@ #nullable disable using System.Security.Claims; using System.Text.Json; +using Altinn.App.Common.Tests; using Altinn.App.Core.Configuration; using Altinn.App.Core.Infrastructure.Clients.Authorization; -using Altinn.App.Core.Tests.Mocks; using Altinn.Authorization.ABAC.Xacml.JsonProfile; using Altinn.Common.PEP.Interfaces; using Altinn.Platform.Storage.Interface.Models; diff --git a/test/Altinn.App.Core.Tests/Infrastructure/Clients/Storage/DataClientTests.cs b/test/Altinn.App.Core.Tests/Infrastructure/Clients/Storage/DataClientTests.cs index c622665d1..1b204e9ef 100644 --- a/test/Altinn.App.Core.Tests/Infrastructure/Clients/Storage/DataClientTests.cs +++ b/test/Altinn.App.Core.Tests/Infrastructure/Clients/Storage/DataClientTests.cs @@ -2,13 +2,13 @@ using System.Net.Http.Headers; using System.Net.Http.Json; using System.Text; +using Altinn.App.Common.Tests; using Altinn.App.Core.Configuration; using Altinn.App.Core.Helpers; using Altinn.App.Core.Infrastructure.Clients.Storage; using Altinn.App.Core.Internal.Auth; using Altinn.App.Core.Models; using Altinn.App.Core.Tests.Infrastructure.Clients.Storage.TestData; -using Altinn.App.Core.Tests.Mocks; using Altinn.App.PlatformServices.Tests.Data; using Altinn.App.PlatformServices.Tests.Mocks; using Altinn.Platform.Storage.Interface.Models; diff --git a/test/Altinn.App.Core.Tests/Internal/App/AppMedataTest.cs b/test/Altinn.App.Core.Tests/Internal/App/AppMedataTest.cs index 5af60298f..bf618b42a 100644 --- a/test/Altinn.App.Core.Tests/Internal/App/AppMedataTest.cs +++ b/test/Altinn.App.Core.Tests/Internal/App/AppMedataTest.cs @@ -1,11 +1,11 @@ #nullable disable using System.Text.Encodings.Web; using System.Text.Json; +using Altinn.App.Common.Tests; using Altinn.App.Core.Configuration; using Altinn.App.Core.Features; using Altinn.App.Core.Internal.App; using Altinn.App.Core.Models; -using Altinn.App.Core.Tests.Mocks; using Altinn.Platform.Storage.Interface.Models; using FluentAssertions; using Microsoft.Extensions.Options; diff --git a/test/Altinn.App.Core.Tests/Internal/Auth/AuthorizationServiceTests.cs b/test/Altinn.App.Core.Tests/Internal/Auth/AuthorizationServiceTests.cs index 598c10895..e198a65f8 100644 --- a/test/Altinn.App.Core.Tests/Internal/Auth/AuthorizationServiceTests.cs +++ b/test/Altinn.App.Core.Tests/Internal/Auth/AuthorizationServiceTests.cs @@ -1,4 +1,5 @@ using System.Security.Claims; +using Altinn.App.Common.Tests; using Altinn.App.Core.Features; using Altinn.App.Core.Features.Action; using Altinn.App.Core.Internal.Auth; @@ -6,7 +7,6 @@ using Altinn.App.Core.Internal.Process.Elements; using Altinn.App.Core.Internal.Process.Elements.AltinnExtensionProperties; using Altinn.App.Core.Models; -using Altinn.App.Core.Tests.Mocks; using Altinn.Platform.Register.Models; using Altinn.Platform.Storage.Interface.Models; using FluentAssertions; diff --git a/test/Altinn.App.Core.Tests/Internal/Patch/PatchServiceTests.cs b/test/Altinn.App.Core.Tests/Internal/Patch/PatchServiceTests.cs index 274a8260e..49eba51d9 100644 --- a/test/Altinn.App.Core.Tests/Internal/Patch/PatchServiceTests.cs +++ b/test/Altinn.App.Core.Tests/Internal/Patch/PatchServiceTests.cs @@ -1,4 +1,5 @@ using System.ComponentModel.DataAnnotations; +using Altinn.App.Common.Tests; using Altinn.App.Core.Features; using Altinn.App.Core.Internal.App; using Altinn.App.Core.Internal.AppModel; @@ -7,7 +8,6 @@ using Altinn.App.Core.Internal.Validation; using Altinn.App.Core.Models; using Altinn.App.Core.Models.Validation; -using Altinn.App.Core.Tests.Mocks; using Altinn.Platform.Storage.Interface.Models; using FluentAssertions; using Json.Patch; diff --git a/test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs b/test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs index 9207fb561..079ad52cd 100644 --- a/test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs +++ b/test/Altinn.App.Core.Tests/Internal/Pdf/PdfServiceTests.cs @@ -1,13 +1,12 @@ using System.Net; +using Altinn.App.Common.Tests; using Altinn.App.Core.Configuration; -using Altinn.App.Core.Features; using Altinn.App.Core.Infrastructure.Clients.Pdf; using Altinn.App.Core.Internal.App; using Altinn.App.Core.Internal.Auth; using Altinn.App.Core.Internal.Data; using Altinn.App.Core.Internal.Pdf; using Altinn.App.Core.Internal.Profile; -using Altinn.App.Core.Tests.Mocks; using Altinn.App.PlatformServices.Tests.Helpers; using Altinn.App.PlatformServices.Tests.Mocks; using Altinn.Platform.Storage.Interface.Models; @@ -15,7 +14,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Options; using Moq; -using Xunit; namespace Altinn.App.PlatformServices.Tests.Internal.Pdf { diff --git a/test/Altinn.App.Core.Tests/Internal/Process/ProcessEngineTest.cs b/test/Altinn.App.Core.Tests/Internal/Process/ProcessEngineTest.cs index c59d7e38b..67cc00fae 100644 --- a/test/Altinn.App.Core.Tests/Internal/Process/ProcessEngineTest.cs +++ b/test/Altinn.App.Core.Tests/Internal/Process/ProcessEngineTest.cs @@ -1,4 +1,5 @@ using System.Security.Claims; +using Altinn.App.Common.Tests; using Altinn.App.Core.Extensions; using Altinn.App.Core.Features; using Altinn.App.Core.Features.Action; @@ -8,7 +9,6 @@ using Altinn.App.Core.Internal.Profile; using Altinn.App.Core.Models.Process; using Altinn.App.Core.Models.UserAction; -using Altinn.App.Core.Tests.Mocks; using Altinn.Platform.Profile.Models; using Altinn.Platform.Register.Models; using Altinn.Platform.Storage.Interface.Enums; diff --git a/test/Altinn.App.Core.Tests/Internal/Process/ProcessReaderTests.cs b/test/Altinn.App.Core.Tests/Internal/Process/ProcessReaderTests.cs index 7e7f5b630..eb4586c86 100644 --- a/test/Altinn.App.Core.Tests/Internal/Process/ProcessReaderTests.cs +++ b/test/Altinn.App.Core.Tests/Internal/Process/ProcessReaderTests.cs @@ -1,12 +1,10 @@ -using Altinn.App.Core.Features; +using Altinn.App.Common.Tests; using Altinn.App.Core.Internal.Process; using Altinn.App.Core.Internal.Process.Elements; using Altinn.App.Core.Internal.Process.Elements.AltinnExtensionProperties; using Altinn.App.Core.Internal.Process.Elements.Base; using Altinn.App.Core.Tests.Internal.Process.TestUtils; -using Altinn.App.Core.Tests.Mocks; using FluentAssertions; -using Xunit; namespace Altinn.App.Core.Tests.Internal.Process; diff --git a/test/Altinn.App.Core.Tests/Internal/Process/TestUtils/ProcessTestUtils.cs b/test/Altinn.App.Core.Tests/Internal/Process/TestUtils/ProcessTestUtils.cs index 2dd3b69df..4c195975f 100644 --- a/test/Altinn.App.Core.Tests/Internal/Process/TestUtils/ProcessTestUtils.cs +++ b/test/Altinn.App.Core.Tests/Internal/Process/TestUtils/ProcessTestUtils.cs @@ -1,6 +1,5 @@ -using Altinn.App.Core.Features; +using Altinn.App.Common.Tests; using Altinn.App.Core.Internal.Process; -using Altinn.App.Core.Tests.Mocks; using Moq; namespace Altinn.App.Core.Tests.Internal.Process.TestUtils;