// ========================================================================== // Squidex Headless CMS // ========================================================================== // Copyright (c) Squidex UG (haftungsbeschraenkt) // All rights reserved. Licensed under the MIT license. // ========================================================================== using System.Globalization; using FakeItEasy; using Squidex.Domain.Apps.Entities.TestHelpers; using Squidex.Infrastructure; using Squidex.Infrastructure.Log; using Xunit; namespace Squidex.Domain.Apps.Entities.Apps { public class DefaultAppLogStoreTests { private readonly CancellationTokenSource cts = new CancellationTokenSource(); private readonly CancellationToken ct; private readonly IRequestLogStore requestLogStore = A.Fake(); private readonly DomainId appId = DomainId.NewGuid(); private readonly DefaultAppLogStore sut; public DefaultAppLogStoreTests() { ct = cts.Token; sut = new DefaultAppLogStore(requestLogStore); } [Fact] public void Should_run_deletion_in_default_order() { var order = ((IDeleter)sut).Order; Assert.Equal(0, order); } [Fact] public async Task Should_remove_events_from_streams() { var app = Mocks.App(NamedId.Of(appId, "my-app")); await ((IDeleter)sut).DeleteAppAsync(app, ct); A.CallTo(() => requestLogStore.DeleteAsync($"^[a-z]-{app.Id}", A._)) .MustNotHaveHappened(); } [Fact] public async Task Should_not_forward_request_if_disabled() { A.CallTo(() => requestLogStore.IsEnabled) .Returns(false); await sut.LogAsync(appId, default, ct); A.CallTo(() => requestLogStore.LogAsync(A._, A._)) .MustNotHaveHappened(); } [Fact] public async Task Should_forward_request_log_to_store() { Request? recordedRequest = null; A.CallTo(() => requestLogStore.IsEnabled) .Returns(true); A.CallTo(() => requestLogStore.LogAsync(A._, ct)) .Invokes(x => recordedRequest = x.GetArgument(0)!); var request = default(RequestLog); request.Bytes = 1024; request.CacheHits = 10; request.CacheServer = "server-fra"; request.CacheStatus = "MISS"; request.CacheTTL = 3600; request.Costs = 1.5; request.ElapsedMs = 120; request.RequestMethod = "GET"; request.RequestPath = "/my-path"; request.StatusCode = 200; request.Timestamp = default; request.UserClientId = "frontend"; request.UserId = "user1"; await sut.LogAsync(appId, request, ct); Assert.NotNull(recordedRequest); Contains(request.Bytes, recordedRequest); Contains(request.CacheHits, recordedRequest); Contains(request.CacheServer, recordedRequest); Contains(request.CacheStatus, recordedRequest); Contains(request.CacheTTL, recordedRequest); Contains(request.ElapsedMs.ToString(CultureInfo.InvariantCulture), recordedRequest); Contains(request.RequestMethod, recordedRequest); Contains(request.RequestPath, recordedRequest); Contains(request.StatusCode, recordedRequest); Contains(request.UserClientId, recordedRequest); Contains(request.UserId, recordedRequest); Assert.Equal(appId.ToString(), recordedRequest?.Key); } [Fact] public async Task Should_write_to_stream() { var dateFrom = DateTime.UtcNow.Date.AddDays(-30); var dateTo = DateTime.UtcNow.Date; A.CallTo(() => requestLogStore.QueryAllAsync(appId.ToString(), dateFrom, dateTo, ct)) .Returns(new[] { CreateRecord(), CreateRecord(), CreateRecord(), CreateRecord() }.ToAsyncEnumerable()); var stream = new MemoryStream(); await sut.ReadLogAsync(appId, dateFrom, dateTo, stream, ct); stream.Position = 0; var lines = 0; using (var reader = new StreamReader(stream)) { while (await reader.ReadLineAsync() != null) { lines++; } } Assert.Equal(5, lines); } private static void Contains(string value, Request? request) { Assert.Contains(value, request!.Properties.Values); } private static void Contains(object value, Request? request) { Assert.Contains(Convert.ToString(value, CultureInfo.InvariantCulture), request!.Properties.Values); } private static Request CreateRecord() { return new Request { Properties = new Dictionary() }; } } }