Example #1
0
        public void DoesNotNotifyWhenNewSelectorHasNoRecord()
        {
            Guid Id = Guid.NewGuid();

            DkimEntityState oldState = new DkimEntityState("", 0, DkimState.PollPending, DateTime.MinValue, DateTime.MinValue, DateTime.MaxValue, new List <DkimSelector>
            {
                CreateDkimSelector("selector1", new List <Message>
                {
                    new Message(Id, "selector 1 record 1 message 1", string.Empty, MessageType.Info)
                })
            });

            DkimRecordEvaluationResult newRecord = new DkimRecordEvaluationResult(null, new List <DkimSelectorResult>
            {
                CreateDkimSelectorResult("selector1", new List <DkimEvaluatorMessage>
                {
                    new DkimEvaluatorMessage(Id, EvaluationErrorType.Info, "selector 1 record 1 message 1 same as old", string.Empty)
                }),

                new DkimSelectorResult(new Contracts.SharedDomain.DkimSelector("rogueSelector"), null)
            });

            _advisoryChangedNotifier.Handle(oldState, newRecord);

            A.CallTo(() => _messageDispatcher.Dispatch(A <Common.Messaging.Abstractions.Message> ._, A <string> ._)).MustNotHaveHappened();
        }
Example #2
0
        public void NotifiesWhenSelectorChanges()
        {
            Dictionary <string, List <string> > selectorsAndRecords1 = new Dictionary <string, List <string> >
            {
                { "selector1", new List <string> {
                      "record1"
                  } }
            };

            Dictionary <string, List <string> > selectorsAndRecords2 = new Dictionary <string, List <string> >
            {
                { "selector2", new List <string> {
                      "record1"
                  } }
            };

            DkimEntityState   state   = CreateDkimEntityState(selectorsAndRecords1);
            DkimRecordsPolled message = CreateDkimRecordsPolled(selectorsAndRecords2);

            _recordChangedNotifier.Handle(state, message);

            A.CallTo(() => _messageDispatcher.Dispatch(A <DkimRecordAdded> ._, A <string> ._)).MustHaveHappenedOnceExactly();
            A.CallTo(() => _messageDispatcher.Dispatch(A <DkimRecordRemoved> ._, A <string> ._)).MustHaveHappenedOnceExactly();

            A.CallTo(() =>
                     _messageDispatcher.Dispatch(
                         A <DkimRecordAdded> .That.Matches(x =>
                                                           x.SelectorRecords[0].Selector == "selector2" && x.SelectorRecords[0].Records[0] == "record1"), A <string> ._)).MustHaveHappenedOnceExactly();

            A.CallTo(() =>
                     _messageDispatcher.Dispatch(
                         A <DkimRecordRemoved> .That.Matches(x =>
                                                             x.SelectorRecords[0].Selector == "selector1" && x.SelectorRecords[0].Records[0] == "record1"), A <string> ._)).MustHaveHappenedOnceExactly();
        }
 public void Handle(DkimEntityState state, Common.Messaging.Abstractions.Message message)
 {
     foreach (IChangeNotifier changeNotifier in _notifiers)
     {
         changeNotifier.Handle(state, message);
     }
 }
Example #4
0
        public void UpdateSelectorWhenNewSelectorsAddsNewSelectors()
        {
            DkimEntityState state = CreateState(Domain, DkimState.Evaluated);

            DkimSelector selector1 = CreateSelector(Selector1);
            DkimSelector selector2 = CreateSelector(Selector2);
            DkimSelector selector3 = CreateSelector(Selector3);

            state.UpdateSelectors(new List <DkimSelector> {
                selector1, selector2
            }, out DkimSelectorsUpdated dkimSelectorsUpdated);

            bool updated = state.UpdateSelectors(new List <DkimSelector> {
                selector3
            }, out dkimSelectorsUpdated);

            Assert.That(updated, Is.True);

            Assert.That(state.Selectors.Count == 3);
            Assert.That(state.Selectors[0], Is.EqualTo(selector1));
            Assert.That(state.Selectors[1], Is.EqualTo(selector2));
            Assert.That(state.Selectors[2], Is.EqualTo(selector3));

            Assert.That(dkimSelectorsUpdated.Selectors.Count == 3);
            Assert.That(dkimSelectorsUpdated.Selectors[0] == selector1.Selector);
            Assert.That(dkimSelectorsUpdated.Selectors[1] == selector2.Selector);
            Assert.That(dkimSelectorsUpdated.Selectors[2] == selector3.Selector);
        }
Example #5
0
        public void DoesNotNotifyWhenNewSelectorHasNoRecord()
        {
            Dictionary <string, List <string> > selectorsAndRecords1 = new Dictionary <string, List <string> >
            {
                { "selector1", new List <string> {
                      "record"
                  } }
            };

            DkimEntityState   state   = CreateDkimEntityState(selectorsAndRecords1);
            DkimRecordsPolled message = new DkimRecordsPolled("", new List <DkimSelectorRecords>
            {
                new DkimSelectorRecords(Guid.NewGuid(), "selector1", new List <DkimTxtRecord>()
                {
                    new DkimTxtRecord(new List <string> {
                        "record"
                    })
                }, null, 0),
                new DkimSelectorRecords(Guid.NewGuid(), "rogueSelector", null, null, 0)
            });

            _recordChangedNotifier.Handle(state, message);

            A.CallTo(() => _messageDispatcher.Dispatch(A <Common.Messaging.Abstractions.Message> .Ignored, A <string> .Ignored)).MustNotHaveHappened();
        }
Example #6
0
        public void NotifiesWhenSelectorRemoved()
        {
            Guid Id1 = Guid.NewGuid();

            DkimEntityState oldState = new DkimEntityState("", 0, DkimState.PollPending, DateTime.MinValue, DateTime.MinValue, DateTime.MaxValue, new List <DkimSelector>
            {
                CreateDkimSelector("selector1", new List <Message>
                {
                    new Message(Id1, "selector 1 record 1 message 1", string.Empty, MessageType.Info)
                }),
                CreateDkimSelector("selector2", new List <Message>
                {
                    new Message(Guid.NewGuid(), "selector 2 record 1 message 1", string.Empty, MessageType.Info)
                })
            });

            DkimRecordEvaluationResult newRecord = new DkimRecordEvaluationResult(null, new List <DkimSelectorResult>
            {
                CreateDkimSelectorResult("selector1", new List <DkimEvaluatorMessage>
                {
                    new DkimEvaluatorMessage(Id1, EvaluationErrorType.Info, "selector 1 record 1 message 1", string.Empty)
                })
            });

            _advisoryChangedNotifier.Handle(oldState, newRecord);

            A.CallTo(() => _messageDispatcher.Dispatch(A <DkimAdvisoryRemoved> ._, A <string> ._)).MustHaveHappenedOnceExactly();
            A.CallTo(() => _messageDispatcher.Dispatch(A <DkimAdvisoryAdded> ._, A <string> ._)).MustNotHaveHappened();

            A.CallTo(() =>
                     _messageDispatcher.Dispatch(
                         A <DkimAdvisoryRemoved> .That.Matches(x =>
                                                               x.SelectorMessages[0].Selector == "selector2" && (MessageType)x.SelectorMessages[0].Messages[0].MessageType == MessageType.Info &&
                                                               x.SelectorMessages[0].Messages[0].Text == "selector 2 record 1 message 1"), A <string> ._)).MustHaveHappenedOnceExactly();
        }
        private DateTime GetRecordStartDate(DkimEntityState dkimEntityState)
        {
            List <DateTime> dateTimes = dkimEntityState.LastUpdateSources.Values.ToList();

            dateTimes.Add(dkimEntityState.Created);

            return(dateTimes.Max());
        }
        public async Task HandleDkimEvaluationUpdateCantUpdateDoesNotContinue()
        {
            DkimEntityState dkimEntityState = A.Fake <DkimEntityState>();

            A.CallTo(() => dkimEntityState.Id).Returns(Id);
            A.CallTo(() => dkimEntityState.CanUpdate(A <string> ._, A <DateTime> ._)).Returns(false);

            A.CallTo(() => _dkimEntityDao.Get(Id)).Returns(dkimEntityState);

            await _dkimEntity.Handle(new DkimRecordEvaluationResult(Id, new List <DkimSelectorResult>()));

            A.CallTo(() => _dkimEntityDao.Save(A <DkimEntityState> ._)).MustNotHaveHappened();
            A.CallTo(() => _dispatcher.Dispatch(A <Common.Messaging.Abstractions.Message> ._, A <string> ._)).MustNotHaveHappened();
        }
        public async Task SaveStateAlreadyPersistedForVersionThrows()
        {
            DkimEntityState state = new DkimEntityState(Id, 1, DkimState.Evaluated, DateTime.UtcNow, DateTime.UtcNow, DateTime.UtcNow,
                                                        new List <DkimSelector> {
                new DkimSelector(Selector1)
            });

            await _dao.Save(state);

            InvalidOperationException exception = Assert.ThrowsAsync <InvalidOperationException>(() => _dao.Save(state));

            Assert.That(exception.Message,
                        Is.EqualTo($"Didn\'t update DkimEntityState because version 1 has already been persisted."));
        }
        public async Task HandleDkimEvaluationUpdateSuccessful()
        {
            DkimEntityState dkimEntityState = A.Fake <DkimEntityState>();

            A.CallTo(() => dkimEntityState.Id).Returns(Id);
            A.CallTo(() => dkimEntityState.CanUpdate(A <string> ._, A <DateTime> ._)).Returns(true);

            A.CallTo(() => _dkimEntityDao.Get(Id)).Returns(dkimEntityState);

            DkimRecordEvaluationResult dkimRecordEvaluationResult = new DkimRecordEvaluationResult(Id, new List <DkimSelectorResult>());
            await _dkimEntity.Handle(dkimRecordEvaluationResult);

            A.CallTo(() => _dkimEntityDao.Save(dkimEntityState)).MustHaveHappened();
            A.CallTo(() => _dispatcher.Dispatch(A <DkimEvaluationUpdated> ._, A <string> ._)).MustHaveHappened();
        }
Example #11
0
        private DkimEntityState CreateDkimEntityState(Dictionary <string, List <string> > selectorsAndRecords)
        {
            List <DkimSelector> dkimSelectors = new List <DkimSelector>();

            foreach (KeyValuePair <string, List <string> > kvp in selectorsAndRecords)
            {
                List <DkimRecord> dkimRecords = kvp.Value.Select(record => new DkimRecord(new DnsRecord(record, null))).ToList();
                dkimSelectors.Add(new DkimSelector(kvp.Key, dkimRecords));
            }

            _fixture.Register(() => dkimSelectors);
            DkimEntityState dkimEntityState = _fixture.Create <DkimEntityState>();

            return(dkimEntityState);
        }
Example #12
0
        public void UpdatesWithNoChangesRecordEvaluationMessages()
        {
            DkimSelector selector1 = CreateSelector(Selector1, CreateRecord(Dkim1));

            DkimEntityState state = CreateState(Domain, DkimState.PollPending, selector1);

            DkimSelector selector2 = CreateSelector(Selector1, CreateRecord(Dkim1));

            state.UpdateRecords(new List <DkimSelector> {
                selector2
            }, DateTime.UtcNow);

            DkimEvaluationUpdated evaluationUpdated = state.UpdateEvaluations(DateTime.UtcNow);

            Assert.That(evaluationUpdated, Is.Not.Null);
        }
Example #13
0
        public void UpdatesEmptyRecordEvaluationMessages()
        {
            DkimSelector selector1 = CreateSelector(Selector1, CreateRecord(Dkim1, new Message(Guid.NewGuid(), "oh boy!", string.Empty, MessageType.Warning)));

            DkimEntityState state = CreateState(Domain, DkimState.PollPending, selector1);

            DkimSelector selector2 = CreateSelector(Selector1, CreateRecordWithEmptyList(Dkim1));

            state.UpdateRecords(new List <DkimSelector> {
                selector2
            }, DateTime.UtcNow);

            DkimEvaluationUpdated evaluationUpdated = state.UpdateEvaluations(DateTime.UtcNow);

            Assert.AreEqual(evaluationUpdated.DkimEvaluationResults[0].Records[0].EvaluationMessages.Count, 0);
        }
        public async Task HandleAggregateReportDomainDkimSelectorsCantUpdateDoesNotContinue()
        {
            DkimEntityState dkimEntityState = A.Fake <DkimEntityState>();

            A.CallTo(() => dkimEntityState.Id).Returns(Id);
            A.CallTo(() => dkimEntityState.CanUpdate(A <string> ._, A <DateTime> ._)).Returns(false);

            DkimSelectorsUpdated dkimSelectorsUpdated;

            A.CallTo(() => dkimEntityState.UpdateSelectors(null, out dkimSelectorsUpdated)).WithAnyArguments().Returns(false);

            A.CallTo(() => _dkimEntityDao.Get(Id)).Returns(dkimEntityState);

            await _dkimEntity.Handle(new DkimSelectorsSeen("", "", Id, new List <string>()));

            A.CallTo(() => _dkimEntityDao.Save(A <DkimEntityState> ._)).MustNotHaveHappened();
            A.CallTo(() => _dispatcher.Dispatch(A <Common.Messaging.Abstractions.Message> ._, A <string> ._)).MustNotHaveHappened();
        }
        public async Task HandleAggregateReportDomainDkimSelectorsStateExistsAndEventCausesUpdate()
        {
            DkimEntityState dkimEntityState = A.Fake <DkimEntityState>();

            A.CallTo(() => dkimEntityState.Id).Returns(Id);
            A.CallTo(() => dkimEntityState.CanUpdate(A <string> ._, A <DateTime> ._)).Returns(true);

            DkimSelectorsUpdated dkimSelectorsUpdated;

            A.CallTo(() => dkimEntityState.UpdateSelectors(null, out dkimSelectorsUpdated)).WithAnyArguments().Returns(true);

            A.CallTo(() => _dkimEntityDao.Get(Id)).Returns(dkimEntityState);

            await _dkimEntity.Handle(new DkimSelectorsSeen("", "", Id, new List <string>()));

            A.CallTo(() => _dkimEntityDao.Save(A <DkimEntityState> ._)).MustHaveHappenedOnceExactly();
            A.CallTo(() => _dkimEntityDao.Save(A <DkimEntityState> ._)).MustHaveHappenedOnceExactly();
        }
Example #16
0
        public void UpdateRecordsWhenNoRecordsFromPollUpdatesErrorWhenNoErrorsExist()
        {
            DkimSelector selector1 = CreateSelector(Selector1);

            DkimEntityState state = CreateState(Domain, DkimState.PollPending, selector1);

            DkimSelector selector3 = new DkimSelector(Selector1, new List <DkimRecord>(), null);

            state.UpdateRecords(new List <DkimSelector> {
                selector3
            }, DateTime.UtcNow);

            Assert.That(state.Selectors[0].Selector, Is.EqualTo(selector3.Selector));

            Assert.That(state.Selectors.Count, Is.EqualTo(1));
            Assert.That(state.Selectors[0].Records.Count, Is.EqualTo(0));
            StringAssert.StartsWith("Didn't find any DNS TXT", state.Selectors[0].PollError.Text);
        }
        public async Task InsertStateAndRetrieveStateCorrectly()
        {
            DkimState dkimState = DkimState.Evaluated;

            DkimEntityState state = new DkimEntityState(Id, 1, dkimState, DateTime.UtcNow, DateTime.UtcNow, DateTime.UtcNow,
                                                        new List <DkimSelector> {
                new DkimSelector(Selector1)
            });

            await _dao.Save(state);

            DkimEntityState stateFromDatabase = await _dao.Get(Id);

            Assert.That(stateFromDatabase.Id, Is.EqualTo(state.Id));
            Assert.That(stateFromDatabase.Version, Is.EqualTo(state.Version));
            Assert.That(stateFromDatabase.Selectors.Count, Is.EqualTo(1));
            Assert.That(stateFromDatabase.Selectors[0].Selector, Is.EqualTo(Selector1));
            Assert.That(stateFromDatabase.State, Is.EqualTo(dkimState));
        }
Example #18
0
        public void UpdateRecordsClearsExistingRecordsAndUpdatesWithNewOnes()
        {
            DkimSelector selector1 = CreateSelector(Selector1);
            DkimSelector selector2 = CreateSelector(Selector2);

            DkimEntityState state = CreateState(Domain, DkimState.PollPending, selector1, selector2);

            DkimSelector selector3 = CreateSelector(Selector1, CreateRecord(Dkim1));

            state.UpdateRecords(new List <DkimSelector> {
                selector3
            }, DateTime.UtcNow);

            Assert.That(state.Selectors[0].Selector, Is.EqualTo(selector3.Selector));

            Assert.That(state.Selectors.Count, Is.EqualTo(1));
            Assert.That(state.Selectors[0].Records.Count, Is.EqualTo(1));
            Assert.That(state.Selectors[0].Records[0].DnsRecord.Record, Is.EqualTo(Dkim1));
            Assert.That(state.Selectors[0].PollError, Is.Null);
        }
        public async Task HandleDkimRecordsPolledCantUpdateDoesNotContinue()
        {
            DkimEntityState dkimEntityState = A.Fake <DkimEntityState>();

            A.CallTo(() => dkimEntityState.Id).Returns(Id);
            A.CallTo(() => dkimEntityState.CanUpdate(A <string> ._, A <DateTime> ._)).Returns(false);

            DkimSelectorsUpdated dkimSelectorsUpdated;

            A.CallTo(() => dkimEntityState.UpdateSelectors(null, out dkimSelectorsUpdated)).WithAnyArguments().Returns(false);

            A.CallTo(() => _dkimEntityDao.Get(Id)).Returns(dkimEntityState);

            await _dkimEntity.Handle(new DkimRecordEvaluationResult(Id, new List <DkimSelectorResult>()));

            A.CallTo(() => _changeNotifierComposite.Handle(A <DkimEntityState> ._, A <Message> ._)).MustNotHaveHappened();
            A.CallTo(() => _domainStatusPublisher.Publish(A <DkimRecordEvaluationResult> ._)).MustNotHaveHappened();
            A.CallTo(() => _dkimEntityDao.Save(A <DkimEntityState> ._)).MustNotHaveHappened();
            A.CallTo(() => _dispatcher.Dispatch(A <Common.Messaging.Abstractions.Message> ._, A <string> ._)).MustNotHaveHappened();
        }
        public async Task HandleDkimRecordEvaluationResultRemovesPolledErrorSelectors()
        {
            DkimRecordEvaluationResult message = new DkimRecordEvaluationResult(
                Id, new List <DkimSelectorResult>
            {
                new DkimSelectorResult(new Contracts.SharedDomain.DkimSelector("selector", null, null, new Contracts.SharedDomain.Message(Guid.Empty, Contracts.SharedDomain.MessageType.error, "error", "markdown")), new List <RecordResult>())
            });

            message.Timestamp = DateTime.UtcNow;

            DkimEntityState dkimEntityState = new DkimEntityState(Id, 1, DkimState.PollPending, DateTime.Now, null, null, new List <DkimSelector>());

            dkimEntityState.Selectors.Add(new DkimSelector("selector1"));

            A.CallTo(() => _dkimEntityDao.Get(Id)).Returns(dkimEntityState);

            await _dkimEntity.Handle(message);

            A.CallTo(() => _dkimEntityDao.Save(A <DkimEntityState> .That.Matches(x => x.Selectors.Count == 0))).MustHaveHappenedOnceExactly();
        }
Example #21
0
        public void UpdateSelectorsWhenNoNewSelectorsNothingChanges()
        {
            DkimEntityState state = CreateState(Domain, DkimState.Evaluated);

            DkimSelector selector1 = CreateSelector(Selector1);
            DkimSelector selector2 = CreateSelector(Selector2);

            state.UpdateSelectors(new List <DkimSelector> {
                selector1, selector2
            }, out DkimSelectorsUpdated _);

            bool updated = state.UpdateSelectors(new List <DkimSelector> {
                selector1, selector2
            }, out _);

            Assert.That(updated, Is.False);

            Assert.That(state.Selectors.Count == 2);
            Assert.That(state.Selectors[0], Is.EqualTo(selector1));
            Assert.That(state.Selectors[1], Is.EqualTo(selector2));
        }
        public async Task Save(DkimEntityState state)
        {
            string connectionString = await _connectionInfoAsync.GetConnectionStringAsync();

            string serializedState = JsonConvert.SerializeObject(state, new JsonSerializerSettings
            {
                ContractResolver = new CamelCasePropertyNamesContractResolver()
            });

            int rowsAffected = await MySqlHelper.ExecuteNonQueryAsync(connectionString,
                                                                      DkimEntityDaoResources.InsertDkimEntity,
                                                                      new MySqlParameter("domain", state.Id),
                                                                      new MySqlParameter("version", state.Version),
                                                                      new MySqlParameter("state", serializedState));

            if (rowsAffected == 0)
            {
                throw new InvalidOperationException(
                          $"Didn't update DkimEntityState because version {state.Version} has already been persisted.");
            }
        }
Example #23
0
        public void DoesNotNotifyWhenNoChanges()
        {
            Dictionary <string, List <string> > selectorsAndRecords1 = new Dictionary <string, List <string> >
            {
                { "selector1", new List <string> {
                      "record"
                  } }
            };
            Dictionary <string, List <string> > selectorsAndRecords2 = new Dictionary <string, List <string> >
            {
                { "selector1", new List <string> {
                      "record"
                  } }
            };

            DkimEntityState   state   = CreateDkimEntityState(selectorsAndRecords1);
            DkimRecordsPolled message = CreateDkimRecordsPolled(selectorsAndRecords2);

            _recordChangedNotifier.Handle(state, message);

            A.CallTo(() => _messageDispatcher.Dispatch(A <Common.Messaging.Abstractions.Message> .Ignored, A <string> .Ignored)).MustNotHaveHappened();
        }
        public async Task HandleDkimRecordEvaluationResultUpdatesPolledSelectors()
        {
            DkimRecordEvaluationResult message = new DkimRecordEvaluationResult(
                Id, new List <DkimSelectorResult>
            {
                new DkimSelectorResult(new Contracts.SharedDomain.DkimSelector("selector1", null), new List <RecordResult> {
                    new RecordResult("record1", new List <DkimEvaluatorMessage>())
                })
            });

            message.Timestamp = DateTime.UtcNow;

            DkimEntityState dkimEntityState = new DkimEntityState(Id, 1, DkimState.PollPending, DateTime.Now, null, null, new List <DkimSelector>());

            dkimEntityState.Selectors.Add(new DkimSelector("selector1"));

            A.CallTo(() => _dkimEntityDao.Get(Id)).Returns(dkimEntityState);

            await _dkimEntity.Handle(message);

            A.CallTo(() => _dkimEntityDao.Save(A <DkimEntityState> .That.Matches(x => x.Selectors.Count == 1 && x.Selectors[0].Selector == "selector1"))).MustHaveHappenedOnceExactly();
        }
        public void Handle(DkimEntityState state, Common.Messaging.Abstractions.Message message)
        {
            if (message is DkimRecordEvaluationResult evaluationResult)
            {
                List <DkimSelector> newRecordsIgnoringEmptyResults = evaluationResult.ToDkimSelectors().Where(x => x.Records != null).ToList();

                List <SelectorMessage> currentSelectorMessages = CreateFlattenedSelectorMessages(state.Selectors);
                List <SelectorMessage> newSelectorMessages     = CreateFlattenedSelectorMessages(newRecordsIgnoringEmptyResults);

                List <SelectorMessages> addedMessages = CreateExclusiveSelectorMessages(newSelectorMessages, currentSelectorMessages);
                if (addedMessages.Any())
                {
                    DkimAdvisoryAdded dkimAdvisoryAdded = new DkimAdvisoryAdded(state.Id, addedMessages);
                    _dispatcher.Dispatch(dkimAdvisoryAdded, _dkimEntityConfig.SnsTopicArn);
                }

                List <SelectorMessages> removedMessages = CreateExclusiveSelectorMessages(currentSelectorMessages, newSelectorMessages);
                if (removedMessages.Any())
                {
                    DkimAdvisoryRemoved dkimAdvisoryRemoved = new DkimAdvisoryRemoved(state.Id, removedMessages);
                    _dispatcher.Dispatch(dkimAdvisoryRemoved, _dkimEntityConfig.SnsTopicArn);
                }
            }
        }
        public void Handle(DkimEntityState state, Common.Messaging.Abstractions.Message message)
        {
            if (message is DkimRecordsPolled polled)
            {
                List <SelectorRecord> currentSelectorRecords = state.Selectors
                                                               .SelectMany(x => x.Records, (y, z) => new SelectorRecord(y.Selector, z.DnsRecord.Record)).ToList();

                List <SelectorRecord> newSelectorRecords = polled.DkimSelectorRecords
                                                           .Where(x => x.Records != null)
                                                           .SelectMany(x => x.Records, (y, z) => new SelectorRecord(y.Selector, z.Record)).ToList();

                List <SelectorRecord> addedRecords = newSelectorRecords.Except(currentSelectorRecords, _selectorRecordEqualityComparer).ToList();
                if (addedRecords.Any())
                {
                    List <SelectorRecords> selectorRecords = addedRecords
                                                             .GroupBy(x => x.Selector, y => y.Record)
                                                             .Select(x => new SelectorRecords(x.Key, x.ToList()))
                                                             .ToList();

                    DkimRecordAdded dkimDnsRecordAdded = new DkimRecordAdded(state.Id, selectorRecords);
                    _dispatcher.Dispatch(dkimDnsRecordAdded, _dkimEntityConfig.SnsTopicArn);
                }

                List <SelectorRecord> removedRecords = currentSelectorRecords.Except(newSelectorRecords, _selectorRecordEqualityComparer).ToList();
                if (removedRecords.Any())
                {
                    List <SelectorRecords> selectorRecords = removedRecords
                                                             .GroupBy(x => x.Selector, y => y.Record)
                                                             .Select(x => new SelectorRecords(x.Key, x.ToList()))
                                                             .ToList();

                    DkimRecordRemoved dkimDnsRecordRemoved = new DkimRecordRemoved(state.Id, selectorRecords);
                    _dispatcher.Dispatch(dkimDnsRecordRemoved, _dkimEntityConfig.SnsTopicArn);
                }
            }
        }
        public async Task GetNoStateExistsReturnsNull()
        {
            DkimEntityState state = await _dao.Get(Id);

            Assert.That(state, Is.Null);
        }