Exemplo n.º 1
0
 def setUp(self):
     """Set up the test case environment."""
     self.app = Flask(__name__)
     self.mock_facade = mock.MagicMock(DBFacade)
     self.mock_github = mock.MagicMock(GithubInterface)
     self.testcommand = UserCommand(self.mock_facade, self.mock_github)
     self.maxDiff = None
Exemplo n.º 2
0
    def setUp(self):
        self.app = Flask(__name__)

        self.u0 = User('U0G9QF9C6')
        self.u1 = User('Utheomadude')
        self.admin = create_test_admin('Uadmin')
        self.db = MemoryDB(users=[self.u0, self.u1, self.admin])

        self.mock_github = mock.MagicMock(GithubInterface)
        self.testcommand = UserCommand(self.db, self.mock_github, None)
        self.maxDiff = None
Exemplo n.º 3
0
    def setUp(self):
        self.app = Flask(__name__)

        self.u0 = User('U0G9QF9C6')
        self.u1 = User('Utheomadude')
        self.t0 = Team("BRS", "brs", "web")
        self.t1 = Team("OTEAM", "other team", "android")
        self.admin = create_test_admin('Uadmin')
        self.db = MemoryDB(users=[self.u0, self.u1, self.admin],
                           teams=[self.t0, self.t1])

        self.mock_github = mock.MagicMock(GithubInterface)
        self.testcommand = UserCommand(self.db, self.mock_github, None)
        self.maxDiff = None
Exemplo n.º 4
0
    def __init__(self,
                 config: Config,
                 db_facade: DBFacade,
                 bot: Bot,
                 gh_interface: GithubInterface,
                 token_config: TokenCommandConfig,
                 metrics: CWMetrics,
                 gcp: Optional[GCPInterface] = None):
        """Initialize the dictionary of command handlers."""
        self.commands: Dict[str, Command] = {}
        self.__facade = db_facade
        self.__bot = bot
        self.__github = gh_interface
        self.__gcp = gcp
        self.__metrics = metrics
        self.commands["user"] = UserCommand(self.__facade,
                                            self.__github,
                                            self.__gcp)
        self.commands["team"] = TeamCommand(config, self.__facade,
                                            self.__github,
                                            self.__bot,
                                            gcp=self.__gcp)
        self.commands["token"] = TokenCommand(self.__facade, token_config)
        self.commands["project"] = ProjectCommand(self.__facade)
        self.commands["karma"] = KarmaCommand(self.__facade)
        self.commands["mention"] = MentionCommand(self.__facade)
        self.commands["i-quit"] = IQuitCommand(self.__facade)

        # Disable project commands (delete when we enable it again)
        del self.commands['project']
Exemplo n.º 5
0
 def __init__(self, db_facade: DBFacade, bot: Bot,
              gh_interface: GithubInterface,
              token_config: TokenCommandConfig) -> None:
     """Initialize the dictionary of command handlers."""
     self.__commands: Dict[str, Command] = {}
     self.__facade = db_facade
     self.__bot = bot
     self.__github = gh_interface
     self.__commands["user"] = UserCommand(self.__facade, self.__github)
     self.__commands["team"] = TeamCommand(self.__facade, self.__github,
                                           self.__bot)
     self.__commands["token"] = TokenCommand(self.__facade, token_config)
     self.__commands["project"] = ProjectCommand(self.__facade)
     self.__commands["karma"] = KarmaCommand(self.__facade)
     self.__commands["mention"] = MentionCommand(self.__facade)
Exemplo n.º 6
0
class TestUserCommand(TestCase):
    def setUp(self):
        self.app = Flask(__name__)

        self.u0 = User('U0G9QF9C6')
        self.u1 = User('Utheomadude')
        self.admin = create_test_admin('Uadmin')
        self.db = MemoryDB(users=[self.u0, self.u1, self.admin])

        self.mock_github = mock.MagicMock(GithubInterface)
        self.testcommand = UserCommand(self.db, self.mock_github, None)
        self.maxDiff = None

    def test_get_help(self):
        """Test user command get_help method."""
        subcommands = list(self.testcommand.subparser.choices.keys())
        help_message = self.testcommand.get_help()
        self.assertEqual(len(subcommands), help_message.count("usage"))

    def test_get_subcommand_help(self):
        """Test user command get_help method for specific subcommands."""
        subcommands = list(self.testcommand.subparser.choices.keys())
        for subcommand in subcommands:
            help_message = self.testcommand.get_help(subcommand=subcommand)
            self.assertEqual(1, help_message.count("usage"))

    def test_get_invalid_subcommand_help(self):
        """Test user command get_help method for invalid subcommands."""
        self.assertEqual(self.testcommand.get_help(),
                         self.testcommand.get_help(subcommand="foo"))

    def test_handle_nosubs(self):
        """Test user with no sub-parsers."""
        self.assertEqual(self.testcommand.handle('user', self.u0.slack_id),
                         (self.testcommand.help, 200))

    def test_handle_bad_args(self):
        """Test user with invalid arguments."""
        self.assertEqual(
            self.testcommand.handle('user geese', self.u0.slack_id),
            (self.testcommand.help, 200))

    def test_handle_add(self):
        """Test user command add method."""
        user_id = "U0G9QF9C7"
        user = User(user_id)
        self.assertTupleEqual(self.testcommand.handle('user add', user_id),
                              ('User added!', 200))
        retr = self.db.retrieve(User, user_id)
        self.assertEqual(user, retr)

    def test_handle_add_no_overwriting(self):
        """Test user command add method when user exists in db."""
        user = User(self.u0.slack_id)
        err_msg = 'User already exists; to overwrite user, add `-f`'
        resp = self.testcommand.handle('user add', user.slack_id)
        self.assertTupleEqual(resp, (err_msg, 200))

    def test_handle_add_with_force(self):
        ret = self.testcommand.handle('user add -f', self.u0.slack_id)
        self.assertEqual(ret, ('User added!', 200))

    def test_handle_view(self):
        user_attaches = [self.u0.get_attachment()]
        with self.app.app_context():
            # jsonify requires translating the byte-string
            resp, code = self.testcommand.handle('user view', self.u0.slack_id)
            expect = {'attachments': user_attaches}
            self.assertDictEqual(resp, expect)
            self.assertEqual(code, 200)

    def test_handle_view_other_user(self):
        user = User("ABCDE8FA9")
        self.db.store(user)
        command = 'user view --username ' + user.slack_id
        user_attaches = [user.get_attachment()]
        with self.app.app_context():
            # jsonify requires translating the byte-string
            resp, code = self.testcommand.handle(command, self.u0.slack_id)
            expect = {'attachments': user_attaches}
            self.assertDictEqual(resp, expect)
            self.assertEqual(code, 200)

    def test_handle_view_lookup_error(self):
        command = 'user view --username ABCDE8FA9'
        self.assertTupleEqual(
            self.testcommand.handle(command, self.u0.slack_id),
            (UserCommand.lookup_error, 200))

    def test_handle_help(self):
        self.assertEqual(
            self.testcommand.handle('user help', self.u0.slack_id),
            (self.testcommand.help, 200))

    def test_handle_delete(self):
        message = f'Deleted user with Slack ID: {self.u0.slack_id}'
        cmd = f'user delete {self.u0.slack_id}'
        self.assertEqual(self.testcommand.handle(cmd, self.admin.slack_id),
                         (message, 200))
        with self.assertRaises(LookupError):
            self.db.retrieve(User, self.u0.slack_id)

    def test_handle_delete_not_admin(self):
        cmd = f'user delete {self.u1.slack_id}'
        self.assertEqual(self.testcommand.handle(cmd, self.u0.slack_id),
                         (UserCommand.permission_error, 200))

    def test_handle_delete_callinguser_lookup_error(self):
        cmd = f'user delete {self.u1.slack_id}'
        self.assertEqual(self.testcommand.handle(cmd, 'rando.id'),
                         (UserCommand.lookup_error, 200))

    def test_handle_edit_name(self):
        with self.app.app_context():
            resp, code = self.testcommand.handle('user edit --name rob',
                                                 self.u0.slack_id)
            expect = {'title': 'Name', 'value': 'rob', 'short': True}
            self.assertIn(expect, resp['attachments'][0]['fields'])
            self.assertEqual(code, 200)

    def test_handle_edit_github(self):
        """Test that editing github username sends request to interface."""
        self.mock_github.org_add_member.return_value = "123"
        with self.app.app_context():
            resp, code = self.testcommand.handle("user edit --github rob",
                                                 self.u0.slack_id)
            expect0 = {
                'title': 'Github Username',
                'value': 'rob',
                'short': True
            }
            expect1 = {'title': 'Github ID', 'value': '123', 'short': True}
            self.assertIn(expect0, resp['attachments'][0]['fields'])
            self.assertIn(expect1, resp['attachments'][0]['fields'])
            self.assertEqual(code, 200)
        self.mock_github.org_add_member.assert_called_once_with("rob")

    def test_handle_edit_github_error(self):
        self.mock_github.org_add_member.side_effect = GithubAPIException("")

        with self.app.app_context():
            resp, code = self.testcommand.handle('user edit --github rob',
                                                 self.u0.slack_id)
            expect = {
                'attachments': [self.u0.get_attachment()],
                'text': '\nError adding user rob to GitHub organization'
            }

            self.assertDictEqual(resp, expect)
            self.assertEqual(code, 200)

    def test_handle_edit_all_fields(self):
        user = User(self.u0.slack_id)
        user.name = 'rob'
        user.email = '*****@*****.**'
        user.position = 'dev'
        user.github_username = '******'
        user.github_id = '123'
        user.major = 'Computer Science'
        user.biography = 'Im a human lol'
        user.permissions_level = Permissions.member
        expect = {'attachments': [user.get_attachment()]}
        self.mock_github.org_add_member.return_value = "123"
        with self.app.app_context():
            resp, code = self.testcommand.handle(
                "user edit "
                "--name rob "
                "--email <mailto:[email protected]|[email protected]> --pos dev --github"
                " rob --major 'Computer Science'"
                " --bio 'Im a human lol'", self.u0.slack_id)
            self.assertDictEqual(resp, expect)
            self.assertEqual(code, 200)

    def test_handle_edit_not_admin(self):
        """Test user command with editor user that is not admin."""
        self.assertEqual(
            self.testcommand.handle(
                'user edit --username ' + self.u1.slack_id + ' '
                '--name rob '
                '--email <mailto:[email protected]|[email protected]> --pos dev --github'
                ' [email protected] --major \'Computer Science\''
                ' --bio \'Im a human\'', self.u0.slack_id),
            (UserCommand.permission_error, 200))

    def test_handle_edit_make_admin(self):
        with self.app.app_context():
            resp, code = self.testcommand.handle(
                f"user edit --username {self.u0.slack_id} "
                "--permission admin", self.admin.slack_id)
            expect = {
                'title': 'Permissions Level',
                'value': 'admin',
                'short': True
            }
            self.assertIn(expect, resp['attachments'][0]['fields'])
            self.assertEqual(code, 200)

    def test_handle_edit_make_self_admin_no_perms(self):
        with self.app.app_context():
            resp, code = self.testcommand.handle(
                "user edit --permission admin", self.u0.slack_id)
            expect = {
                'attachments': [self.u0.get_attachment()],
                'text': "\nCannot change own permission: user isn't admin."
            }
            self.assertDictEqual(resp, expect)
            self.assertEqual(code, 200)

    def test_handle_edit_lookup_error_editee(self):
        self.assertEqual(
            self.testcommand.handle(
                "user edit --username random.something "
                "--name rob "
                "--email <mailto:[email protected]|[email protected]> --pos dev --github"
                " [email protected] --major 'Computer Science'"
                " --bio 'Im a human'", self.admin.slack_id),
            (UserCommand.lookup_error, 200))

    def test_handle_edit_lookup_error(self):
        """Test user command where user is not in database."""
        self.assertEqual(
            self.testcommand.handle('user edit --name rob', 'rando'),
            (UserCommand.lookup_error, 200))

    def test_handle_command_help(self):
        ret, code = self.testcommand.handle('user help', self.u0.slack_id)
        self.assertEqual(ret, self.testcommand.get_help())
        self.assertEqual(code, 200)

    def test_handle_multiple_subcommands(self):
        """Test handling multiple observed subcommands."""
        ret, code = self.testcommand.handle('user edit view', self.u0.slack_id)
        self.assertEqual(ret, self.testcommand.get_help())
        self.assertEqual(code, 200)

    def test_handle_subcommand_help(self):
        subcommands = list(self.testcommand.subparser.choices.keys())
        for subcommand in subcommands:
            cmd_args = ['--help', '-h', '--invalid argument']
            for arg in cmd_args:
                command = f'user {subcommand} {arg}'
                ret, code = self.testcommand.handle(command, self.u0.slack_id)
                self.assertEqual(1, ret.count("usage"))
                self.assertIn(subcommand, ret)
                self.assertEqual(code, 200)
Exemplo n.º 7
0
class TestUserCommand(TestCase):
    """Test Case for UserCommand class."""
    def setUp(self):
        """Set up the test case environment."""
        self.app = Flask(__name__)
        self.mock_facade = mock.MagicMock(DBFacade)
        self.mock_github = mock.MagicMock(GithubInterface)
        self.testcommand = UserCommand(self.mock_facade, self.mock_github)
        self.maxDiff = None

    def test_get_command_name(self):
        """Test user command get_name method."""
        assert self.testcommand.get_name() == "user"

    def test_get_help(self):
        """Test user command get_help method."""
        subcommands = list(self.testcommand.subparser.choices.keys())
        help_message = self.testcommand.get_help()
        self.assertEqual(len(subcommands), help_message.count("usage"))

    def test_get_subcommand_help(self):
        """Test user command get_help method for specific subcommands."""
        subcommands = list(self.testcommand.subparser.choices.keys())
        for subcommand in subcommands:
            help_message = self.testcommand.get_help(subcommand=subcommand)
            self.assertEqual(1, help_message.count("usage"))

    def test_get_invalid_subcommand_help(self):
        """Test user command get_help method for invalid subcommands."""
        self.assertEqual(self.testcommand.get_help(),
                         self.testcommand.get_help(subcommand="foo"))

    def test_handle_nosubs(self):
        """Test user with no sub-parsers."""
        self.assertEqual(self.testcommand.handle('user', "U0G9QF9C6"),
                         (self.testcommand.help, 200))

    def test_handle_bad_args(self):
        """Test user with invalid arguments."""
        self.assertEqual(self.testcommand.handle('user geese', "U0G9QF9C6"),
                         (self.testcommand.help, 200))

    def test_handle_add(self):
        """Test user command add method."""
        user_id = "U0G9QF9C6"
        user = User(user_id)
        lookup_error = LookupError(f'User "{user_id}" not found')
        self.mock_facade.retrieve.side_effect = lookup_error
        self.assertTupleEqual(self.testcommand.handle('user add', user_id),
                              ('User added!', 200))
        self.mock_facade.store.assert_called_once_with(user)

    def test_handle_add_no_overwriting(self):
        """Test user command add method when user exists in db."""
        user_id = "U0G9QF9C6"
        user = User(user_id)
        self.mock_facade.retrieve.return_value = user

        # Since the user exists, we don't call store_user()
        err_msg = 'User already exists; to overwrite user, add `-f`'
        resp = self.testcommand.handle('user add', user_id)
        self.assertTupleEqual(resp, (err_msg, 200))
        self.mock_facade.retrieve.assert_called_once_with(User, user_id)
        self.mock_facade.store.assert_not_called()

    def test_handle_add_overwriting(self):
        """Test user command add method when user exists in db."""
        user_id = "U0G9QF9C6"
        user2_id = "U0G9QF9C69"
        user = User(user_id)
        user2 = User(user2_id)

        self.testcommand.handle('user add -f', user_id)
        self.mock_facade.store.assert_called_with(user)
        self.testcommand.handle('user add --force', user2_id)
        self.mock_facade.store.assert_called_with(user2)
        self.mock_facade.retrieve.assert_not_called()

    def test_handle_view(self):
        """Test user command view parser and handle method."""
        user_id = "U0G9QF9C6"
        user = User(user_id)
        self.mock_facade.retrieve.return_value = user
        user_attaches = [user.get_attachment()]
        with self.app.app_context():
            # jsonify requires translating the byte-string
            resp, code = self.testcommand.handle('user view', user_id)
            expect = {'attachments': user_attaches}
            self.assertDictEqual(resp, expect)
            self.assertEqual(code, 200)
        self.mock_facade.retrieve.assert_called_once_with(User, "U0G9QF9C6")

    def test_handle_view_other_user(self):
        """Test user command view handle with slack-id parameter."""
        user_id = "U0G9QF9C6"
        user = User("ABCDE8FA9")
        command = 'user view --slack-id ' + user.slack_id
        self.mock_facade.retrieve.return_value = user
        user_attaches = [user.get_attachment()]
        with self.app.app_context():
            # jsonify requires translating the byte-string
            resp, code = self.testcommand.handle(command, user_id)
            expect = {'attachments': user_attaches}
            self.assertDictEqual(resp, expect)
            self.assertEqual(code, 200)
        self.mock_facade.retrieve. \
            assert_called_once_with(User, "ABCDE8FA9")

    def test_handle_view_lookup_error(self):
        """Test user command view handle with user not in database."""
        user_id = "U0G9QF9C6"
        command = 'user view --slack-id ABCDE8FA9'
        self.mock_facade.retrieve.side_effect = LookupError
        self.assertTupleEqual(self.testcommand.handle(command, user_id),
                              (UserCommand.lookup_error, 200))
        self.mock_facade.retrieve.assert_called_once_with(User, "ABCDE8FA9")

    def test_handle_help(self):
        """Test user command help parser."""
        self.assertEqual(self.testcommand.handle('user help', "U0G9QF9C6"),
                         (self.testcommand.help, 200))

    def test_handle_delete(self):
        """Test user command delete parser."""
        user = User("ABCDEFG2F")
        user.permissions_level = Permissions.admin
        self.mock_facade.retrieve.return_value = user
        message = "Deleted user with Slack ID: " + "U0G9QF9C6"
        self.assertEqual(
            self.testcommand.handle("user delete U0G9QF9C6", "ABCDEFG2F"),
            (message, 200))
        self.mock_facade.retrieve.assert_called_once_with(User, "ABCDEFG2F")
        self.mock_facade.delete.assert_called_once_with(User, "U0G9QF9C6")

    def test_handle_delete_not_admin(self):
        """Test user command delete where user is not admin."""
        user = User("ABCDEFG2F")
        user.permissions_level = Permissions.member
        self.mock_facade.retrieve.return_value = user
        self.assertEqual(
            self.testcommand.handle("user delete U0G9QF9C6", "ABCDEFG2F"),
            (UserCommand.permission_error, 200))
        self.mock_facade.retrieve.assert_called_once_with(User, "ABCDEFG2F")
        self.mock_facade.delete.assert_not_called()

    def test_handle_delete_lookup_error(self):
        """Test user command delete parser."""
        user = User("ABCDEFG2F")
        user.permissions_level = Permissions.admin
        self.mock_facade.retrieve.return_value = user
        self.mock_facade.delete.side_effect = LookupError
        self.assertEqual(
            self.testcommand.handle("user delete U0G9QF9C6", "ABCDEFG2F"),
            (UserCommand.lookup_error, 200))
        self.mock_facade.retrieve.assert_called_once_with(User, "ABCDEFG2F")
        self.mock_facade.delete.assert_called_once_with(User, "U0G9QF9C6")

    def test_handle_edit_name(self):
        """Test user command edit parser with one field."""
        user = User("U0G9QF9C6")
        user.name = "rob"
        user_attaches = [user.get_attachment()]
        self.mock_facade.retrieve.return_value = user
        with self.app.app_context():
            resp, code = self.testcommand.handle("user edit --name rob",
                                                 "U0G9QF9C6")
            expect = {'attachments': user_attaches}
            self.assertDictEqual(resp, expect)
            self.assertEqual(code, 200)
        self.mock_facade.retrieve.assert_called_once_with(User, "U0G9QF9C6")
        self.mock_facade.store.assert_called_once_with(user)

    def test_handle_edit_github(self):
        """Test that editing github username sends request to interface."""
        user = User("U0G9QF9C6")
        user.github_username = "******"
        user.github_id = "123"
        user_attaches = [user.get_attachment()]
        self.mock_facade.retrieve.return_value = user
        self.mock_github.org_add_member.return_value = "123"
        with self.app.app_context():
            resp, code = self.testcommand.handle("user edit --github rob",
                                                 "U0G9QF9C6")
            expect = {'attachments': user_attaches}
            self.assertDictEqual(resp, expect)
            self.assertEqual(code, 200)
        self.mock_facade.retrieve.assert_called_once_with(User, "U0G9QF9C6")
        self.mock_facade.store.assert_called_once_with(user)
        self.mock_github.org_add_member.assert_called_once_with("rob")

    def test_handle_edit_github_error(self):
        """Test that editing github username sends request to interface."""
        user = User("U0G9QF9C6")
        self.mock_facade.retrieve.return_value = user
        self.mock_github.org_add_member.side_effect = GithubAPIException("")
        user_attaches = [user.get_attachment()]

        with self.app.app_context():
            resp, code = self.testcommand.handle('user edit --github rob',
                                                 'U0G9QF9C6')
            expect = {
                'attachments': user_attaches,
                'text': '\nError adding user rob to GitHub organization'
            }

            expect = expect

            self.assertDictEqual(resp, expect)
            self.assertEqual(code, 200)

        self.mock_facade.retrieve.assert_called_once_with(User, "U0G9QF9C6")
        self.mock_facade.store.assert_called_once_with(user)

    def test_handle_edit_other_user(self):
        """Test user command edit parser with all fields."""
        user = User("ABCDE89JK")
        user.name = "rob"
        user.email = "*****@*****.**"
        user.position = "dev"
        user.github_username = "******"
        user.github_id = "123"
        user.major = "Computer Science"
        user.biography = "Im a human"
        user.permissions_level = Permissions.admin
        user_attaches = [user.get_attachment()]
        self.mock_facade.retrieve.return_value = user
        self.mock_github.org_add_member.return_value = "123"
        with self.app.app_context():
            resp, code = self.testcommand.handle(
                "user edit --member U0G9QF9C6 "
                "--name rob "
                "--email <mailto:[email protected]|[email protected]> --pos dev --github"
                " [email protected] --major 'Computer Science'"
                " --bio 'Im a human'", "U0G9QF9C6")
            expect = {'attachments': user_attaches}
            self.assertDictEqual(resp, expect)
            self.assertEqual(code, 200)
        self.mock_facade.retrieve.assert_any_call(User, "U0G9QF9C6")
        self.mock_facade.store.assert_called_once_with(user)

    def test_handle_edit_not_admin(self):
        """Test user command with editor user that is not admin."""
        user_editor = User("U0G9QF9C6")
        user_editor.permissions_level = Permissions.member
        self.mock_facade.retrieve.return_value = user_editor
        self.assertEqual(
            self.testcommand.handle(
                "user edit --member ABCDE89JK "
                "--name rob "
                "--email <mailto:[email protected]|[email protected]> --pos dev --github"
                " [email protected] --major 'Computer Science'"
                " --bio 'Im a human'", "U0G9QF9C6"),
            (UserCommand.permission_error, 200))
        self.mock_facade.retrieve.assert_called_once_with(User, "U0G9QF9C6")
        self.mock_facade.store.assert_not_called()

    def test_handle_edit_make_admin(self):
        """Test user command with editor that is admin and user who's not."""
        editor = User("a1")
        editee = User("arst")
        rets = [editor, editee]
        editor.permissions_level = Permissions.admin
        editee.permissions_level = Permissions.admin
        self.mock_facade.retrieve.side_effect = rets
        editee_attaches = [editee.get_attachment()]
        with self.app.app_context():
            resp, code = self.testcommand.handle(
                "user edit --member arst "
                "--permission admin", "a1")
            expect = {'attachments': editee_attaches}
            self.assertDictEqual(resp, expect)
            self.assertEqual(code, 200)

    def test_handle_edit_make_admin_no_perms(self):
        """Test user command with editor that isn't admin."""
        editor = User("a1")
        editor_attaches = [editor.get_attachment()]
        self.mock_facade.retrieve.return_value = editor
        with self.app.app_context():
            resp, code = self.testcommand.handle(
                "user edit --permission admin", "a1")
            expect = {
                'attachments': editor_attaches,
                'text': "\nCannot change own permission: user isn't admin."
            }
            self.assertDictEqual(resp, expect)
            self.assertEqual(code, 200)

    def test_handle_edit_lookup_error_editor(self):
        """Test user command where user editor is not in database."""
        user_editor = User("U0G9QF9C6")
        self.mock_facade.retrieve.return_value = user_editor
        self.mock_facade.retrieve.side_effect = LookupError
        self.assertEqual(
            self.testcommand.handle(
                "user edit --member ABCDE89JK "
                "--name rob "
                "--email <mailto:[email protected]|[email protected]> --pos dev --github"
                " [email protected] --major 'Computer Science'"
                " --bio 'Im a human'", "U0G9QF9C6"),
            (UserCommand.lookup_error, 200))
        self.mock_facade.retrieve.assert_called_once_with(User, "U0G9QF9C6")
        self.mock_facade.store.assert_not_called()

    def test_handle_edit_lookup_error(self):
        """Test user command where user is not in database."""
        user = User("U0G9QF9C6")
        self.mock_facade.retrieve.return_value = user
        self.mock_facade.retrieve.side_effect = LookupError
        self.assertEqual(
            self.testcommand.handle("user edit --name rob", "U0G9QF9C6"),
            (UserCommand.lookup_error, 200))
        self.mock_facade.retrieve.assert_called_once_with(User, "U0G9QF9C6")
        self.mock_facade.store.assert_not_called()

    def test_handle_command_help(self):
        """Test user command help text."""
        ret, code = self.testcommand.handle("user help", "U0G9QF9C6")
        self.assertEqual(ret, self.testcommand.get_help())
        self.assertEqual(code, 200)

    def test_handle_multiple_subcommands(self):
        """Test handling multiple observed subcommands."""
        ret, code = self.testcommand.handle("user edit view", "U0G9QF9C6")
        self.assertEqual(ret, self.testcommand.get_help())
        self.assertEqual(code, 200)

    def test_handle_subcommand_help(self):
        """Test user subcommand help text."""
        subcommands = list(self.testcommand.subparser.choices.keys())
        for subcommand in subcommands:
            command = f"user {subcommand} --help"
            ret, code = self.testcommand.handle(command, "U0G9QF9C6")
            self.assertEqual(1, ret.count("usage"))
            self.assertEqual(code, 200)

            command = f"user {subcommand} -h"
            ret, code = self.testcommand.handle(command, "U0G9QF9C6")
            self.assertEqual(1, ret.count("usage"))
            self.assertEqual(code, 200)

            command = f"user {subcommand} --invalid argument"
            ret, code = self.testcommand.handle(command, "U0G9QF9C6")
            self.assertEqual(1, ret.count("usage"))
            self.assertEqual(code, 200)