public void SyncsGroupsCorrectly()
        {
            var mockGroups       = new MockGraphGroupRepository();
            var mockSyncJobs     = new MockSyncJobRepository();
            var mockLogs         = new MockLoggingRepository();
            var mockMails        = new MockMailRepository();
            var mockGraph        = new MockGraphGroupRepository();
            var mockEmail        = new MockEmail <IEmailSenderRecipient>();
            var updater          = new GraphUpdaterApplication(new MembershipDifferenceCalculator <AzureADUser>(), mockGroups, mockSyncJobs, mockLogs, mockMails, mockGraph, mockEmail);
            var sessionCollector = new SessionMessageCollector(updater);

            var mockSession = new MockMessageSession()
            {
                SessionId = "someId"
            };

            var syncJobKeys = (Guid.NewGuid().ToString(), Guid.NewGuid().ToString());

            var syncJob = new SyncJob(syncJobKeys.Item1, syncJobKeys.Item2)
            {
                Enabled = true,
                Status  = "InProgress",
            };

            mockSyncJobs.ExistingSyncJobs.Add(syncJobKeys, syncJob);

            var incomingMessages = MakeMembershipMessages();

            mockGroups.GroupsToUsers.Add(incomingMessages.First().Body.Destination.ObjectId, new List <AzureADUser>()
            {
                new AzureADUser {
                    ObjectId = Guid.NewGuid()
                }
            });

            foreach (var message in incomingMessages)
            {
                message.Body.SyncJobPartitionKey = syncJobKeys.Item1;
                message.Body.SyncJobRowKey       = syncJobKeys.Item2;
            }

            foreach (var message in incomingMessages.SkipLast(1))
            {
                sessionCollector.HandleNewMessage(message, mockSession);

                // sessionCollector doesn't do anything until it gets the last message.
                Assert.AreEqual(0, mockLogs.MessagesLogged);
                Assert.IsFalse(mockSession.Closed);
                Assert.AreEqual(0, mockSession.CompletedLockTokens.Count);
            }

            sessionCollector.HandleNewMessage(incomingMessages.Last(), mockSession);

            Assert.IsTrue(mockSession.Closed);
            Assert.AreEqual(9, mockLogs.MessagesLogged);
            Assert.AreEqual("Idle", syncJob.Status);
            Assert.IsTrue(syncJob.Enabled);
            Assert.AreEqual(1, mockGroups.GroupsToUsers.Count);
            Assert.AreEqual(MockGroupMembershipHelper.UserCount, mockGroups.GroupsToUsers.Values.Single().Count);
        }
        public async Task RemoveUsersToGroupInDryRunMode()
        {
            var mockLogs            = new MockLoggingRepository();
            var telemetryClient     = new TelemetryClient(TelemetryConfiguration.CreateDefault());
            var mockGraphGroup      = new MockGraphGroupRepository();
            var mockMail            = new MockMailRepository();
            var mailSenders         = new EmailSenderRecipient("*****@*****.**", "fake_pass", "*****@*****.**", "*****@*****.**", "*****@*****.**");
            var mockSynJobs         = new MockSyncJobRepository();
            var dryRun              = new DryRunValue(true);
            var graphUpdaterService = new GraphUpdaterService(mockLogs, telemetryClient, mockGraphGroup, mockMail, mailSenders, mockSynJobs, dryRun);

            var  runId         = Guid.NewGuid();
            var  groupId       = Guid.NewGuid();
            bool isInitialSync = false;

            var newUsers  = new List <AzureADUser>();
            var userCount = 10;

            for (int i = 0; i < userCount; i++)
            {
                newUsers.Add(new AzureADUser {
                    ObjectId = Guid.NewGuid()
                });
            }

            mockGraphGroup.GroupsToUsers.Add(groupId, newUsers);

            var status = await graphUpdaterService.RemoveUsersFromGroupAsync(newUsers.Take(5).ToList(), groupId, runId, isInitialSync);

            Assert.AreEqual(GraphUpdaterStatus.Ok, status.Status);
            Assert.AreEqual(newUsers.Count, mockGraphGroup.GroupsToUsers[groupId].Count);
        }
예제 #3
0
        public async Task ProperlyGetsAndSendsMembership()
        {
            const int userCount        = 2500213;
            Guid      sourceGroup      = Guid.NewGuid();
            Guid      destinationGroup = Guid.NewGuid();
            var       initialUsers     = Enumerable.Range(0, userCount).Select(
                x => new AzureADUser {
                ObjectId = Guid.NewGuid()
            }).ToList();

            var graphRepo = new MockGraphGroupRepository()
            {
                GroupsToUsers = new Dictionary <Guid, List <AzureADUser> > {
                    { sourceGroup, initialUsers }
                }
            };
            var serviceBus = new MockMembershipServiceBusRepository();

            var calc = new SGMembershipCalculator(graphRepo, serviceBus, new MockLogger());

            await calc.SendMembership(new SyncJob
            {
                TargetOfficeGroupId = destinationGroup,
                Query = sourceGroup.ToString()
            });

            CollectionAssert.AreEqual(initialUsers, serviceBus.Sent.SourceMembers);
            Assert.AreEqual(sourceGroup, serviceBus.Sent.Sources.Single().ObjectId);
            Assert.AreEqual(destinationGroup, serviceBus.Sent.Destination.ObjectId);
        }
        public async Task UpdateSyncJobStatusDryRunModeTest()
        {
            var mockLogs        = new MockLoggingRepository();
            var telemetryClient = new TelemetryClient(TelemetryConfiguration.CreateDefault());
            var mockGraphGroup  = new MockGraphGroupRepository();
            var mockMail        = new MockMailRepository();
            var mailSenders     = new EmailSenderRecipient("*****@*****.**", "fake_pass", "*****@*****.**", "*****@*****.**", "*****@*****.**");
            var mockSynJobs     = new MockSyncJobRepository();
            var dryRun          = new DryRunValue(false);

            var graphUpdaterService = new GraphUpdaterService(mockLogs, telemetryClient, mockGraphGroup, mockMail, mailSenders, mockSynJobs, dryRun);

            var runId       = Guid.NewGuid();
            var lastRunTime = DateTime.UtcNow.AddDays(-1);
            var job         = new SyncJob {
                PartitionKey = "00-00-00", RowKey = Guid.NewGuid().ToString(), Status = SyncStatus.InProgress.ToString(), Enabled = true, DryRunTimeStamp = lastRunTime
            };

            mockSynJobs.ExistingSyncJobs.Add((job.PartitionKey, job.RowKey), job);

            await graphUpdaterService.UpdateSyncJobStatusAsync(job, SyncStatus.Idle, true, runId);

            Assert.AreEqual(SyncStatus.Idle.ToString(), job.Status);
            Assert.IsTrue(job.DryRunTimeStamp > lastRunTime);
            Assert.IsTrue(job.LastRunTime < lastRunTime);
            Assert.IsNotNull(job.RunId);
        }
예제 #5
0
 public void InitializeTest()
 {
     _syncJobRepository          = new MockSyncJobRepository();
     _loggingRepository          = new MockLoggingRepository();
     _serviceBusTopicsRepository = new MockServiceBusTopicsRepository();
     _graphGroupRepository       = new MockGraphGroupRepository();
     _syncJobTopicsService       = new SyncJobTopicsService(_loggingRepository, _syncJobRepository, _serviceBusTopicsRepository, _graphGroupRepository, new MockKeyVaultSecret <ISyncJobTopicService>());
 }
예제 #6
0
        public void HandlesErroredJobs()
        {
            var mockGroups       = new MockGraphGroupRepository();
            var mockSyncJobs     = new MockSyncJobRepository();
            var mockLogs         = new MockLoggingRepository();
            var updater          = new GraphUpdaterApplication(new MembershipDifferenceCalculator <AzureADUser>(), mockGroups, mockSyncJobs, mockLogs);
            var sessionCollector = new SessionMessageCollector(updater);

            var mockSession = new MockMessageSession()
            {
                SessionId = "someId"
            };

            var syncJobKeys = (Guid.NewGuid().ToString(), Guid.NewGuid().ToString());

            var syncJob = new SyncJob(syncJobKeys.Item1, syncJobKeys.Item2)
            {
                Enabled = true,
                Status  = "InProgress",
            };

            mockSyncJobs.ExistingSyncJobs.Add(syncJobKeys, syncJob);

            var incomingMessage = new GroupMembershipMessage
            {
                LockToken = "hi",
                Body      = new Entities.ServiceBus.GroupMembership
                {
                    Errored = true,
                    Sources = new[] { new AzureADGroup {
                                          ObjectId = Guid.NewGuid()
                                      } },
                    Destination = new AzureADGroup {
                        ObjectId = Guid.NewGuid()
                    },
                    IsLastMessage       = true,
                    RunId               = Guid.NewGuid(),
                    SourceMembers       = new List <AzureADUser>(),
                    SyncJobPartitionKey = syncJobKeys.Item1.ToString(),
                    SyncJobRowKey       = syncJobKeys.Item2.ToString()
                }
            };

            mockGroups.GroupsToUsers.Add(incomingMessage.Body.Destination.ObjectId, new List <AzureADUser>()
            {
                new AzureADUser {
                    ObjectId = Guid.NewGuid()
                }
            });

            sessionCollector.HandleNewMessage(incomingMessage, mockSession);

            Assert.IsTrue(mockSession.Closed);
            Assert.AreEqual("Error", syncJob.Status);
            Assert.IsFalse(syncJob.Enabled);
            Assert.AreEqual(1, mockGroups.GroupsToUsers.Count);
            Assert.AreEqual(1, mockGroups.GroupsToUsers.Values.Single().Count);
        }
예제 #7
0
 public void InitializeTest()
 {
     _syncJobRepository          = new MockSyncJobRepository();
     _loggingRepository          = new MockLoggingRepository();
     _serviceBusTopicsRepository = new MockServiceBusTopicsRepository();
     _graphGroupRepository       = new MockGraphGroupRepository();
     _mailRepository             = new MockMailRepository();
     _jobTriggerService          = new JobTriggerService(_loggingRepository, _syncJobRepository, _serviceBusTopicsRepository, _graphGroupRepository, new MockKeyVaultSecret <IJobTriggerService>(), _mailRepository, new MockEmail <IEmailSenderRecipient>());
 }
예제 #8
0
        public async Task ProperlyGetsAndSendsMembership(int getGroupExceptions, int getMembersExceptions)
        {
            const int userCount   = 2500213;
            var       allUsers    = new List <AzureADUser> {
            };
            Guid sourceGroup      = Guid.NewGuid();
            Guid destinationGroup = Guid.NewGuid();
            var  initialUsers     = Enumerable.Range(0, userCount).Select(
                x => new AzureADUser {
                ObjectId = Guid.NewGuid()
            }).ToList();

            var graphRepo = new MockGraphGroupRepository()
            {
                GroupsToUsers = new Dictionary <Guid, List <AzureADUser> > {
                    { sourceGroup, initialUsers }
                },
                ThrowSocketExceptionsFromGroupExistsBeforeSuccess     = getGroupExceptions,
                ThrowSocketExceptionsFromGetUsersInGroupBeforeSuccess = getMembersExceptions
            };
            var serviceBus    = new MockMembershipServiceBusRepository();
            var mail          = new MockMailRepository();
            var mailAddresses = new MockEmail <IEmailSenderRecipient>();
            var syncJobs      = new MockSyncJobRepository();
            var dryRun        = new MockDryRunValue()
            {
                DryRunEnabled = false
            };

            var calc = new SGMembershipCalculator(graphRepo, serviceBus, mail, mailAddresses, syncJobs, new MockLoggingRepository(), dryRun);

            var testJob = new SyncJob
            {
                RowKey              = "row",
                PartitionKey        = "partition",
                TargetOfficeGroupId = destinationGroup,
                Query  = sourceGroup.ToString(),
                Status = "InProgress"
            };

            syncJobs.ExistingSyncJobs.Add((testJob.RowKey, testJob.PartitionKey), testJob);

            var groups = calc.ReadSourceGroups(testJob);
            await calc.SendMembershipAsync(testJob, Guid.NewGuid(), allUsers);

            foreach (var group in groups)
            {
                var groupExistsResult = await calc.GroupExistsAsync(group.ObjectId, Guid.NewGuid());

                Assert.AreEqual(OutcomeType.Successful, groupExistsResult.Outcome);
                Assert.AreEqual(true, groupExistsResult.Result);
            }
        }
예제 #9
0
        public void IgnoresMissingDestinationGroup()
        {
            var mockGroups       = new MockGraphGroupRepository();
            var mockSyncJobs     = new MockSyncJobRepository();
            var mockLogs         = new MockLoggingRepository();
            var updater          = new GraphUpdaterApplication(new MembershipDifferenceCalculator <AzureADUser>(), mockGroups, mockSyncJobs, mockLogs);
            var sessionCollector = new SessionMessageCollector(updater);

            var mockSession = new MockMessageSession()
            {
                SessionId = "someId"
            };

            var syncJobKeys = (Guid.NewGuid().ToString(), Guid.NewGuid().ToString());

            var syncJob = new SyncJob(syncJobKeys.Item1, syncJobKeys.Item2)
            {
                Enabled = true,
                Status  = "InProgress",
            };

            mockSyncJobs.ExistingSyncJobs.Add(syncJobKeys, syncJob);

            var incomingMessages = MakeMembershipMessages();

            foreach (var message in incomingMessages)
            {
                message.Body.SyncJobPartitionKey = syncJobKeys.Item1;
                message.Body.SyncJobRowKey       = syncJobKeys.Item2;
            }

            foreach (var message in incomingMessages.SkipLast(1))
            {
                sessionCollector.HandleNewMessage(message, mockSession);

                // sessionCollector doesn't do anything until it gets the last message.
                Assert.AreEqual(0, mockLogs.MessagesLogged);
                Assert.IsFalse(mockSession.Closed);
                Assert.AreEqual(0, mockSession.CompletedLockTokens.Count);
            }

            sessionCollector.HandleNewMessage(incomingMessages.Last(), mockSession);

            Assert.IsTrue(mockSession.Closed);
            Assert.AreEqual(4, mockLogs.MessagesLogged);
            Assert.AreEqual("Error", syncJob.Status);
            Assert.IsFalse(syncJob.Enabled);
            Assert.AreEqual(0, mockGroups.GroupsToUsers.Count);
        }
예제 #10
0
        public async Task ProperlyGetsAndSendsMembershipWithMultipleSources()
        {
            const int userCount = 2500213;

            Guid[] sourceGroups     = Enumerable.Range(0, 5).Select(_ => Guid.NewGuid()).ToArray();
            Guid   destinationGroup = Guid.NewGuid();

            var mockGroups = new Dictionary <Guid, List <AzureADUser> >();

            for (int i = 0; i < userCount; i++)
            {
                var currentGroup = sourceGroups[i % sourceGroups.Length];
                var userToAdd    = new AzureADUser {
                    ObjectId = Guid.NewGuid()
                };
                if (mockGroups.TryGetValue(currentGroup, out var users))
                {
                    users.Add(userToAdd);
                }
                else
                {
                    mockGroups.Add(currentGroup, new List <AzureADUser> {
                        userToAdd
                    });
                }
            }

            var graphRepo = new MockGraphGroupRepository()
            {
                GroupsToUsers = mockGroups
            };
            var serviceBus = new MockMembershipServiceBusRepository();

            var calc = new SGMembershipCalculator(graphRepo, serviceBus, new MockLogger());

            await calc.SendMembership(new SyncJob
            {
                TargetOfficeGroupId = destinationGroup,
                Query = string.Join(';', sourceGroups)
            });

            CollectionAssert.AreEquivalent(mockGroups.Values.SelectMany(x => x).ToArray(), serviceBus.Sent.SourceMembers);
            CollectionAssert.AreEqual(sourceGroups.Select(x => new AzureADGroup {
                ObjectId = x
            }).ToArray(), serviceBus.Sent.Sources);
            Assert.AreEqual(destinationGroup, serviceBus.Sent.Destination.ObjectId);
        }
예제 #11
0
        public async Task ProperlyErrorsOnNonexistentGroups()
        {
            const int userCount        = 2500213;
            var       sourceGroups     = Enumerable.Range(0, 5).Select(_ => Guid.NewGuid()).ToArray();
            Guid      destinationGroup = Guid.NewGuid();

            var mockGroups = new Dictionary <Guid, List <AzureADUser> >();

            for (int i = 0; i < userCount; i++)
            {
                var currentGroup = sourceGroups[i % sourceGroups.Length];
                var userToAdd    = new AzureADUser {
                    ObjectId = Guid.NewGuid()
                };
                if (mockGroups.TryGetValue(currentGroup, out var users))
                {
                    users.Add(userToAdd);
                }
                else
                {
                    mockGroups.Add(currentGroup, new List <AzureADUser> {
                        userToAdd
                    });
                }
            }

            var graphRepo = new MockGraphGroupRepository()
            {
                GroupsToUsers = mockGroups
            };
            var serviceBus = new MockMembershipServiceBusRepository();

            var calc = new SGMembershipCalculator(graphRepo, serviceBus, new MockLogger());

            Guid nonexistentGroupId = Guid.NewGuid();
            await calc.SendMembership(new SyncJob
            {
                TargetOfficeGroupId = destinationGroup,
                Query = string.Join(';', sourceGroups) + $";{nonexistentGroupId}"
            });

            Assert.IsTrue(serviceBus.Sent.Errored);
            Assert.AreEqual(0, serviceBus.Sent.SourceMembers.Count);
        }
예제 #12
0
        public async Task ProperlyErrorsOnAllNonexistentGroups(int getGroupExceptions, int getMembersExceptions)
        {
            Guid[] sourceGroups     = Enumerable.Range(0, 5).Select(_ => Guid.NewGuid()).ToArray();
            Guid   destinationGroup = Guid.NewGuid();
            var    graphRepo        = new MockGraphGroupRepository()
            {
                GroupsToUsers = new Dictionary <Guid, List <AzureADUser> >(),
                ThrowSocketExceptionsFromGroupExistsBeforeSuccess     = getGroupExceptions,
                ThrowSocketExceptionsFromGetUsersInGroupBeforeSuccess = getMembersExceptions
            };
            var serviceBus    = new MockMembershipServiceBusRepository();
            var mail          = new MockMailRepository();
            var mailAddresses = new MockEmail <IEmailSenderRecipient>();
            var syncJobs      = new MockSyncJobRepository();
            var dryRun        = new MockDryRunValue()
            {
                DryRunEnabled = false
            };

            var calc = new SGMembershipCalculator(graphRepo, serviceBus, mail, mailAddresses, syncJobs, new MockLoggingRepository(), dryRun);

            var testJob = new SyncJob
            {
                RowKey              = "row",
                PartitionKey        = "partition",
                TargetOfficeGroupId = destinationGroup,
                Query  = string.Join(';', sourceGroups) + $";{Guid.NewGuid()}",
                Status = "InProgress"
            };

            syncJobs.ExistingSyncJobs.Add((testJob.RowKey, testJob.PartitionKey), testJob);

            var groups = calc.ReadSourceGroups(testJob);

            foreach (var group in groups)
            {
                var groupExistsResult = await calc.GroupExistsAsync(group.ObjectId, Guid.NewGuid());

                Assert.AreEqual(false, groupExistsResult.Result);
            }
            Assert.IsNull(serviceBus.Sent);
        }
        public async Task SendEmailTest()
        {
            var mockLogs        = new MockLoggingRepository();
            var telemetryClient = new TelemetryClient(TelemetryConfiguration.CreateDefault());
            var mockGraphGroup  = new MockGraphGroupRepository();
            var mockMail        = new MockMailRepository();
            var mailSenders     = new EmailSenderRecipient("*****@*****.**", "fake_pass", "*****@*****.**", "*****@*****.**", "*****@*****.**");
            var mockSynJobs     = new MockSyncJobRepository();
            var dryRun          = new DryRunValue(false);

            var graphUpdaterService = new GraphUpdaterService(mockLogs, telemetryClient, mockGraphGroup, mockMail, mailSenders, mockSynJobs, dryRun);

            var toEmail  = "user@domain";
            var template = "SampleTemplate";
            var runId    = Guid.NewGuid();
            await graphUpdaterService.SendEmailAsync(toEmail, template, new string[0] {
            }, runId);

            Assert.AreEqual(1, mockMail.SentEmails.Count);
        }
        public async Task GroupExistsTest()
        {
            var mockLogs        = new MockLoggingRepository();
            var telemetryClient = new TelemetryClient(TelemetryConfiguration.CreateDefault());
            var mockGraphGroup  = new MockGraphGroupRepository();
            var mockMail        = new MockMailRepository();
            var mailSenders     = new EmailSenderRecipient("*****@*****.**", "fake_pass", "*****@*****.**", "*****@*****.**", "*****@*****.**");
            var mockSynJobs     = new MockSyncJobRepository();
            var dryRun          = new DryRunValue(false);

            var graphUpdaterService = new GraphUpdaterService(mockLogs, telemetryClient, mockGraphGroup, mockMail, mailSenders, mockSynJobs, dryRun);

            var groupId = Guid.NewGuid();
            var runId   = Guid.NewGuid();

            mockGraphGroup.GroupsToUsers.Add(groupId, new List <AzureADUser>());

            var response = await graphUpdaterService.GroupExistsAsync(groupId, runId);

            Assert.IsTrue(response.Result);
        }
예제 #15
0
        public async Task ProperlyErrorsOnAllNonexistentGroups()
        {
            Guid[] sourceGroups     = Enumerable.Range(0, 5).Select(_ => Guid.NewGuid()).ToArray();
            Guid   destinationGroup = Guid.NewGuid();

            var graphRepo = new MockGraphGroupRepository()
            {
                GroupsToUsers = new Dictionary <Guid, List <AzureADUser> >()
            };
            var serviceBus = new MockMembershipServiceBusRepository();

            var calc = new SGMembershipCalculator(graphRepo, serviceBus, new MockLogger());

            await calc.SendMembership(new SyncJob
            {
                TargetOfficeGroupId = destinationGroup,
                Query = string.Join(';', sourceGroups) + $";{Guid.NewGuid()}"
            });

            Assert.IsTrue(serviceBus.Sent.Errored);
            Assert.AreEqual(0, serviceBus.Sent.SourceMembers.Count);
        }
예제 #16
0
        public async Task ProperlyGetsAndSendsMembershipWithMultipleSources(int getGroupExceptions, int getMembersExceptions)
        {
            const int userCount = 2500213;

            Guid[] sourceGroups     = Enumerable.Range(0, 5).Select(_ => Guid.NewGuid()).ToArray();
            Guid   destinationGroup = Guid.NewGuid();
            var    allUsers         = new List <AzureADUser> {
            };

            var mockGroups = new Dictionary <Guid, List <AzureADUser> >();

            for (int i = 0; i < userCount; i++)
            {
                var currentGroup = sourceGroups[i % sourceGroups.Length];
                var userToAdd    = new AzureADUser {
                    ObjectId = Guid.NewGuid()
                };
                if (mockGroups.TryGetValue(currentGroup, out var users))
                {
                    users.Add(userToAdd);
                }
                else
                {
                    mockGroups.Add(currentGroup, new List <AzureADUser> {
                        userToAdd
                    });
                }
            }

            var graphRepo = new MockGraphGroupRepository()
            {
                GroupsToUsers = mockGroups,
                ThrowSocketExceptionsFromGroupExistsBeforeSuccess     = getGroupExceptions,
                ThrowSocketExceptionsFromGetUsersInGroupBeforeSuccess = getMembersExceptions
            };
            var serviceBus    = new MockMembershipServiceBusRepository();
            var mail          = new MockMailRepository();
            var mailAddresses = new MockEmail <IEmailSenderRecipient>();
            var syncJobs      = new MockSyncJobRepository();
            var dryRun        = new MockDryRunValue()
            {
                DryRunEnabled = false
            };

            var calc = new SGMembershipCalculator(graphRepo, serviceBus, mail, mailAddresses, syncJobs, new MockLoggingRepository(), dryRun);

            var testJob = new SyncJob
            {
                RowKey              = "row",
                PartitionKey        = "partition",
                TargetOfficeGroupId = destinationGroup,
                Query  = string.Join(';', sourceGroups),
                Status = "InProgress"
            };

            syncJobs.ExistingSyncJobs.Add((testJob.RowKey, testJob.PartitionKey), testJob);

            var groups = calc.ReadSourceGroups(testJob);
            await calc.SendMembershipAsync(testJob, Guid.NewGuid(), allUsers);

            foreach (var group in groups)
            {
                var groupExistsResult = await calc.GroupExistsAsync(group.ObjectId, Guid.NewGuid());

                Assert.AreEqual(OutcomeType.Successful, groupExistsResult.Outcome);
                Assert.AreEqual(true, groupExistsResult.Result);
            }
        }
예제 #17
0
        public async Task IgnoresNonGuidArguments(int getGroupExceptions, int getMembersExceptions)
        {
            const int userCount = 2500213;

            Guid[] sourceGroups     = Enumerable.Range(0, 5).Select(_ => Guid.NewGuid()).ToArray();
            Guid   destinationGroup = Guid.NewGuid();

            var mockGroups = new Dictionary <Guid, List <AzureADUser> >();

            for (int i = 0; i < userCount; i++)
            {
                var currentGroup = sourceGroups[i % sourceGroups.Length];
                var userToAdd    = new AzureADUser {
                    ObjectId = Guid.NewGuid()
                };
                if (mockGroups.TryGetValue(currentGroup, out var users))
                {
                    users.Add(userToAdd);
                }
                else
                {
                    mockGroups.Add(currentGroup, new List <AzureADUser> {
                        userToAdd
                    });
                }
            }

            var graphRepo = new MockGraphGroupRepository()
            {
                GroupsToUsers = mockGroups,
                ThrowSocketExceptionsFromGroupExistsBeforeSuccess     = getGroupExceptions,
                ThrowSocketExceptionsFromGetUsersInGroupBeforeSuccess = getMembersExceptions
            };
            var serviceBus    = new MockMembershipServiceBusRepository();
            var mail          = new MockMailRepository();
            var mailAddresses = new MockEmail <IEmailSenderRecipient>();
            var syncJobs      = new MockSyncJobRepository();
            var dryRun        = new MockDryRunValue()
            {
                DryRunEnabled = false
            };

            var calc = new SGMembershipCalculator(graphRepo, serviceBus, mail, mailAddresses, syncJobs, new MockLoggingRepository(), dryRun);

            var testJob = new SyncJob
            {
                RowKey              = "row",
                PartitionKey        = "partition",
                TargetOfficeGroupId = destinationGroup,
                Query  = string.Join(';', sourceGroups) + ";nasdfasfd;;;",
                Status = "InProgress"
            };

            syncJobs.ExistingSyncJobs.Add((testJob.RowKey, testJob.PartitionKey), testJob);
            var groups = calc.ReadSourceGroups(testJob);

            foreach (var group in groups)
            {
                await calc.SendEmailAsync(testJob, Guid.NewGuid(), "Content", null);
            }
            Assert.IsNull(serviceBus.Sent);
        }