Exemple #1
0
        private ParseMessageResult HandleBan(IMeetingMessage msg)
        {
            ParseMessageResult result = ParseMessageResult.Success;

            Action <string> action = delegate(string user)
            {
                if (this.chairs.Contains(user))
                {
                    result = ParseMessageResult.CanNotDoThisToChair;
                }
                else
                {
                    if (this.bannedUsers.Contains(user) == false)
                    {
                        this.bannedUsers.Add(user);
                    }

                    PurgeUser(user);
                }
            };

            DoUserSplitAction(msg.CommandArgs, action);

            return(result);
        }
Exemple #2
0
        public void ChairWithSeveralWhitespacesTest()
        {
            const string             prefix   = "#chair";
            string                   args     = $"  {user1}      {user2}    ";
            const MeetingAction      action   = MeetingAction.Chair;
            const CommandRestriction restrict = CommandRestriction.ChairsOnly;

            IMeetingMessage msg = MakeMessage(
                prefix,
                args,
                action,
                restrict
                );

            // Owner is chair by default.
            ParseMessageResult result = this.uut.ParseMessage(msg, owner, this.testTime);

            // Should be a successful parse.
            Assert.AreEqual(ParseMessageResult.Success, result);
            Assert.AreEqual(1, uut.MeetingNotes.Count);
            CompareNotes(msg, uut.MeetingNotes.Last(), owner);

            // New chair should show up in list.
            Assert.AreEqual(3, uut.Chairs.Count());
            Assert.IsTrue(uut.Chairs.Contains(owner));
            Assert.IsTrue(uut.Chairs.Contains(user1));
            Assert.IsTrue(uut.Chairs.Contains(user2));

            // Other lists should be empty
            Assert.AreEqual(0, uut.SilencedUsers.Count);
            Assert.AreEqual(0, uut.BannedUsers.Count);
        }
Exemple #3
0
        public void OwnerCantUnchairThemselfTest()
        {
            // Ensure owner can unchair chair.
            {
                const string             prefix   = "#unchair";
                const string             args     = owner;
                const MeetingAction      action   = MeetingAction.Unchair;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                ParseMessageResult result = this.uut.ParseMessage(msg, owner, this.testTime);

                // Should have correct error message and added to list.
                Assert.AreEqual(ParseMessageResult.CanNotDoThisToOwner, result);
                Assert.AreEqual(1, uut.MeetingNotes.Count);
                CompareNotes(msg, uut.MeetingNotes.Last(), owner);

                // Should still be a chair
                Assert.AreEqual(1, uut.Chairs.Count());
                Assert.IsTrue(uut.Chairs.Contains(owner));

                // Other lists should be empty
                Assert.AreEqual(0, uut.SilencedUsers.Count);
                Assert.AreEqual(0, uut.BannedUsers.Count);
            }
        }
Exemple #4
0
        /// <summary>
        /// Adds a normal message to the meeting notes,
        /// and returns the message that was added.
        /// </summary>
        private IMeetingMessage AddMessage(
            string message,
            string user,
            ParseMessageResult expectedResult = ParseMessageResult.Success,
            DateTime?timestamp = null
            )
        {
            const string             prefix   = "";
            string                   args     = message;
            const MeetingAction      action   = MeetingAction.Unknown;
            const CommandRestriction restrict = CommandRestriction.Unknown;

            IMeetingMessage msg = MakeMessage(
                prefix,
                args,
                action,
                restrict
                );

            ParseMessageResult result = this.uut.ParseMessage(msg, user, timestamp ?? this.testTime);

            Assert.AreEqual(expectedResult, result);

            return(msg);
        }
Exemple #5
0
        public void PurgeUserTest()
        {
            // Add some messages

            AddAndCheckMessage(
                "Hello World 1",
                1,
                user1
                );

            AddAndCheckMessage(
                "Hello World 2",
                2,
                user1
                );

            // Purge the user.
            {
                const string             prefix   = "#purge";
                const string             args     = user1;
                const MeetingAction      action   = MeetingAction.Purge;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                ParseMessageResult result = this.uut.ParseMessage(msg, owner, this.testTime);

                // Should be a successful parse.
                Assert.AreEqual(ParseMessageResult.Success, result);

                // Only 1 message should remain; the purge one.
                Assert.AreEqual(1, uut.MeetingNotes.Count);
                CompareNotes(msg, uut.MeetingNotes.Last(), owner);

                EnsureDefaultLists();
            }

            // User should still be able to chat though.  Should be 2 messages now
            // (new message + purge message).

            AddAndCheckMessage(
                "Hello World 2",
                2,
                user1
                );
        }
Exemple #6
0
        public void NormalUserCantSilenceWithChairOnlySettingTest()
        {
            const string             prefix   = "#silence";
            const string             args     = user2;
            const MeetingAction      action   = MeetingAction.Silence;
            const CommandRestriction restrict = CommandRestriction.ChairsOnly;

            IMeetingMessage silenceMsg = MakeMessage(
                prefix,
                args,
                action,
                restrict
                );

            ParseMessageResult result = this.uut.ParseMessage(silenceMsg, user1, this.testTime);

            Assert.AreEqual(ParseMessageResult.ChairOnlyCommand, result);

            EnsureDefaultLists();

            Assert.AreEqual(1, uut.MeetingNotes.Count);
            CompareNotes(silenceMsg, uut.MeetingNotes.Last(), user1, expectedMeetingAction: MeetingAction.Unknown);
        }
Exemple #7
0
        public void HelpTest()
        {
            const string             prefix   = "#help";
            const string             args     = "#link";
            const MeetingAction      action   = MeetingAction.Help;
            const CommandRestriction restrict = CommandRestriction.Anyone;

            IMeetingMessage msg = MakeMessage(
                prefix,
                args,
                action,
                restrict
                );

            ParseMessageResult result = this.uut.ParseMessage(msg, owner, this.testTime);

            Assert.AreEqual(ParseMessageResult.Success, result);

            EnsureDefaultLists();

            // Should be nothing in the meeting notes, helps are not added.
            Assert.AreEqual(0, uut.MeetingNotes.Count);
        }
Exemple #8
0
        private ParseMessageResult HandleUnChair(IMeetingMessage msg)
        {
            ParseMessageResult result = ParseMessageResult.Success;

            Action <string> action = delegate(string user)
            {
                if (this.chairs.Contains(user))
                {
                    if (user != this.MeetingInfo.Owner)
                    {
                        this.chairs.Remove(user);
                    }
                    else
                    {
                        // Override our result if we attempt to do this to an owner.
                        result = ParseMessageResult.CanNotDoThisToOwner;
                    }
                }
            };

            DoUserSplitAction(msg.CommandArgs, action);

            return(result);
        }
Exemple #9
0
        public void BotAdminSilenceTest()
        {
            const string             prefix   = "#silence";
            const string             args     = user1;
            const MeetingAction      action   = MeetingAction.Silence;
            const CommandRestriction restrict = CommandRestriction.ChairsAndBotAdmins;

            IMeetingMessage silenceMsg = MakeMessage(
                prefix,
                args,
                action,
                restrict
                );

            ParseMessageResult result = this.uut.ParseMessage(silenceMsg, adminName, this.testTime);

            Assert.AreEqual(ParseMessageResult.Success, result);

            Assert.AreEqual(1, uut.SilencedUsers.Count);
            Assert.IsTrue(uut.SilencedUsers.Contains(user1));

            Assert.AreEqual(1, uut.MeetingNotes.Count);
            CompareNotes(silenceMsg, uut.MeetingNotes.Last(), adminName);
        }
Exemple #10
0
        // ---------------- Test Helpers ----------------

        private IMeetingMessage AddChairMessage(
            string userToChair,
            string userThatChairs,
            ParseMessageResult expectedResult = ParseMessageResult.Success
            )
        {
            const string             prefix   = "#chair";
            string                   args     = userToChair;
            const MeetingAction      action   = MeetingAction.Chair;
            const CommandRestriction restrict = CommandRestriction.ChairsOnly;

            IMeetingMessage msg = MakeMessage(
                prefix,
                args,
                action,
                restrict
                );

            ParseMessageResult result = this.uut.ParseMessage(msg, userThatChairs, this.testTime);

            Assert.AreEqual(expectedResult, result);

            return(msg);
        }
Exemple #11
0
        public void CanNotSilenceOwnerOrChairTest()
        {
            // Make user1 a chair.
            AddChairMessage(user1, owner);

            // Sanity check
            Assert.IsTrue(this.uut.Chairs.Contains(user1));

            void CheckLists()
            {
                Assert.AreEqual(0, this.uut.BannedUsers.Count);
                Assert.AreEqual(0, this.uut.SilencedUsers.Count);
                Assert.AreEqual(2, this.uut.Chairs.Count);
                Assert.IsTrue(this.uut.Chairs.Contains(owner));
                Assert.IsTrue(this.uut.Chairs.Contains(user1));
            }

            // Make sure we can not silence an owner.
            {
                const string             prefix   = "#silence";
                const string             args     = owner;
                const MeetingAction      action   = MeetingAction.Silence;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                ParseMessageResult result = this.uut.ParseMessage(msg, user1, this.testTime);
                // Error message is the generic "Can not do this to chair", even if we try
                // to silence the owner. It just makes things easier that way.
                Assert.AreEqual(ParseMessageResult.CanNotDoThisToChair, result);

                CheckLists();

                // 2 Messages: the first chair and the failed silence attempt.
                Assert.AreEqual(2, uut.MeetingNotes.Count);
                CompareNotes(msg, uut.MeetingNotes.Last(), user1);
            }

            // Make sure a not-chair can not silence a chair.
            {
                const string             prefix   = "#silence";
                const string             args     = user1;
                const MeetingAction      action   = MeetingAction.Silence;
                const CommandRestriction restrict = CommandRestriction.Anyone;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                ParseMessageResult result = this.uut.ParseMessage(msg, user2, this.testTime);
                Assert.AreEqual(ParseMessageResult.CanNotDoThisToChair, result);

                CheckLists();

                // 3 Messages: the first chair and the failed silence attempt.
                Assert.AreEqual(3, uut.MeetingNotes.Count);
                CompareNotes(msg, uut.MeetingNotes.Last(), user2);
            }

            // Make sure chair and owner can still chat
            AddAndCheckMessage(
                "Owner Check",
                4,
                owner
                );

            AddAndCheckMessage(
                "Chair Check",
                5,
                user1
                );
        }
Exemple #12
0
        public void SilenceVoiceTest()
        {
            // Make a standard message from a user
            AddAndCheckMessage(
                "Hello, world",
                1,
                user1
                );

            void CheckLists()
            {
                Assert.AreEqual(0, this.uut.BannedUsers.Count);
                Assert.AreEqual(1, this.uut.Chairs.Count);
                Assert.IsTrue(this.uut.Chairs.Contains(owner));
            }

            // User said something dumb, silence him!
            IMeetingMessage silenceMsg;

            {
                const string             prefix   = "#silence";
                const string             args     = user1;
                const MeetingAction      action   = MeetingAction.Silence;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                silenceMsg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                ParseMessageResult result = this.uut.ParseMessage(silenceMsg, owner, this.testTime);
                Assert.AreEqual(ParseMessageResult.Success, result);

                CheckLists();
                Assert.AreEqual(1, uut.SilencedUsers.Count);
                Assert.IsTrue(uut.SilencedUsers.Contains(user1));

                // 2 Messages: the first message and the silence.
                Assert.AreEqual(2, uut.MeetingNotes.Count);
                CompareNotes(silenceMsg, uut.MeetingNotes.Last(), owner);
            }

            // Silence user message should not be added to the logs.
            {
                AddMessage(
                    "Can you hear me?",
                    user1,
                    ParseMessageResult.UserIsSilenced
                    );

                CheckLists();
                Assert.AreEqual(1, uut.SilencedUsers.Count);
                Assert.IsTrue(uut.SilencedUsers.Contains(user1));

                // Most recent message should be the silence.
                Assert.AreEqual(2, uut.MeetingNotes.Count);
                CompareNotes(silenceMsg, uut.MeetingNotes.Last(), owner);
            }

            // Unsilence the user.
            {
                const string             prefix   = "#voice";
                const string             args     = user1;
                const MeetingAction      action   = MeetingAction.Voice;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                ParseMessageResult result = this.uut.ParseMessage(msg, owner, this.testTime);
                Assert.AreEqual(ParseMessageResult.Success, result);

                CheckLists();
                Assert.AreEqual(0, uut.SilencedUsers.Count);

                // 3 Messages: now including the voice message.
                Assert.AreEqual(3, uut.MeetingNotes.Count);
                CompareNotes(msg, uut.MeetingNotes.Last(), owner);
            }

            // User should now be able to chat.
            AddAndCheckMessage(
                "Hello, world, again!",
                4,
                user1
                );
        }
Exemple #13
0
        public void BanUserTest()
        {
            // Add some messages

            AddAndCheckMessage(
                "Hello World 1",
                1,
                user1
                );

            AddAndCheckMessage(
                "Hello World 2",
                2,
                user1
                );

            void CheckLists()
            {
                // 1 user should be added to the banned users.
                Assert.AreEqual(1, uut.BannedUsers.Count);
                Assert.IsTrue(uut.BannedUsers.Contains(user1));

                // 1 Person should still be in the chairs, no one should be silenced.
                Assert.AreEqual(0, uut.SilencedUsers.Count);
                Assert.AreEqual(1, uut.Chairs.Count);
                Assert.IsTrue(uut.Chairs.Contains(owner));
            }

            // Ban the user.
            IMeetingMessage banMsg;
            {
                const string             prefix   = "#ban";
                const string             args     = user1;
                const MeetingAction      action   = MeetingAction.Banish;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                banMsg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                ParseMessageResult result = this.uut.ParseMessage(banMsg, owner, this.testTime);

                // Should be a successful parse.
                Assert.AreEqual(ParseMessageResult.Success, result);

                // Only 1 message should remain; the ban one.
                Assert.AreEqual(1, uut.MeetingNotes.Count);
                CompareNotes(banMsg, uut.MeetingNotes.Last(), owner);

                CheckLists();
            }

            // User should no longer be able to chat.
            // Last message received should still be the ban message.

            {
                AddMessage(
                    "Hello World 3",
                    user1,
                    ParseMessageResult.UserIsSilenced
                    );

                Assert.AreEqual(1, uut.MeetingNotes.Count);
                CompareNotes(banMsg, uut.MeetingNotes.Last(), owner);

                CheckLists();
            }
        }
Exemple #14
0
        public void ChairCanUnchairThemSelfTest()
        {
            // Chair 1 user.
            {
                const string             prefix   = "#chair";
                const string             args     = user1;
                const MeetingAction      action   = MeetingAction.Chair;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                // Owner is chair by default.
                ParseMessageResult result = this.uut.ParseMessage(msg, owner, this.testTime);

                // Should be a successful parse.
                Assert.AreEqual(ParseMessageResult.Success, result);
                Assert.AreEqual(1, uut.MeetingNotes.Count);
                CompareNotes(msg, uut.MeetingNotes.Last(), owner);

                // New chair should show up in list.
                Assert.AreEqual(2, uut.Chairs.Count());
                Assert.IsTrue(uut.Chairs.Contains(owner));
                Assert.IsTrue(uut.Chairs.Contains(user1));

                // Other lists should be empty
                Assert.AreEqual(0, uut.SilencedUsers.Count);
                Assert.AreEqual(0, uut.BannedUsers.Count);
            }

            // Unchair themself.
            {
                const string             prefix   = "#unchair";
                const string             args     = user1;
                const MeetingAction      action   = MeetingAction.Unchair;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                // Owner is chair by default.
                ParseMessageResult result = this.uut.ParseMessage(msg, user1, this.testTime);

                // Should be a successful parse.
                Assert.AreEqual(ParseMessageResult.Success, result);
                Assert.AreEqual(2, uut.MeetingNotes.Count);
                CompareNotes(msg, uut.MeetingNotes.Last(), user1);

                // No longer should appear in list.
                Assert.AreEqual(1, uut.Chairs.Count());
                Assert.IsTrue(uut.Chairs.Contains(owner));

                // Other lists should be empty
                Assert.AreEqual(0, uut.SilencedUsers.Count);
                Assert.AreEqual(0, uut.BannedUsers.Count);
            }
        }
Exemple #15
0
        public void ChairUnchairSameUserTest()
        {
            // Chair 1 user.
            {
                const string             prefix   = "#chair";
                const string             args     = user1;
                const MeetingAction      action   = MeetingAction.Chair;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                // Owner is chair by default.
                ParseMessageResult result = this.uut.ParseMessage(msg, owner, this.testTime);

                // Should be a successful parse.
                Assert.AreEqual(ParseMessageResult.Success, result);
                Assert.AreEqual(1, uut.MeetingNotes.Count);
                CompareNotes(msg, uut.MeetingNotes.Last(), owner);

                // New chair should show up in list.
                Assert.AreEqual(2, uut.Chairs.Count());
                Assert.IsTrue(uut.Chairs.Contains(owner));
                Assert.IsTrue(uut.Chairs.Contains(user1));

                // Other lists should be empty
                Assert.AreEqual(0, uut.SilencedUsers.Count);
                Assert.AreEqual(0, uut.BannedUsers.Count);
            }

            // Chair 1 user again!
            {
                const string             prefix   = "#chair";
                const string             args     = user1;
                const MeetingAction      action   = MeetingAction.Chair;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                // Owner is chair by default.
                ParseMessageResult result = this.uut.ParseMessage(msg, owner, this.testTime);

                // Should be a successful parse.
                Assert.AreEqual(ParseMessageResult.Success, result);
                Assert.AreEqual(2, uut.MeetingNotes.Count);
                CompareNotes(msg, uut.MeetingNotes.Last(), owner);

                // New chair should show up in list, but only once!
                Assert.AreEqual(2, uut.Chairs.Count());
                Assert.IsTrue(uut.Chairs.Contains(owner));
                Assert.IsTrue(uut.Chairs.Contains(user1));

                // Other lists should be empty
                Assert.AreEqual(0, uut.SilencedUsers.Count);
                Assert.AreEqual(0, uut.BannedUsers.Count);
            }

            {
                const string             prefix   = "#unchair";
                const string             args     = user1;
                const MeetingAction      action   = MeetingAction.Unchair;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                ParseMessageResult result = this.uut.ParseMessage(msg, owner, this.testTime);

                // Should have correct error message and added to list.
                Assert.AreEqual(ParseMessageResult.Success, result);
                Assert.AreEqual(3, uut.MeetingNotes.Count);
                CompareNotes(msg, uut.MeetingNotes.Last(), owner);

                // User should no longer be a chair.
                Assert.AreEqual(1, uut.Chairs.Count());
                Assert.IsTrue(uut.Chairs.Contains(owner));

                // Other lists should be empty
                Assert.AreEqual(0, uut.SilencedUsers.Count);
                Assert.AreEqual(0, uut.BannedUsers.Count);
            }

            {
                const string             prefix   = "#unchair";
                const string             args     = user1;
                const MeetingAction      action   = MeetingAction.Unchair;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                ParseMessageResult result = this.uut.ParseMessage(msg, owner, this.testTime);

                // Should have correct error message and added to list.
                Assert.AreEqual(ParseMessageResult.Success, result);
                Assert.AreEqual(4, uut.MeetingNotes.Count);
                CompareNotes(msg, uut.MeetingNotes.Last(), owner);

                // User should no longer be a chair.
                Assert.AreEqual(1, uut.Chairs.Count());
                Assert.IsTrue(uut.Chairs.Contains(owner));

                // Other lists should be empty
                Assert.AreEqual(0, uut.SilencedUsers.Count);
                Assert.AreEqual(0, uut.BannedUsers.Count);
            }
        }
Exemple #16
0
        public void ChairUnchair1PersonTest()
        {
            // Chair 1 user.
            {
                const string             prefix   = "#chair";
                const string             args     = user1;
                const MeetingAction      action   = MeetingAction.Chair;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                // Owner is chair by default.
                ParseMessageResult result = this.uut.ParseMessage(msg, owner, this.testTime);

                // Should be a successful parse.
                Assert.AreEqual(ParseMessageResult.Success, result);
                Assert.AreEqual(1, uut.MeetingNotes.Count);
                CompareNotes(msg, uut.MeetingNotes.Last(), owner);

                // New chair should show up in list.
                Assert.AreEqual(2, uut.Chairs.Count());
                Assert.IsTrue(uut.Chairs.Contains(owner));
                Assert.IsTrue(uut.Chairs.Contains(user1));

                // Other lists should be empty
                Assert.AreEqual(0, uut.SilencedUsers.Count);
                Assert.AreEqual(0, uut.BannedUsers.Count);
            }

            // Ensure chair can not unchair owner.
            {
                const string             prefix   = "#unchair";
                const string             args     = owner;
                const MeetingAction      action   = MeetingAction.Unchair;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                ParseMessageResult result = this.uut.ParseMessage(msg, user1, this.testTime);

                // Should have correct error message and added to list.
                Assert.AreEqual(ParseMessageResult.CanNotDoThisToOwner, result);
                Assert.AreEqual(2, uut.MeetingNotes.Count);
                CompareNotes(msg, uut.MeetingNotes.Last(), user1);

                // Chair list should not be modified.
                Assert.AreEqual(2, uut.Chairs.Count());
                Assert.IsTrue(uut.Chairs.Contains(owner));
                Assert.IsTrue(uut.Chairs.Contains(user1));

                // Other lists should be empty
                Assert.AreEqual(0, uut.SilencedUsers.Count);
                Assert.AreEqual(0, uut.BannedUsers.Count);
            }

            // Ensure nonchair can not unchair chair
            {
                const string             prefix   = "#unchair";
                const string             args     = user1;
                const MeetingAction      action   = MeetingAction.Unchair;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                ParseMessageResult result = this.uut.ParseMessage(msg, user2, this.testTime);

                // Should have correct error message and added to list.
                Assert.AreEqual(ParseMessageResult.ChairOnlyCommand, result);
                Assert.AreEqual(3, uut.MeetingNotes.Count);

                // Invalid permission, meeting message becomes standard message so it
                // doesn't show up in the log as something special.
                IReadOnlyMeetingNote note = uut.MeetingNotes.Last();
                Assert.AreEqual(MeetingAction.Unknown, note.MeetingAction);
                Assert.AreEqual($"{msg.CommandPrefix} {msg.CommandArgs}", note.Message);
                Assert.AreEqual(user2, note.UserName);

                // Chair list should not be modified.
                Assert.AreEqual(2, uut.Chairs.Count());
                Assert.IsTrue(uut.Chairs.Contains(owner));
                Assert.IsTrue(uut.Chairs.Contains(user1));

                // Other lists should be empty
                Assert.AreEqual(0, uut.SilencedUsers.Count);
                Assert.AreEqual(0, uut.BannedUsers.Count);
            }

            // Ensure owner can unchair chair.
            {
                const string             prefix   = "#unchair";
                const string             args     = user1;
                const MeetingAction      action   = MeetingAction.Unchair;
                const CommandRestriction restrict = CommandRestriction.ChairsOnly;

                IMeetingMessage msg = MakeMessage(
                    prefix,
                    args,
                    action,
                    restrict
                    );

                ParseMessageResult result = this.uut.ParseMessage(msg, owner, this.testTime);

                // Should have correct error message and added to list.
                Assert.AreEqual(ParseMessageResult.Success, result);
                Assert.AreEqual(4, uut.MeetingNotes.Count);
                CompareNotes(msg, uut.MeetingNotes.Last(), owner);

                // User should no longer be a chair.
                Assert.AreEqual(1, uut.Chairs.Count());
                Assert.IsTrue(uut.Chairs.Contains(owner));

                // Other lists should be empty
                Assert.AreEqual(0, uut.SilencedUsers.Count);
                Assert.AreEqual(0, uut.BannedUsers.Count);
            }
        }