public async Task CanGroupSyncIntoEmptyGroup()
        {
            await _groupRepo.AddUsersToGroup(_testUsers, _sourceGroup);

            await Task.Delay(_waitForGraph);             // Sometimes you have to wait for the graph to catch up.

            var sourceGroupMembers = await _groupRepo.GetUsersInGroupTransitively(_sourceGroup.ObjectId);

            Assert.AreEqual(UserCount, _testUsers.Length);
            Assert.IsTrue(SequencesMatch(_testUsers, sourceGroupMembers));

            var destinationGroupMembers = await _groupRepo.GetUsersInGroupTransitively(_destinationGroup.ObjectId);

            Assert.AreEqual(0, destinationGroupMembers.Count);

            var calc            = new MembershipDifferenceCalculator <AzureADUser>();
            var membershipDelta = calc.CalculateDifference(sourceGroupMembers, destinationGroupMembers);

            Assert.IsTrue(SequencesMatch(_testUsers, membershipDelta.ToAdd));
            Assert.AreEqual(0, membershipDelta.ToRemove.Count);

            await _groupRepo.AddUsersToGroup(membershipDelta.ToAdd, _destinationGroup);

            await _groupRepo.RemoveUsersFromGroup(membershipDelta.ToRemove, _destinationGroup);

            await Task.Delay(_waitForGraph);

            destinationGroupMembers = await _groupRepo.GetUsersInGroupTransitively(_destinationGroup.ObjectId);

            Assert.IsTrue(SequencesMatch(_testUsers, destinationGroupMembers));
        }
        public async Task CanGroupSyncIntoNonEmptyGroup()
        {
            var initialSourceUsers       = _testUsers.Where((_, idx) => idx % 2 == 0).ToArray();
            var inititalDestinationUsers = _testUsers.Where((_, idx) => idx % 2 == 1).ToArray();

            await _groupRepo.AddUsersToGroup(initialSourceUsers, _sourceGroup);

            await _groupRepo.AddUsersToGroup(inititalDestinationUsers, _destinationGroup);

            await Task.Delay(_waitForGraph);             // Sometimes you have to wait for the graph to catch up.

            var sourceGroupMembers = await _groupRepo.GetUsersInGroupTransitively(_sourceGroup.ObjectId);

            Assert.IsTrue(SequencesMatch(initialSourceUsers, sourceGroupMembers));

            var destinationGroupMembers = await _groupRepo.GetUsersInGroupTransitively(_destinationGroup.ObjectId);

            Assert.IsTrue(SequencesMatch(inititalDestinationUsers, destinationGroupMembers));

            var calc            = new MembershipDifferenceCalculator <AzureADUser>();
            var membershipDelta = calc.CalculateDifference(sourceGroupMembers, destinationGroupMembers);

            Assert.IsTrue(SequencesMatch(initialSourceUsers, membershipDelta.ToAdd));
            Assert.IsTrue(SequencesMatch(inititalDestinationUsers, membershipDelta.ToRemove));

            await _groupRepo.AddUsersToGroup(membershipDelta.ToAdd, _destinationGroup);

            await _groupRepo.RemoveUsersFromGroup(membershipDelta.ToRemove, _destinationGroup);

            await Task.Delay(_waitForGraph);             // Sometimes you have to wait for the graph to catch up.

            destinationGroupMembers = await _groupRepo.GetUsersInGroupTransitively(_destinationGroup.ObjectId);

            Assert.IsTrue(SequencesMatch(initialSourceUsers, destinationGroupMembers));
        }
        public async Task CanGroupSyncWithNestedGroups()
        {
            // the bottom half is in the source
            // the upper two thirds are in the destination
            var initialSourceUsers      = _testUsers.Where((_, idx) => idx < (UserCount / 2)).ToArray();
            var initialDestinationUsers = _testUsers.Where((_, idx) => idx > (UserCount / 3)).ToArray();

            await _groupRepo.AddUsersToGroup(initialDestinationUsers, _destinationGroup);

            const int testGroupCount = 5;
            var       testGroups     = new AzureADGroup[testGroupCount];

            for (int i = 0; i < testGroupCount; i++)
            {
                testGroups[i] = await CreateOrClearGroup(
                    new Group { DisplayName = $"TestSourceGroup{i}", MailEnabled = false, MailNickname = $"testsourcegroup{i}", SecurityEnabled = true });

                _groupsToRemove.Add(testGroups[i]);

                // add all the users to the first group, every other user to the second group, etc.
                await _groupRepo.AddUsersToGroup(initialSourceUsers.Where((_, idx) => idx % (i + 1) == 0), testGroups[i]);

                await _graphServiceClient.Groups[_sourceGroup.ObjectId.ToString()].Members.References.Request().AddAsync(new DirectoryObject {
                    Id = testGroups[i].ObjectId.ToString()
                });
            }

            await Task.Delay(_waitForGraph);             // Sometimes you have to wait for the graph to catch up.

            var sourceGroupMembers = await _groupRepo.GetUsersInGroupTransitively(_sourceGroup.ObjectId);

            Assert.IsTrue(SequencesMatch(initialSourceUsers, sourceGroupMembers));

            var destinationGroupMembers = await _groupRepo.GetUsersInGroupTransitively(_destinationGroup.ObjectId);

            Assert.IsTrue(SequencesMatch(initialDestinationUsers, destinationGroupMembers));

            var calc            = new MembershipDifferenceCalculator <AzureADUser>();
            var membershipDelta = calc.CalculateDifference(sourceGroupMembers, destinationGroupMembers);

            // Have to add the bottom third and remove the top half
            Assert.IsTrue(SequencesMatch(_testUsers.Where((_, idx) => idx <= (UserCount / 3)), membershipDelta.ToAdd));
            Assert.IsTrue(SequencesMatch(_testUsers.Where((_, idx) => idx >= (UserCount / 2)), membershipDelta.ToRemove));

            await _groupRepo.AddUsersToGroup(membershipDelta.ToAdd, _destinationGroup);

            await _groupRepo.RemoveUsersFromGroup(membershipDelta.ToRemove, _destinationGroup);

            await Task.Delay(_waitForGraph);             // Sometimes you have to wait for the graph to catch up.

            destinationGroupMembers = await _groupRepo.GetUsersInGroupTransitively(_destinationGroup.ObjectId);

            Assert.IsTrue(SequencesMatch(initialSourceUsers, destinationGroupMembers));
        }
        public void HandlesDisjointGroups()
        {
            var source      = new[] { _help.UserNamed(1), _help.UserNamed(2), _help.UserNamed(3) };
            var destination = new[] { _help.UserNamed(4), _help.UserNamed(5) };

            var result = _calc.CalculateDifference(source, destination);

            CollectionAssert.AreEquivalent(source, result.ToAdd.ToArray());
            CollectionAssert.AreEquivalent(destination, result.ToRemove.ToArray());
        }
        public async Task CanGroupSyncIntoOverlappingGroup()
        {
            // the bottom half is in the source
            // the upper two thirds are in the destination
            var initialSourceUsers      = _testUsers.Where((_, idx) => idx < (UserCount / 2)).ToArray();
            var initialDestinationUsers = _testUsers.Where((_, idx) => idx > (UserCount / 3)).ToArray();

            await _groupRepo.AddUsersToGroup(initialSourceUsers, _sourceGroup);

            await _groupRepo.AddUsersToGroup(initialDestinationUsers, _destinationGroup);

            await Task.Delay(_waitForGraph);             // Sometimes you have to wait for the graph to catch up.

            var sourceGroupMembers = await _groupRepo.GetUsersInGroupTransitively(_sourceGroup.ObjectId);

            Assert.IsTrue(SequencesMatch(initialSourceUsers, sourceGroupMembers));

            var destinationGroupMembers = await _groupRepo.GetUsersInGroupTransitively(_destinationGroup.ObjectId);

            Assert.IsTrue(SequencesMatch(initialDestinationUsers, destinationGroupMembers));

            var calc            = new MembershipDifferenceCalculator <AzureADUser>();
            var membershipDelta = calc.CalculateDifference(sourceGroupMembers, destinationGroupMembers);

            // Have to add the bottom third and remove the top half
            Assert.IsTrue(SequencesMatch(_testUsers.Where((_, idx) => idx <= (UserCount / 3)), membershipDelta.ToAdd));
            Assert.IsTrue(SequencesMatch(_testUsers.Where((_, idx) => idx >= (UserCount / 2)), membershipDelta.ToRemove));

            await _groupRepo.AddUsersToGroup(membershipDelta.ToAdd, _destinationGroup);

            await _groupRepo.RemoveUsersFromGroup(membershipDelta.ToRemove, _destinationGroup);

            await Task.Delay(_waitForGraph);             // Sometimes you have to wait for the graph to catch up.

            destinationGroupMembers = await _groupRepo.GetUsersInGroupTransitively(_destinationGroup.ObjectId);

            Assert.IsTrue(SequencesMatch(initialSourceUsers, destinationGroupMembers));
        }