Example #1
0
        private async Task PutPrepareToSendNotificationEventAsync(
            PrepareToSendNotificationOutcome prepareToSendNotificationOutcome,
            GetAudienceResult getAudienceResult,
            GetDeliveryChannelConfigsResult getDeliveryChannelConfigsResult,
            PrepareToSendNotificationResult prepareToSendNotificationResult,
            long trackingCodeId,
            IReadOnlyCollection <NamedValue <string> > inheritableTags)
        {
            PrepareToSendNotificationEventBase @event;

            switch (prepareToSendNotificationOutcome)
            {
            case PrepareToSendNotificationOutcome.AudienceOptedOutOfAllChannels:
                @event = new AudienceOptedOutOfAllChannelsEvent(trackingCodeId, DateTime.UtcNow, getAudienceResult, getDeliveryChannelConfigsResult, prepareToSendNotificationResult);
                break;

            case PrepareToSendNotificationOutcome.PreparedToSendOnAllChannels:
                @event = new PreparedToSendOnAllChannelsEvent(trackingCodeId, DateTime.UtcNow, getAudienceResult, getDeliveryChannelConfigsResult, prepareToSendNotificationResult);
                break;

            case PrepareToSendNotificationOutcome.PreparedToSendOnSomeChannels:
                @event = new PreparedToSendOnSomeChannelsEvent(trackingCodeId, DateTime.UtcNow, getAudienceResult, getDeliveryChannelConfigsResult, prepareToSendNotificationResult);
                break;

            case PrepareToSendNotificationOutcome.CouldNotPrepareToSendOnAnyChannelDespiteAttemptingAll:
            case PrepareToSendNotificationOutcome.CouldNotPrepareToSendOnAnyChannelBecauseOneForcedAllToBeDiscarded:
                @event = new CouldNotPrepareToSendOnAnyChannelEvent(trackingCodeId, DateTime.UtcNow, getAudienceResult, getDeliveryChannelConfigsResult, prepareToSendNotificationResult);
                break;

            default:
                throw new NotSupportedException(Invariant($"This {nameof(PrepareToSendNotificationOutcome)} is not supported: {prepareToSendNotificationOutcome}"));
            }

            var tags = this.buildPrepareToSendNotificationEventTagsProtocol.ExecuteBuildTags(trackingCodeId, @event, inheritableTags);

            await this.eventStream.PutWithIdAsync(trackingCodeId, @event, tags, ExistingRecordStrategy.DoNotWriteIfFoundByIdAndType);
        }
Example #2
0
        static PrepareToSendNotificationResultTest()
        {
            ConstructorArgumentValidationTestScenarios
            .RemoveAllScenarios()
            .AddScenario(() =>
                         new ConstructorArgumentValidationTestScenario <PrepareToSendNotificationResult>
            {
                Name             = "constructor should throw ArgumentNullException when parameter 'channelToPrepareToSendOnChannelResultMap' is null scenario",
                ConstructionFunc = () =>
                {
                    var referenceObject = A.Dummy <PrepareToSendNotificationResult>();

                    var result = new PrepareToSendNotificationResult(
                        null,
                        referenceObject.CannotPrepareToSendOnChannelAction,
                        referenceObject.ChannelsToSendOn);

                    return(result);
                },
                ExpectedExceptionType            = typeof(ArgumentNullException),
                ExpectedExceptionMessageContains = new[] { "channelToPrepareToSendOnChannelResultMap", },
            })
            .AddScenario(() =>
                         new ConstructorArgumentValidationTestScenario <PrepareToSendNotificationResult>
            {
                Name             = "constructor should throw ArgumentException when parameter 'channelToPrepareToSendOnChannelResultMap' contains a key-value pair with a null value scenario",
                ConstructionFunc = () =>
                {
                    var referenceObject = A.Dummy <PrepareToSendNotificationResult>().Whose(_ => _.ChannelToPrepareToSendOnChannelResultMap.Any());

                    var dictionaryWithNullValue = referenceObject.ChannelToPrepareToSendOnChannelResultMap.ToDictionary(_ => _.Key, _ => _.Value);

                    var randomKey = dictionaryWithNullValue.Keys.ElementAt(ThreadSafeRandom.Next(0, dictionaryWithNullValue.Count));

                    dictionaryWithNullValue[randomKey] = null;

                    var result = new PrepareToSendNotificationResult(
                        dictionaryWithNullValue,
                        referenceObject.CannotPrepareToSendOnChannelAction,
                        referenceObject.ChannelsToSendOn);

                    return(result);
                },
                ExpectedExceptionType            = typeof(ArgumentException),
                ExpectedExceptionMessageContains = new[] { "channelToPrepareToSendOnChannelResultMap", "contains at least one key-value pair with a null value", },
            })
            .AddScenario(() =>
                         new ConstructorArgumentValidationTestScenario <PrepareToSendNotificationResult>
            {
                Name             = "constructor should throw ArgumentException when parameter 'channelToPrepareToSendOnChannelResultMap' values are not distinct",
                ConstructionFunc = () =>
                {
                    var referenceObject = A.Dummy <PrepareToSendNotificationResult>();

                    var prepareToSendOnChannelResult = A.Dummy <PrepareToSendOnChannelResult>();

                    var channelToPrepareToSendOnChannelResultMap = new Dictionary <IDeliveryChannel, PrepareToSendOnChannelResult>
                    {
                        {
                            new SlackDeliveryChannel(),
                            prepareToSendOnChannelResult
                        },
                        {
                            new EmailDeliveryChannel(),
                            prepareToSendOnChannelResult
                        },
                    };

                    var result = new PrepareToSendNotificationResult(
                        channelToPrepareToSendOnChannelResultMap,
                        referenceObject.CannotPrepareToSendOnChannelAction,
                        null);

                    return(result);
                },
                ExpectedExceptionType            = typeof(ArgumentException),
                ExpectedExceptionMessageContains = new[] { "prepareToSendOnChannelResult", "contains two or more elements that are equal" },
            })
            .AddScenario(() =>
                         new ConstructorArgumentValidationTestScenario <PrepareToSendNotificationResult>
            {
                Name             = "constructor should throw ArgumentOutOfRangeException when parameter 'cannotPrepareToSendOnChannelAction' is CannotPrepareToSendOnChannelAction.Unknown.",
                ConstructionFunc = () =>
                {
                    var referenceObject = A.Dummy <PrepareToSendNotificationResult>();

                    var result = new PrepareToSendNotificationResult(
                        referenceObject.ChannelToPrepareToSendOnChannelResultMap,
                        CannotPrepareToSendOnChannelAction.Unknown,
                        referenceObject.ChannelsToSendOn);

                    return(result);
                },
                ExpectedExceptionType            = typeof(ArgumentOutOfRangeException),
                ExpectedExceptionMessageContains = new[] { "cannotPrepareToSendOnChannelAction", "Unknown" },
            })
            .AddScenario(() =>
                         new ConstructorArgumentValidationTestScenario <PrepareToSendNotificationResult>
            {
                Name             = "constructor should throw ArgumentNullException when parameter 'channelsToSendOn' is null scenario",
                ConstructionFunc = () =>
                {
                    var referenceObject = A.Dummy <PrepareToSendNotificationResult>();

                    var result = new PrepareToSendNotificationResult(
                        referenceObject.ChannelToPrepareToSendOnChannelResultMap,
                        referenceObject.CannotPrepareToSendOnChannelAction,
                        null);

                    return(result);
                },
                ExpectedExceptionType            = typeof(ArgumentNullException),
                ExpectedExceptionMessageContains = new[] { "channelsToSendOn", },
            })
            .AddScenario(() =>
                         new ConstructorArgumentValidationTestScenario <PrepareToSendNotificationResult>
            {
                Name             = "constructor should throw ArgumentException when parameter 'channelsToSendOn' contains a null element scenario",
                ConstructionFunc = () =>
                {
                    var referenceObject = A.Dummy <PrepareToSendNotificationResult>();

                    var result = new PrepareToSendNotificationResult(
                        referenceObject.ChannelToPrepareToSendOnChannelResultMap,
                        referenceObject.CannotPrepareToSendOnChannelAction,
                        new IDeliveryChannel[0].Concat(referenceObject.ChannelsToSendOn).Concat(new IDeliveryChannel[] { null }).Concat(referenceObject.ChannelsToSendOn).ToList());

                    return(result);
                },
                ExpectedExceptionType            = typeof(ArgumentException),
                ExpectedExceptionMessageContains = new[] { "channelsToSendOn", "contains at least one null element", },
            })
            .AddScenario(() =>
                         new ConstructorArgumentValidationTestScenario <PrepareToSendNotificationResult>
            {
                Name             = "constructor should throw ArgumentException when parameter 'channelsToSendOn' has a channel that's not a key in 'channelToPrepareToSendOnChannelResultMap'",
                ConstructionFunc = () =>
                {
                    var referenceObject = A.Dummy <PrepareToSendNotificationResult>();

                    var result = new PrepareToSendNotificationResult(
                        new Dictionary <IDeliveryChannel, PrepareToSendOnChannelResult>
                    {
                        { new SlackDeliveryChannel(), A.Dummy <PrepareToSendOnChannelResult>() },
                    },
                        referenceObject.CannotPrepareToSendOnChannelAction,
                        new[] { new EmailDeliveryChannel() });

                    return(result);
                },
                ExpectedExceptionType            = typeof(ArgumentException),
                ExpectedExceptionMessageContains = new[] { "channelToPrepareToSendOnChannelResultMap", "does not contain the key to search for", },
            });

            DeepCloneWithTestScenarios
            .RemoveAllScenarios()
            .AddScenario(() =>
                         new DeepCloneWithTestScenario <PrepareToSendNotificationResult>
            {
                Name             = "DeepCloneWithChannelToPrepareToSendOnChannelResultMap should deep clone object and replace ChannelToPrepareToSendOnChannelResultMap with the provided channelToPrepareToSendOnChannelResultMap",
                WithPropertyName = "ChannelToPrepareToSendOnChannelResultMap",
                SystemUnderTestDeepCloneWithValueFunc = () =>
                {
                    var systemUnderTest = A.Dummy <PrepareToSendNotificationResult>();

                    var referenceObject = A.Dummy <PrepareToSendNotificationResult>().ThatIs(_ => (!systemUnderTest.ChannelToPrepareToSendOnChannelResultMap.IsEqualTo(_.ChannelToPrepareToSendOnChannelResultMap)) && systemUnderTest.ChannelsToSendOn.IsEqualTo(_.ChannelsToSendOn));

                    var result = new SystemUnderTestDeepCloneWithValue <PrepareToSendNotificationResult>
                    {
                        SystemUnderTest    = systemUnderTest,
                        DeepCloneWithValue = referenceObject.ChannelToPrepareToSendOnChannelResultMap,
                    };

                    return(result);
                },
            })
            .AddScenario(() =>
                         new DeepCloneWithTestScenario <PrepareToSendNotificationResult>
            {
                Name             = "DeepCloneWithCannotPrepareToSendOnChannelAction should deep clone object and replace CannotPrepareToSendOnChannelAction with the provided cannotPrepareToSendOnChannelAction",
                WithPropertyName = "CannotPrepareToSendOnChannelAction",
                SystemUnderTestDeepCloneWithValueFunc = () =>
                {
                    var systemUnderTest = A.Dummy <PrepareToSendNotificationResult>();

                    var referenceObject = A.Dummy <PrepareToSendNotificationResult>().ThatIs(_ => !systemUnderTest.CannotPrepareToSendOnChannelAction.IsEqualTo(_.CannotPrepareToSendOnChannelAction));

                    var result = new SystemUnderTestDeepCloneWithValue <PrepareToSendNotificationResult>
                    {
                        SystemUnderTest    = systemUnderTest,
                        DeepCloneWithValue = referenceObject.CannotPrepareToSendOnChannelAction,
                    };

                    return(result);
                },
            })
            .AddScenario(() =>
                         new DeepCloneWithTestScenario <PrepareToSendNotificationResult>
            {
                Name             = "DeepCloneWithChannelsToSendOn should deep clone object and replace ChannelsToSendOn with the provided channelsToSendOn",
                WithPropertyName = "ChannelsToSendOn",
                SystemUnderTestDeepCloneWithValueFunc = () =>
                {
                    var systemUnderTest = A.Dummy <PrepareToSendNotificationResult>().Whose(_ => _.ChannelToPrepareToSendOnChannelResultMap.Any());

                    var referenceObject = A.Dummy <PrepareToSendNotificationResult>().ThatIs(_ => (!systemUnderTest.ChannelsToSendOn.IsEqualTo(_.ChannelsToSendOn)) && _.ChannelsToSendOn.All(c => systemUnderTest.ChannelToPrepareToSendOnChannelResultMap.ContainsKey(c)));

                    var result = new SystemUnderTestDeepCloneWithValue <PrepareToSendNotificationResult>
                    {
                        SystemUnderTest    = systemUnderTest,
                        DeepCloneWithValue = referenceObject.ChannelsToSendOn,
                    };

                    return(result);
                },
            });

            var referenceObjectForEquatableTestScenarios = A.Dummy <PrepareToSendNotificationResult>().Whose(_ => _.ChannelToPrepareToSendOnChannelResultMap.Any());

            EquatableTestScenarios
            .RemoveAllScenarios()
            .AddScenario(() =>
                         new EquatableTestScenario <PrepareToSendNotificationResult>
            {
                Name            = "Default Code Generated Scenario",
                ReferenceObject = referenceObjectForEquatableTestScenarios,
                ObjectsThatAreEqualToButNotTheSameAsReferenceObject = new PrepareToSendNotificationResult[]
                {
                    new PrepareToSendNotificationResult(
                        referenceObjectForEquatableTestScenarios.ChannelToPrepareToSendOnChannelResultMap,
                        referenceObjectForEquatableTestScenarios.CannotPrepareToSendOnChannelAction,
                        referenceObjectForEquatableTestScenarios.ChannelsToSendOn),
                },
                ObjectsThatAreNotEqualToReferenceObject = new PrepareToSendNotificationResult[]
                {
                    new PrepareToSendNotificationResult(
                        A.Dummy <PrepareToSendNotificationResult>().Whose(_ => (!_.ChannelToPrepareToSendOnChannelResultMap.IsEqualTo(referenceObjectForEquatableTestScenarios.ChannelToPrepareToSendOnChannelResultMap)) && referenceObjectForEquatableTestScenarios.ChannelsToSendOn.All(c => _.ChannelToPrepareToSendOnChannelResultMap.ContainsKey(c))).ChannelToPrepareToSendOnChannelResultMap,
                        referenceObjectForEquatableTestScenarios.CannotPrepareToSendOnChannelAction,
                        referenceObjectForEquatableTestScenarios.ChannelsToSendOn),
                    new PrepareToSendNotificationResult(
                        referenceObjectForEquatableTestScenarios.ChannelToPrepareToSendOnChannelResultMap,
                        A.Dummy <PrepareToSendNotificationResult>().Whose(_ => !_.CannotPrepareToSendOnChannelAction.IsEqualTo(referenceObjectForEquatableTestScenarios.CannotPrepareToSendOnChannelAction)).CannotPrepareToSendOnChannelAction,
                        referenceObjectForEquatableTestScenarios.ChannelsToSendOn),
                    new PrepareToSendNotificationResult(
                        referenceObjectForEquatableTestScenarios.ChannelToPrepareToSendOnChannelResultMap,
                        referenceObjectForEquatableTestScenarios.CannotPrepareToSendOnChannelAction,
                        A.Dummy <PrepareToSendNotificationResult>().Whose(_ => (!_.ChannelsToSendOn.IsEqualTo(referenceObjectForEquatableTestScenarios.ChannelsToSendOn)) && _.ChannelsToSendOn.All(c => referenceObjectForEquatableTestScenarios.ChannelToPrepareToSendOnChannelResultMap.ContainsKey(c))).ChannelsToSendOn),
                },
                ObjectsThatAreNotOfTheSameTypeAsReferenceObject = new object[]
                {
                    A.Dummy <object>(),
                    A.Dummy <string>(),
                    A.Dummy <int>(),
                    A.Dummy <int?>(),
                    A.Dummy <Guid>(),
                },
            });
        }
Example #3
0
        public NotificationDummyFactory()
        {
            /* Add any overriding or custom registrations here. */

            // enums
            AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(CannotPrepareToSendOnChannelAction.Unknown);
            AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(DeliveryChannelAction.Unknown);
            AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(FailureAction.Unknown);
            AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(PrepareToSendOnChannelFailureAction.Unknown);
            AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(AttemptToSendNotificationOutcome.Unknown);
            AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(ChannelOperationOutcome.Unknown);
            AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(GetAudienceOutcome.Unknown);
            AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(GetDeliveryChannelConfigsOutcome.Unknown);
            AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(PrepareToSendNotificationOutcome.Unknown);
            AutoFixtureBackedDummyFactory.ConstrainDummyToExclude(PrepareToSendOnChannelOutcome.Unknown);

            // interfaces
            AutoFixtureBackedDummyFactory.AddDummyCreator <IOperation>(A.Dummy <OperationBase>);// remove when moved to Protocol Dummy Factory
            AutoFixtureBackedDummyFactory.UseRandomInterfaceImplementationForDummy <IAudience>();
            AutoFixtureBackedDummyFactory.UseRandomInterfaceImplementationForDummy <IDeliveryChannel>();
            AutoFixtureBackedDummyFactory.UseRandomInterfaceImplementationForDummy <IFailure>();
            AutoFixtureBackedDummyFactory.UseRandomInterfaceImplementationForDummy <INotification>();

            // events
            AutoFixtureBackedDummyFactory.AddDummyCreator(() => new CouldNotSendOnAnyPreparedChannelEvent(
                                                              A.Dummy <long>(),
                                                              A.Dummy <UtcDateTime>(),
                                                              A.Dummy <AttemptToSendNotificationResult>().Whose(_ => _.GetOutcome() == AttemptToSendNotificationOutcome.CouldNotSendOnAnyPreparedChannel)));

            AutoFixtureBackedDummyFactory.AddDummyCreator(() => new SentOnAllPreparedChannelsEvent(
                                                              A.Dummy <long>(),
                                                              A.Dummy <UtcDateTime>(),
                                                              A.Dummy <AttemptToSendNotificationResult>().Whose(_ => _.GetOutcome() == AttemptToSendNotificationOutcome.SentOnAllPreparedChannels)));

            AutoFixtureBackedDummyFactory.AddDummyCreator(() => new SentOnSomePreparedChannelsEvent(
                                                              A.Dummy <long>(),
                                                              A.Dummy <UtcDateTime>(),
                                                              A.Dummy <AttemptToSendNotificationResult>().Whose(_ => _.GetOutcome() == AttemptToSendNotificationOutcome.SentOnSomePreparedChannels)));

            AutoFixtureBackedDummyFactory.AddDummyCreator(() => new CouldNotGetOrUseAudienceEvent(
                                                              A.Dummy <long>(),
                                                              A.Dummy <UtcDateTime>(),
                                                              A.Dummy <GetAudienceResult>().Whose(_ => (_.GetOutcome() != GetAudienceOutcome.GotAudienceWithNoFailuresReported) && (_.GetOutcome() != GetAudienceOutcome.GotAudienceWithReportedFailuresIgnored))));

            AutoFixtureBackedDummyFactory.AddDummyCreator(() => new CouldNotGetOrUseDeliveryChannelConfigsEvent(
                                                              A.Dummy <long>(),
                                                              A.Dummy <UtcDateTime>(),
                                                              GetPassingAudienceResult(),
                                                              A.Dummy <GetDeliveryChannelConfigsResult>().Whose(_ => (_.GetOutcome() != GetDeliveryChannelConfigsOutcome.GotDeliveryChannelConfigsWithNoFailuresReported) && (_.GetOutcome() != GetDeliveryChannelConfigsOutcome.GotDeliveryChannelConfigsWithReportedFailuresIgnored))));

            AutoFixtureBackedDummyFactory.AddDummyCreator(() => new AudienceOptedOutOfAllChannelsEvent(
                                                              A.Dummy <long>(),
                                                              A.Dummy <UtcDateTime>(),
                                                              GetPassingAudienceResult(),
                                                              GetPassingDeliveryChannelConfigsResult(),
                                                              A.Dummy <PrepareToSendNotificationResult>().Whose(_ => (_.GetOutcome() == PrepareToSendNotificationOutcome.AudienceOptedOutOfAllChannels))));

            AutoFixtureBackedDummyFactory.AddDummyCreator(() => new CouldNotPrepareToSendOnAnyChannelEvent(
                                                              A.Dummy <long>(),
                                                              A.Dummy <UtcDateTime>(),
                                                              GetPassingAudienceResult(),
                                                              GetPassingDeliveryChannelConfigsResult(),
                                                              A.Dummy <PrepareToSendNotificationResult>().Whose(_ => (_.GetOutcome() == PrepareToSendNotificationOutcome.CouldNotPrepareToSendOnAnyChannelDespiteAttemptingAll) || (_.GetOutcome() == PrepareToSendNotificationOutcome.CouldNotPrepareToSendOnAnyChannelBecauseOneForcedAllToBeDiscarded))));

            AutoFixtureBackedDummyFactory.AddDummyCreator(() => new PreparedToSendOnAllChannelsEvent(
                                                              A.Dummy <long>(),
                                                              A.Dummy <UtcDateTime>(),
                                                              GetPassingAudienceResult(),
                                                              GetPassingDeliveryChannelConfigsResult(),
                                                              A.Dummy <PrepareToSendNotificationResult>().Whose(_ => (_.GetOutcome() == PrepareToSendNotificationOutcome.PreparedToSendOnAllChannels))));

            AutoFixtureBackedDummyFactory.AddDummyCreator(() => new PreparedToSendOnSomeChannelsEvent(
                                                              A.Dummy <long>(),
                                                              A.Dummy <UtcDateTime>(),
                                                              GetPassingAudienceResult(),
                                                              GetPassingDeliveryChannelConfigsResult(),
                                                              A.Dummy <PrepareToSendNotificationResult>().Whose(_ => (_.GetOutcome() == PrepareToSendNotificationOutcome.PreparedToSendOnSomeChannels))));

            AutoFixtureBackedDummyFactory.AddDummyCreator(() => new SendNotificationRequestedEvent(
                                                              A.Dummy <long>(),
                                                              A.Dummy <UtcDateTime>(),
                                                              A.Dummy <SendNotificationOp>()));

            // model classes
            AutoFixtureBackedDummyFactory.AddDummyCreator(() =>
            {
                var channelToOperationsOutcomeInfoMap = new Dictionary <IDeliveryChannel, IReadOnlyCollection <ChannelOperationOutcomeInfo> >();

                var randomIndex = ThreadSafeRandom.Next(1, 4);

                var randomChannels = new IDeliveryChannel[] { new SlackDeliveryChannel(), new EmailDeliveryChannel() }.RandomizeElements().ToArray();

                if (randomIndex == 1)
                {
                    // SentOnAllPreparedChannels
                    randomChannels = randomChannels.Take(ThreadSafeRandom.Next(1, randomChannels.Length + 1)).ToArray();

                    foreach (var randomChannel in randomChannels)
                    {
                        channelToOperationsOutcomeInfoMap.Add(
                            randomChannel,
                            Some.ReadOnlyDummies <ChannelOperationOutcomeInfo>().Whose(_ => _.All(o => o.Outcome == ChannelOperationOutcome.Succeeded)).ToList());
                    }
                }
                else if (randomIndex == 2)
                {
                    // SentOnSomePreparedChannels
                    channelToOperationsOutcomeInfoMap.Add(
                        randomChannels[0],
                        Some.ReadOnlyDummies <ChannelOperationOutcomeInfo>().Whose(_ => _.Select(o => o.Outcome).Distinct().Count() > 1).ToList());

                    channelToOperationsOutcomeInfoMap.Add(
                        randomChannels[1],
                        Some.ReadOnlyDummies <ChannelOperationOutcomeInfo>().Whose(_ => _.All(o => o.Outcome == ChannelOperationOutcome.Succeeded)).ToList());
                }
                else
                {
                    // CouldNotSendOnAnyPreparedChannel
                    channelToOperationsOutcomeInfoMap.Add(
                        randomChannels[0],
                        Some.ReadOnlyDummies <ChannelOperationOutcomeInfo>().Whose(_ => _.Select(o => o.Outcome).Distinct().Count() > 1).ToList());

                    channelToOperationsOutcomeInfoMap.Add(
                        randomChannels[1],
                        Some.ReadOnlyDummies <ChannelOperationOutcomeInfo>().Whose(_ => _.All(o => o.Outcome == ChannelOperationOutcome.Failed)).ToList());
                }


                var result = new AttemptToSendNotificationResult(channelToOperationsOutcomeInfoMap);

                return(result);
            });

            AutoFixtureBackedDummyFactory.AddDummyCreator(() =>
            {
                var randomIndex = ThreadSafeRandom.Next(1, 6);

                GetAudienceResult result;

                if (randomIndex == 1)
                {
                    // GotAudienceWithNoFailuresReported
                    result = new GetAudienceResult(A.Dummy <IAudience>(), null, A.Dummy <FailureAction>());
                }
                else if (randomIndex == 2)
                {
                    // GotAudienceWithReportedFailuresIgnored
                    result = new GetAudienceResult(A.Dummy <IAudience>(), Some.ReadOnlyDummies <IFailure>().ToList(), FailureAction.IgnoreAndProceedIfPossibleOtherwiseStop);
                }
                else if (randomIndex == 3)
                {
                    // CouldNotGetAudienceAndNoFailuresReported
                    if (ThreadSafeRandom.Next(2) == 0)
                    {
                        result = new GetAudienceResult(null, null, A.Dummy <FailureAction>());
                    }
                    else
                    {
                        result = new GetAudienceResult(null, new IFailure[0], A.Dummy <FailureAction>());
                    }
                }
                else if (randomIndex == 4)
                {
                    // CouldNotGetAudienceWithSomeFailuresReported
                    result = new GetAudienceResult(null, Some.ReadOnlyDummies <IFailure>().ToList(), A.Dummy <FailureAction>());
                }
                else
                {
                    // DespiteGettingAudienceFailuresPreventUsingIt
                    result = new GetAudienceResult(A.Dummy <IAudience>(), Some.ReadOnlyDummies <IFailure>().ToList(), FailureAction.Stop);
                }

                return(result);
            });

            AutoFixtureBackedDummyFactory.AddDummyCreator(() =>
            {
                var randomChannels = new IDeliveryChannel[] { new SlackDeliveryChannel(), new EmailDeliveryChannel() };

                randomChannels = randomChannels.RandomizeElements().Take(ThreadSafeRandom.Next(1, randomChannels.Length + 1)).ToArray();

                var deliveryChannelConfigs = randomChannels.Select(_ => new DeliveryChannelConfig(_, A.Dummy <DeliveryChannelAction>())).ToList();

                var randomIndex = ThreadSafeRandom.Next(1, 6);

                GetDeliveryChannelConfigsResult result;

                if (randomIndex == 1)
                {
                    // GotDeliveryChannelConfigsWithNoFailuresReported
                    result = new GetDeliveryChannelConfigsResult(
                        deliveryChannelConfigs,
                        null,
                        A.Dummy <FailureAction>());
                }
                else if (randomIndex == 2)
                {
                    // GotDeliveryChannelConfigsWithReportedFailuresIgnored
                    result = new GetDeliveryChannelConfigsResult(
                        deliveryChannelConfigs,
                        Some.ReadOnlyDummies <IFailure>().ToList(),
                        FailureAction.IgnoreAndProceedIfPossibleOtherwiseStop);
                }
                else if (randomIndex == 3)
                {
                    // CouldNotGetDeliveryChannelConfigsAndNoFailuresReported
                    result = new GetDeliveryChannelConfigsResult(null, null, A.Dummy <FailureAction>());
                }
                else if (randomIndex == 4)
                {
                    // CouldNotGetDeliveryChannelConfigsWithSomeFailuresReported
                    result = new GetDeliveryChannelConfigsResult(null, Some.ReadOnlyDummies <IFailure>().ToList(), A.Dummy <FailureAction>());
                }
                else
                {
                    // DespiteGettingDeliveryChannelConfigsFailuresPreventUsingThem
                    result = new GetDeliveryChannelConfigsResult(
                        deliveryChannelConfigs,
                        Some.ReadOnlyDummies <IFailure>().ToList(),
                        FailureAction.Stop);
                }

                return(result);
            });

            AutoFixtureBackedDummyFactory.AddDummyCreator(() =>
            {
                var randomChannels = new IDeliveryChannel[] { new SlackDeliveryChannel(), new EmailDeliveryChannel() }.RandomizeElements().ToArray();

                var randomIndex = ThreadSafeRandom.Next(1, 6);

                PrepareToSendNotificationResult result;

                if (randomIndex == 1)
                {
                    // AudienceOptedOutOfAllChannels
                    result = new PrepareToSendNotificationResult(
                        new Dictionary <IDeliveryChannel, PrepareToSendOnChannelResult>(),
                        A.Dummy <CannotPrepareToSendOnChannelAction>(),
                        new IDeliveryChannel[0]);
                }
                else if (randomIndex == 2)
                {
                    // PreparedToSendOnAllChannels
                    var channelToPrepareToSendOnChannelResultMap = new Dictionary <IDeliveryChannel, PrepareToSendOnChannelResult>();

                    foreach (var randomChannel in randomChannels)
                    {
                        channelToPrepareToSendOnChannelResultMap.Add(
                            randomChannel,
                            new PrepareToSendOnChannelResult(GetChannelOperationInstructions(), null, A.Dummy <PrepareToSendOnChannelFailureAction>()));
                    }

                    result = new PrepareToSendNotificationResult(
                        channelToPrepareToSendOnChannelResultMap,
                        A.Dummy <CannotPrepareToSendOnChannelAction>(),
                        randomChannels);
                }
                else if (randomIndex == 3)
                {
                    // PreparedToSendOnSomeChannels
                    result = new PrepareToSendNotificationResult(
                        new Dictionary <IDeliveryChannel, PrepareToSendOnChannelResult>
                    {
                        {
                            randomChannels[0],
                            new PrepareToSendOnChannelResult(
                                GetChannelOperationInstructions(),
                                Some.ReadOnlyDummies <IFailure>().ToList(),
                                PrepareToSendOnChannelFailureAction.IgnoreAndProceedIfPossibleOtherwiseDoNotSendOnChannel)
                        },
                        {
                            randomChannels[1],
                            new PrepareToSendOnChannelResult(
                                null,
                                null,
                                PrepareToSendOnChannelFailureAction.IgnoreAndProceedIfPossibleOtherwiseDoNotSendOnChannel)
                        },
                    },
                        CannotPrepareToSendOnChannelAction.ContinueAndAttemptPreparingToSendOnNextChannel,
                        new IDeliveryChannel[] { randomChannels[0] });
                }
                else if (randomIndex == 4)
                {
                    // CouldNotPrepareToSendOnAnyChannelDespiteAttemptingAll
                    result = new PrepareToSendNotificationResult(
                        new Dictionary <IDeliveryChannel, PrepareToSendOnChannelResult>
                    {
                        {
                            new SlackDeliveryChannel(),
                            new PrepareToSendOnChannelResult(
                                null,
                                null,
                                PrepareToSendOnChannelFailureAction.IgnoreAndProceedIfPossibleOtherwiseDoNotSendOnChannel)
                        },
                        {
                            new EmailDeliveryChannel(),
                            new PrepareToSendOnChannelResult(
                                GetChannelOperationInstructions(),
                                Some.ReadOnlyDummies <IFailure>().ToList(),
                                PrepareToSendOnChannelFailureAction.DoNotSendOnChannel)
                        },
                    },
                        CannotPrepareToSendOnChannelAction.ContinueAndAttemptPreparingToSendOnNextChannel,
                        new IDeliveryChannel[0]);
                }
                else
                {
                    // CouldNotPrepareToSendOnAnyChannelBecauseOneForcedAllToBeDiscarded
                    result = new PrepareToSendNotificationResult(
                        new Dictionary <IDeliveryChannel, PrepareToSendOnChannelResult>
                    {
                        {
                            randomChannels[0],
                            new PrepareToSendOnChannelResult(
                                GetChannelOperationInstructions(),
                                Some.ReadOnlyDummies <IFailure>().ToList(),
                                PrepareToSendOnChannelFailureAction.DoNotSendOnChannel)
                        },
                    },
                        CannotPrepareToSendOnChannelAction.StopAndNotDoNotSendOnAnyChannel,
                        new IDeliveryChannel[0]);
                }

                return(result);
            });

            AutoFixtureBackedDummyFactory.AddDummyCreator(() =>
            {
                var randomIndex = ThreadSafeRandom.Next(1, 6);

                PrepareToSendOnChannelResult result;

                if (randomIndex == 1)
                {
                    // PreparedToSendOnChannelWithNoFailuresReported
                    result = new PrepareToSendOnChannelResult(
                        GetChannelOperationInstructions(),
                        null,
                        A.Dummy <PrepareToSendOnChannelFailureAction>());
                }
                else if (randomIndex == 2)
                {
                    // PreparedToSendOnChannelWithReportedFailuresIgnored
                    result = new PrepareToSendOnChannelResult(
                        GetChannelOperationInstructions(),
                        Some.ReadOnlyDummies <IFailure>().ToList(),
                        PrepareToSendOnChannelFailureAction.IgnoreAndProceedIfPossibleOtherwiseDoNotSendOnChannel);
                }
                else if (randomIndex == 3)
                {
                    // CouldNotPrepareToSendOnChannelAndNoFailuresReported
                    if (ThreadSafeRandom.Next(2) == 0)
                    {
                        result = new PrepareToSendOnChannelResult(
                            null,
                            null,
                            PrepareToSendOnChannelFailureAction.IgnoreAndProceedIfPossibleOtherwiseDoNotSendOnChannel);
                    }
                    else
                    {
                        result = new PrepareToSendOnChannelResult(
                            new ChannelOperationInstruction[0],
                            new IFailure[0],
                            PrepareToSendOnChannelFailureAction.IgnoreAndProceedIfPossibleOtherwiseDoNotSendOnChannel);
                    }
                }
                else if (randomIndex == 4)
                {
                    // CouldNotPrepareToSendOnChannelWithSomeFailuresReported
                    result = new PrepareToSendOnChannelResult(
                        null,
                        Some.ReadOnlyDummies <IFailure>().ToList(),
                        PrepareToSendOnChannelFailureAction.IgnoreAndProceedIfPossibleOtherwiseDoNotSendOnChannel);
                }
                else
                {
                    // DespitePreparingToSendOnChannelFailuresPreventUsingIt
                    result = new PrepareToSendOnChannelResult(
                        GetChannelOperationInstructions(),
                        Some.ReadOnlyDummies <IFailure>().ToList(),
                        PrepareToSendOnChannelFailureAction.DoNotSendOnChannel);
                }

                return(result);
            });

            AutoFixtureBackedDummyFactory.AddDummyCreator(() =>
            {
                var succeededEventType = A.Dummy <TypeRepresentation>();

                var failedEventType = A.Dummy <TypeRepresentation>().ThatIsNot(succeededEventType);

                var result = new ChannelOperationMonitoringInfo(A.Dummy <long>(), succeededEventType, failedEventType);

                return(result);
            });
        }
Example #4
0
        /// <inheritdoc />
        public override async Task ExecuteAsync(
            HandleRecordOp <ExecuteOpRequestedEvent <long, SendNotificationOp> > operation)
        {
            new { operation }.AsArg().Must().NotBeNull();

            // Pull some info out of the operation.
            var trackingCodeId     = operation.RecordToHandle.Payload.Id;
            var sendNotificationOp = operation.RecordToHandle.Payload.Operation;
            var notification       = sendNotificationOp.Notification;
            var tags = operation.RecordToHandle.Metadata.Tags;

            // Write SendNotificationRequestedEvent to the Event Stream (creates the Notification Aggregate).
            await this.PutSendNotificationRequestedEventAsync(sendNotificationOp, trackingCodeId, tags);

            // Get the audience and write CannotGetOrUseAudienceEvent
            // to the Event Stream if there is none of if we should stop on failures.
            var getAudienceResult = await this.GetAudienceAsync(notification);

            var getAudienceOutcome = getAudienceResult.GetOutcome();

            if ((getAudienceOutcome != GetAudienceOutcome.GotAudienceWithNoFailuresReported) &&
                (getAudienceOutcome != GetAudienceOutcome.GotAudienceWithReportedFailuresIgnored))
            {
                await this.PutCannotGetOrUseAudienceEventAsync(getAudienceResult, trackingCodeId, tags);

                return;
            }

            // Get the delivery channel configs and write CannotGetOrUseDeliveryChannelConfigsEvent
            // to the Event Stream if there are none or if we should stop on failures.
            var audience = getAudienceResult.Audience;

            var getDeliveryChannelConfigsResult = await this.GetDeliveryChannelConfigsAsync(notification, getAudienceResult.Audience);

            var getDeliveryChannelConfigsOutcome = getDeliveryChannelConfigsResult.GetOutcome();

            if ((getDeliveryChannelConfigsOutcome != GetDeliveryChannelConfigsOutcome.GotDeliveryChannelConfigsWithNoFailuresReported) &&
                (getDeliveryChannelConfigsOutcome != GetDeliveryChannelConfigsOutcome.GotDeliveryChannelConfigsWithReportedFailuresIgnored))
            {
                await this.PutCannotGetOrUseDeliveryChannelConfigsEventAsync(getAudienceResult, getDeliveryChannelConfigsResult, trackingCodeId, tags);

                return;
            }

            // Prepare the notification to be sent to the audience on all channels configured for sending.
            var channelConfigs = getDeliveryChannelConfigsResult.Configs;

            var channelToPrepareToSendOnChannelResultMap = new Dictionary <IDeliveryChannel, PrepareToSendOnChannelResult>();

            var channelToOperationInstructionsMap = new Dictionary <IDeliveryChannel, IReadOnlyList <ChannelOperationInstruction> >();

            foreach (var channelConfig in channelConfigs)
            {
                if (channelConfig.Action == DeliveryChannelAction.AudienceOptedOut)
                {
                    continue;
                }
                else if (channelConfig.Action == DeliveryChannelAction.SendOnChannel)
                {
                    // no-op
                }
                else
                {
                    throw new NotSupportedException(Invariant($"This {nameof(DeliveryChannelAction)} is not supported: {channelConfig.Action}."));
                }

                var channel = channelConfig.Channel;

                var prepareToSendOnChannelResult = await this.PrepareToSendOnChannelAsync(notification, audience, channel, tags);

                var prepareToSendOnChannelOutcome = prepareToSendOnChannelResult.GetOutcome();

                channelToPrepareToSendOnChannelResultMap.Add(channel, prepareToSendOnChannelResult);

                if ((prepareToSendOnChannelOutcome == PrepareToSendOnChannelOutcome.PreparedToSendOnChannelWithNoFailuresReported) ||
                    (prepareToSendOnChannelOutcome == PrepareToSendOnChannelOutcome.PreparedToSendOnChannelWithReportedFailuresIgnored))
                {
                    new { this.channelToOperationStreamMap }.AsOp().Must().ContainKey(channel, Invariant($"Staging to send on channel {channel.GetType().ToStringReadable()} but there is no operation stream associated with that channel."));

                    channelToOperationInstructionsMap.Add(channel, prepareToSendOnChannelResult.ChannelOperationInstructions);
                }
                else
                {
                    if (this.cannotPrepareToSendOnChannelAction == CannotPrepareToSendOnChannelAction.ContinueAndAttemptPreparingToSendOnNextChannel)
                    {
                        // no-op
                    }
                    else if (this.cannotPrepareToSendOnChannelAction == CannotPrepareToSendOnChannelAction.StopAndNotDoNotSendOnAnyChannel)
                    {
                        // discard all channel operations
                        channelToOperationInstructionsMap = new Dictionary <IDeliveryChannel, IReadOnlyList <ChannelOperationInstruction> >();

                        break;
                    }
                    else
                    {
                        throw new NotSupportedException(Invariant($"This {nameof(CannotPrepareToSendOnChannelAction)} is not supported: {this.cannotPrepareToSendOnChannelAction}."));
                    }
                }
            }

            var prepareToSendNotificationResult = new PrepareToSendNotificationResult(channelToPrepareToSendOnChannelResultMap, this.cannotPrepareToSendOnChannelAction, channelToOperationInstructionsMap.Keys.ToList());

            var prepareToSendNotificationOutcome = prepareToSendNotificationResult.GetOutcome();

            await this.PutPrepareToSendNotificationEventAsync(prepareToSendNotificationOutcome, getAudienceResult, getDeliveryChannelConfigsResult, prepareToSendNotificationResult, trackingCodeId, tags);

            // Is at least one channel is prepared for sending?  Continue.
            if ((prepareToSendNotificationOutcome != PrepareToSendNotificationOutcome.PreparedToSendOnAllChannels) &&
                (prepareToSendNotificationOutcome != PrepareToSendNotificationOutcome.PreparedToSendOnSomeChannels))
            {
                return;
            }

            // Create a Saga to track channel operations.
            await this.PutSagaAsync(trackingCodeId, channelToOperationInstructionsMap, tags);

            // Push the channel-operations to their streams.
            foreach (var channel in channelToOperationInstructionsMap.Keys)
            {
                var channelOperationInstructions = channelToOperationInstructionsMap[channel];

                await this.PutChannelOperationsAsync(channel, channelOperationInstructions);
            }
        }