diff --git a/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventReceiverTests.cs b/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventReceiverTests.cs new file mode 100644 index 000000000..7b2b9fa13 --- /dev/null +++ b/tests/Squidex.Infrastructure.Tests/CQRS/Events/EventReceiverTests.cs @@ -0,0 +1,163 @@ +// ========================================================================== +// EventReceiverTests.cs +// Squidex Headless CMS +// ========================================================================== +// Copyright (c) Squidex Group +// All rights reserved. +// ========================================================================== + +using System; +using Microsoft.Extensions.Logging; +using Moq; +using Newtonsoft.Json; +using NodaTime; +using Xunit; + +namespace Squidex.Infrastructure.CQRS.Events +{ + public class EventReceiverTests + { + public sealed class MyEvent : IEvent + { + } + + private sealed class MyLogger : ILogger + { + public int LogCount { get; private set; } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatterr) + { + LogCount++; + } + + public bool IsEnabled(LogLevel logLevel) + { + return false; + } + + public IDisposable BeginScope(TState state) + { + return null; + } + } + + private readonly Mock liveConsumer1 = new Mock(); + private readonly Mock liveConsumer2 = new Mock(); + private readonly Mock catchConsumer1 = new Mock(); + private readonly Mock catchConsumer2 = new Mock(); + private readonly Mock eventStream = new Mock(); + private readonly Mock formatter = new Mock(new JsonSerializerSettings()); + private readonly EventData eventDataPast = new EventData(); + private readonly EventData eventDataFuture = new EventData(); + private readonly Envelope eventPast = new Envelope(new MyEvent()); + private readonly Envelope eventFuture = new Envelope(new MyEvent()); + private readonly MyLogger logger = new MyLogger(); + private readonly EventReceiver sut; + + public EventReceiverTests() + { + eventFuture.SetTimestamp(Instant.FromDateTimeUtc(DateTime.UtcNow.AddYears(1))); + + formatter.Setup(x => x.Parse(eventDataPast)).Returns(eventPast); + formatter.Setup(x => x.Parse(eventDataFuture)).Returns(eventFuture); + + eventStream.Setup(x => x.Connect("squidex", It.IsAny>())).Callback( + new Action>((queue, callback) => + { + callback(eventDataPast); + callback(eventDataFuture); + })); + + sut = + new EventReceiver( + logger, + eventStream.Object, + new[] + { + liveConsumer1.Object, + liveConsumer2.Object + }, + new[] + { + catchConsumer1.Object, + catchConsumer2.Object + }, + formatter.Object); + } + + [Fact] + public void Should_only_connect_once() + { + sut.Subscribe(); + sut.Subscribe(); + + eventStream.Verify(x => x.Connect("squidex", It.IsAny>()), Times.Once()); + } + + [Fact] + public void Should_invoke_consumers() + { + sut.Subscribe(); + + catchConsumer1.Verify(x => x.On(eventPast), Times.Once()); + catchConsumer2.Verify(x => x.On(eventPast), Times.Once()); + + catchConsumer1.Verify(x => x.On(eventFuture), Times.Once()); + catchConsumer2.Verify(x => x.On(eventFuture), Times.Once()); + + liveConsumer1.Verify(x => x.On(eventPast), Times.Never()); + liveConsumer2.Verify(x => x.On(eventPast), Times.Never()); + + liveConsumer1.Verify(x => x.On(eventFuture), Times.Once()); + liveConsumer2.Verify(x => x.On(eventFuture), Times.Once()); + + Assert.Equal(0, logger.LogCount); + } + + [Fact] + public void Should_log_if_parsing_event_failed() + { + formatter.Setup(x => x.Parse(eventDataPast)).Throws(new InvalidOperationException()); + + sut.Subscribe(); + + catchConsumer1.Verify(x => x.On(eventPast), Times.Never()); + catchConsumer2.Verify(x => x.On(eventPast), Times.Never()); + + catchConsumer1.Verify(x => x.On(eventFuture), Times.Once()); + catchConsumer2.Verify(x => x.On(eventFuture), Times.Once()); + + liveConsumer1.Verify(x => x.On(eventPast), Times.Never()); + liveConsumer2.Verify(x => x.On(eventPast), Times.Never()); + + liveConsumer1.Verify(x => x.On(eventFuture), Times.Once()); + liveConsumer2.Verify(x => x.On(eventFuture), Times.Once()); + + Assert.Equal(1, logger.LogCount); + } + + [Fact] + public void Should_log_if_handling_failed() + { + catchConsumer1.Setup(x => x.On(eventPast)).Throws(new InvalidOperationException()); + + liveConsumer1.Setup(x => x.On(eventFuture)).Throws(new InvalidOperationException()); + + sut.Subscribe(); + + catchConsumer1.Verify(x => x.On(eventPast), Times.Once()); + catchConsumer2.Verify(x => x.On(eventPast), Times.Once()); + + catchConsumer1.Verify(x => x.On(eventFuture), Times.Once()); + catchConsumer2.Verify(x => x.On(eventFuture), Times.Once()); + + liveConsumer1.Verify(x => x.On(eventPast), Times.Never()); + liveConsumer2.Verify(x => x.On(eventPast), Times.Never()); + + liveConsumer1.Verify(x => x.On(eventFuture), Times.Once()); + liveConsumer2.Verify(x => x.On(eventFuture), Times.Once()); + + Assert.Equal(2, logger.LogCount); + } + } +}