Example #1
0
    def test_recheck_limits_exception(self, mock_limits_hydrate,
                                      mock_exception, mock_ControlDaemon,
                                      mock_format_exc):
        limit_data = mock.Mock(
            **{
                'get_limits.side_effect': test_utils.TestException,
            })
        mock_ControlDaemon.return_value = mock.Mock(
            **{
                'get_limits.return_value': limit_data,
            })
        lc = compactor.LimitContainer(config.Config(), mock.Mock())
        old_limits = [mock.Mock(uuid='old_uuid1'), mock.Mock(uuid='old_uuid2')]
        lc.limits = old_limits
        lc.limit_sum = 'old_sum'
        old_limit_map = dict(old_uuid1=old_limits[0], old_uuid2=old_limits[1])
        lc.limit_map = old_limit_map

        lc.recheck_limits()

        mock_ControlDaemon.return_value.get_limits.assert_called_once_with()
        limit_data.get_limits.assert_called_once_with('old_sum')
        self.assertFalse(mock_limits_hydrate.called)
        self.assertEqual(lc.limits, old_limits)
        self.assertEqual(lc.limit_sum, 'old_sum')
        self.assertEqual(lc.limit_map, old_limit_map)
        mock_exception.assert_called_once_with("Could not load limits")
        mock_format_exc.assert_called_once_with()
        lc.db.assert_has_calls([
            mock.call.sadd('errors', 'Failed to load limits: <traceback>'),
            mock.call.publish('errors', 'Failed to load limits: <traceback>'),
        ])
Example #2
0
    def test_reload_exception_altkeys(self, mock_format_exc, mock_exception):
        cd = control.ControlDaemon(
            'middleware',
            config.Config(
                conf_dict={
                    'control.errors_key': 'alt_err',
                    'control.errors_channel': 'alt_chan',
                }))
        cd.pending = mock.Mock(**{'acquire.return_value': True})
        cd.limits = mock.Mock(
            **{
                'set_limits.side_effect': test_utils.TestException,
            })
        cd._db = mock.Mock(**{'zrange.return_value': ['limit1', 'limit2']})

        cd.reload()

        cd.pending.assert_has_calls([
            mock.call.acquire(False),
            mock.call.release(),
        ])
        self.assertEqual(len(cd.pending.method_calls), 2)
        cd.limits.set_limits.assert_called_once_with(['limit1', 'limit2'])
        cd._db.assert_has_calls([
            mock.call.zrange('limits', 0, -1),
            mock.call.sadd('alt_err', 'Failed to load limits: <traceback>'),
            mock.call.publish('alt_chan',
                              'Failed to load limits: <traceback>'),
        ])
        self.assertEqual(len(cd._db.method_calls), 3)
        mock_exception.assert_called_once_with('Could not load limits')
        mock_format_exc.assert_called_once_with()
Example #3
0
    def test_recheck_limits_basic(self, mock_limits_hydrate, mock_exception,
                                  mock_ControlDaemon, mock_format_exc):
        limit_data = mock.Mock(**{
            'get_limits.return_value': ('new_sum', ['limit1', 'limit2']),
        })
        mock_ControlDaemon.return_value = mock.Mock(
            **{
                'get_limits.return_value': limit_data,
            })
        lc = compactor.LimitContainer(config.Config(), mock.Mock())
        lc.limits = [mock.Mock(uuid='old_uuid1'), mock.Mock(uuid='old_uuid2')]
        lc.limit_sum = 'old_sum'
        lc.limit_map = dict(old_uuid1=lc.limits[0], old_uuid2=lc.limits[1])

        lc.recheck_limits()

        mock_ControlDaemon.return_value.get_limits.assert_called_once_with()
        limit_data.get_limits.assert_called_once_with('old_sum')
        mock_limits_hydrate.assert_called_once_with(lc.db,
                                                    ['limit1', 'limit2'])
        self.assertEqual(lc.limits, mock_limits_hydrate.return_value)
        self.assertEqual(lc.limit_sum, 'new_sum')
        self.assertEqual(
            lc.limit_map,
            dict(
                uuid1=mock_limits_hydrate.return_value[0],
                uuid2=mock_limits_hydrate.return_value[1],
            ))
        self.assertFalse(mock_exception.called)
        self.assertFalse(mock_format_exc.called)
        self.assertEqual(len(lc.db.method_calls), 0)
Example #4
0
    def test_recheck_limits_unchanged(self, mock_limits_hydrate,
                                      mock_exception, mock_ControlDaemon,
                                      mock_format_exc):
        limit_data = mock.Mock(
            **{
                'get_limits.side_effect': control.NoChangeException,
            })
        mock_ControlDaemon.return_value = mock.Mock(
            **{
                'get_limits.return_value': limit_data,
            })
        lc = compactor.LimitContainer(config.Config(), mock.Mock())
        old_limits = [mock.Mock(uuid='old_uuid1'), mock.Mock(uuid='old_uuid2')]
        lc.limits = old_limits
        lc.limit_sum = 'old_sum'
        old_limit_map = dict(old_uuid1=old_limits[0], old_uuid2=old_limits[1])
        lc.limit_map = old_limit_map

        lc.recheck_limits()

        mock_ControlDaemon.return_value.get_limits.assert_called_once_with()
        limit_data.get_limits.assert_called_once_with('old_sum')
        self.assertFalse(mock_limits_hydrate.called)
        self.assertEqual(lc.limits, old_limits)
        self.assertEqual(lc.limit_sum, 'old_sum')
        self.assertEqual(lc.limit_map, old_limit_map)
        self.assertFalse(mock_exception.called)
        self.assertFalse(mock_format_exc.called)
        self.assertEqual(len(lc.db.method_calls), 0)
Example #5
0
    def test_ping_no_channel(self):
        conf = config.Config()
        db = mock.Mock()
        daemon = mock.Mock(config=conf, db=db)

        control.ping(daemon, '')

        self.assertFalse(db.publish.called)
Example #6
0
    def test_getitem(self, mock_recheck_limits, mock_ControlDaemon):
        lc = compactor.LimitContainer(config.Config(), 'db')
        lc.limit_map = dict(uuid1='limit1', uuid2='limit2')

        self.assertEqual(lc['uuid1'], 'limit1')
        self.assertEqual(lc['uuid2'], 'limit2')

        mock_recheck_limits.assert_has_calls([mock.call(), mock.call()])
Example #7
0
    def test_forced_spread(self, mock_random, mock_spawn_n, mock_spawn_after):
        daemon = mock.Mock(reload='reload', config=config.Config())

        control.reload(daemon, 'spread', '20.4')

        mock_random.assert_called_once_with()
        mock_spawn_after.assert_called_once_with(10.2, 'reload')
        self.assertFalse(mock_spawn_n.called)
Example #8
0
    def test_ping_with_data_no_nodename(self):
        conf = config.Config()
        db = mock.Mock()
        daemon = mock.Mock(config=conf, db=db)

        control.ping(daemon, 'reply', 'data')

        db.publish.assert_called_once_with('reply', 'pong::data')
Example #9
0
    def test_basic(self, mock_random, mock_spawn_n, mock_spawn_after):
        daemon = mock.Mock(reload='reload', config=config.Config())

        control.reload(daemon)

        self.assertFalse(mock_random.called)
        self.assertFalse(mock_spawn_after.called)
        mock_spawn_n.assert_called_once_with('reload')
Example #10
0
    def test_init_empty(self, mock_SafeConfigParser):
        cfg = config.Config()

        self.assertEqual(cfg._config, {
            None: {
                'status': '413 Request Entity Too Large',
            },
        })
        self.assertFalse(mock_SafeConfigParser.called)
Example #11
0
    def test_ping_with_data_with_nodename(self):
        conf = config.Config(conf_dict={
            'control.node_name': 'node',
        })
        db = mock.Mock()
        daemon = mock.Mock(config=conf, db=db)

        control.ping(daemon, 'reply', 'data')

        db.publish.assert_called_once_with('reply', 'pong:node:data')
Example #12
0
    def test_contains(self, mock_SafeConfigParser):
        local_conf = {
            'preprocess': 'foo:bar',
            'redis.host': '10.0.0.1',
            'control.channel': 'control_channel',
            'control.connection_pool.connection': 'FoobarConnection',
        }
        cfg = config.Config(conf_dict=local_conf)

        self.assertTrue('redis' in cfg)
        self.assertFalse('nosuch' in cfg)
Example #13
0
    def test_getitem(self, mock_SafeConfigParser):
        local_conf = {
            'preprocess': 'foo:bar',
            'redis.host': '10.0.0.1',
            'control.channel': 'control_channel',
            'control.connection_pool.connection': 'FoobarConnection',
        }
        cfg = config.Config(conf_dict=local_conf)

        self.assertEqual(cfg['redis'], dict(host='10.0.0.1'))
        self.assertEqual(cfg['nosuch'], {})
Example #14
0
    def test_get(self, mock_SafeConfigParser):
        local_conf = {
            'preprocess': 'foo:bar',
            'redis.host': '10.0.0.1',
            'control.channel': 'control_channel',
            'control.connection_pool.connection': 'FoobarConnection',
        }
        cfg = config.Config(conf_dict=local_conf)

        self.assertEqual(cfg.get('preprocess'), 'foo:bar')
        self.assertEqual(cfg.get('nosuch'), None)
        self.assertEqual(cfg.get('nosuch', 'other'), 'other')
Example #15
0
    def test_getattr(self, mock_SafeConfigParser):
        local_conf = {
            'preprocess': 'foo:bar',
            'redis.host': '10.0.0.1',
            'control.channel': 'control_channel',
            'control.connection_pool.connection': 'FoobarConnection',
        }
        cfg = config.Config(conf_dict=local_conf)

        self.assertEqual(cfg.preprocess, 'foo:bar')
        with self.assertRaises(AttributeError):
            dummy = cfg.nosuch
Example #16
0
    def test_bad_spread_fallback(self, mock_random, mock_spawn_n,
                                 mock_spawn_after):
        daemon = mock.Mock(
            reload='reload',
            config=config.Config(conf_dict={
                'control.reload_spread': '40.8',
            }))

        control.reload(daemon, 'spread', '20.4.3')

        mock_random.assert_called_once_with()
        mock_spawn_after.assert_called_once_with(20.4, 'reload')
        self.assertFalse(mock_spawn_n.called)
Example #17
0
    def test_configured_spread_override(self, mock_random, mock_spawn_n,
                                        mock_spawn_after):
        daemon = mock.Mock(
            reload='reload',
            config=config.Config(conf_dict={
                'control.reload_spread': '20.4',
            }))

        control.reload(daemon, 'immediate')

        self.assertFalse(mock_random.called)
        self.assertFalse(mock_spawn_after.called)
        mock_spawn_n.assert_called_once_with('reload')
Example #18
0
    def test_configured_spread(self, mock_random, mock_spawn_n,
                               mock_spawn_after):
        daemon = mock.Mock(
            reload='reload',
            config=config.Config(conf_dict={
                'control.reload_spread': '20.4',
            }))

        control.reload(daemon)

        mock_random.assert_called_once_with()
        mock_spawn_after.assert_called_once_with(10.2, 'reload')
        self.assertFalse(mock_spawn_n.called)
Example #19
0
    def test_init_from_files(self, mock_SafeConfigParser):
        items = {
            'turnstile': [
                ('preprocess', 'foo:bar'),
            ],
            'redis': [
                ('password', 'spampass'),
            ],
            'control': [
                ('channel', 'control_channel'),
                ('connection_pool', 'FoobarConnectionPool'),
            ],
        }
        mock_SafeConfigParser.return_value.sections.return_value = \
            ['turnstile', 'redis', 'control']
        mock_SafeConfigParser.return_value.items.side_effect = \
            lambda x: items[x]
        local_conf = {
            'config': 'file_from_dict',
            'status': '500 Internal Error',
            'redis.host': '10.0.0.1',
        }

        cfg = config.Config(conf_dict=local_conf)

        self.assertEqual(cfg._config, {
            None: {
                'status': '500 Internal Error',
                'config': 'file_from_dict',
                'preprocess': 'foo:bar',
            },
            'redis': {
                'host': '10.0.0.1',
                'password': '******',
            },
            'control': {
                'channel': 'control_channel',
                'connection_pool': 'FoobarConnectionPool',
            },
        })
        mock_SafeConfigParser.assert_called_once_with()
        mock_SafeConfigParser.return_value.assert_has_calls([
            mock.call.read(['file_from_dict']),
            mock.call.sections(),
            mock.call.items('turnstile'),
            mock.call.items('redis'),
            mock.call.items('control'),
        ])
Example #20
0
    def test_init_files(self, mock_SafeConfigParser):
        local_conf = {
            'config': 'file_from_dict',
        }

        cfg = config.Config(conf_dict=local_conf, conf_file='file_from_args')

        self.assertEqual(cfg._config, {
            None: {
                'status': '413 Request Entity Too Large',
                'config': 'file_from_dict',
            },
        })
        mock_SafeConfigParser.assert_called_once_with()
        mock_SafeConfigParser.return_value.read.assert_called_once_with(
            ['file_from_dict', 'file_from_args'])
Example #21
0
    def test_reload_noacquire(self, mock_format_exc, mock_exception):
        cd = control.ControlDaemon('middleware', config.Config())
        cd.pending = mock.Mock(**{'acquire.return_value': False})
        cd.limits = mock.Mock()
        cd._db = mock.Mock()

        cd.reload()

        cd.pending.assert_has_calls([
            mock.call.acquire(False),
        ])
        self.assertEqual(len(cd.pending.method_calls), 1)
        self.assertEqual(len(cd.limits.method_calls), 0)
        self.assertEqual(len(cd._db.method_calls), 0)
        self.assertFalse(mock_exception.called)
        self.assertFalse(mock_format_exc.called)
Example #22
0
    def test_init_local(self, mock_RemoteControlDaemon, mock_ControlDaemon):
        conf = config.Config(conf_dict={})

        lc = compactor.LimitContainer(conf, 'db')

        self.assertEqual(lc.conf, conf)
        self.assertEqual(lc.db, 'db')
        self.assertEqual(lc.limits, [])
        self.assertEqual(lc.limit_map, {})
        self.assertEqual(lc.limit_sum, None)
        self.assertEqual(lc.control_daemon, mock_ControlDaemon.return_value)

        self.assertFalse(mock_RemoteControlDaemon.called)
        mock_ControlDaemon.assert_has_calls([
            mock.call(lc, conf),
            mock.call().start(),
        ])
Example #23
0
    def test_listen_shardhint(self, mock_get_database):
        pubsub = mock.Mock(**{'listen.return_value': []})
        db = mock.Mock(**{'pubsub.return_value': pubsub})
        mock_get_database.return_value = db
        cd = control.ControlDaemon(
            'middleware',
            config.Config(conf_dict={
                'control.shard_hint': 'shard',
            }))

        cd.listen()

        mock_get_database.assert_called_once_with('control')
        db.pubsub.assert_called_once_with(shard_hint='shard')
        pubsub.assert_has_calls([
            mock.call.subscribe('control'),
            mock.call.listen(),
        ])
Example #24
0
    def test_reload(self, mock_format_exc, mock_exception):
        cd = control.ControlDaemon('middleware', config.Config())
        cd.pending = mock.Mock(**{'acquire.return_value': True})
        cd.limits = mock.Mock()
        cd._db = mock.Mock(**{'zrange.return_value': ['limit1', 'limit2']})

        cd.reload()

        cd.pending.assert_has_calls([
            mock.call.acquire(False),
            mock.call.release(),
        ])
        self.assertEqual(len(cd.pending.method_calls), 2)
        cd.limits.set_limits.assert_called_once_with(['limit1', 'limit2'])
        cd._db.assert_has_calls([
            mock.call.zrange('limits', 0, -1),
        ])
        self.assertEqual(len(cd._db.method_calls), 1)
        self.assertFalse(mock_exception.called)
        self.assertFalse(mock_format_exc.called)
Example #25
0
    def test_get_database_override(self, mock_initialize,
                                   mock_SafeConfigParser):
        local_conf = {
            'redis.host': '10.0.0.1',
            'redis.password': '******',
            'redis.db': '3',
            'control.host': '10.0.0.2',
            'control.redis.host': '10.0.0.11',
            'control.redis.port': '1234',
            'control.redis.password': '******',
            'control.redis.db': '',
        }
        cfg = config.Config(conf_dict=local_conf)

        result = cfg.get_database(override='control')

        self.assertEqual(result, 'db_handle')
        mock_initialize.assert_called_once_with({
            'host': '10.0.0.11',
            'port': '1234',
            'password': '******',
        })
        self.assertEqual(cfg._config, {
            None: {
                'status': '413 Request Entity Too Large',
            },
            'redis': {
                'host': '10.0.0.1',
                'password': '******',
                'db': '3',
            },
            'control': {
                'host': '10.0.0.2',
                'redis.host': '10.0.0.11',
                'redis.password': '******',
                'redis.port': '1234',
                'redis.db': '',
            },
        })
Example #26
0
    def test_init_dict(self, mock_SafeConfigParser):
        local_conf = {
            'preprocess': 'foo:bar',
            'redis.host': '10.0.0.1',
            'control.channel': 'control_channel',
            'control.connection_pool.connection': 'FoobarConnection',
        }

        cfg = config.Config(conf_dict=local_conf)

        self.assertEqual(cfg._config, {
            None: {
                'status': '413 Request Entity Too Large',
                'preprocess': 'foo:bar',
            },
            'redis': {
                'host': '10.0.0.1',
            },
            'control': {
                'channel': 'control_channel',
                'connection_pool.connection': 'FoobarConnection',
            },
        })
        self.assertFalse(mock_SafeConfigParser.called)
Example #27
0
    def test_listen_altchan(self, mock_exception, mock_error,
                            mock_get_database, mock_find_entrypoint):
        pubsub = mock.Mock(
            **{
                'listen.return_value': [
                    {
                        'type': 'other',
                        'channel': 'control',
                        'data': 'ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'other',
                        'data': 'ping',
                    },
                    {
                        'type': 'message',
                        'channel': 'other',
                        'data': 'ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': '',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': '',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': '_ping',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': '_ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'nosuch',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'nosuch',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'fail',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'fail',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'fail:arg1:arg2',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'fail:arg1:arg2',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'ping',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'ping:arg1:arg2',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'ping:arg1:arg2',
                    },
                ]
            })
        db = mock.Mock(**{'pubsub.return_value': pubsub})
        mock_get_database.return_value = db
        cd = control.ControlDaemon(
            'middleware',
            config.Config(conf_dict={
                'control.channel': 'other',
            }))

        cd.listen()

        mock_get_database.assert_called_once_with('control')
        db.pubsub.assert_called_once_with()
        pubsub.assert_has_calls([
            mock.call.subscribe('other'),
            mock.call.listen(),
        ])
        self.assertFalse(mock_error.called)
        self.assertFalse(mock_exception.called)
        control.ControlDaemon._commands['ping'].assert_has_calls([
            mock.call(cd),
            mock.call(cd),
        ])
        self.assertFalse(control.ControlDaemon._commands['_ping'].called)
        self.assertFalse(control.ControlDaemon._commands['fail'].called)
        self.assertFalse(mock_find_entrypoint.called)
Example #28
0
    def test_db_middleware(self):
        middleware = mock.Mock(db='midware_db')
        cd = control.ControlDaemon(middleware, config.Config())

        self.assertEqual(cd.db, 'midware_db')
Example #29
0
    def test_db_present(self):
        middleware = mock.Mock(db='midware_db')
        cd = control.ControlDaemon(middleware, config.Config())
        cd._db = 'cached_db'

        self.assertEqual(cd.db, 'cached_db')
Example #30
0
    def test_listen(self, mock_exception, mock_error, mock_get_database,
                    mock_find_entrypoint):
        entrypoints = dict(discovered=mock.Mock())
        mock_find_entrypoint.side_effect = (
            lambda x, y, compat: entrypoints.get(y))
        pubsub = mock.Mock(
            **{
                'listen.return_value': [
                    {
                        'type': 'other',
                        'channel': 'control',
                        'data': 'ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'other',
                        'data': 'ping',
                    },
                    {
                        'type': 'message',
                        'channel': 'other',
                        'data': 'ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': '',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': '',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': '_ping',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': '_ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'nosuch',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'nosuch',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'fail',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'fail',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'fail:arg1:arg2',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'fail:arg1:arg2',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'ping',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'ping',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'ping:arg1:arg2',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'ping:arg1:arg2',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'discovered',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'discovered',
                    },
                    {
                        'type': 'pmessage',
                        'channel': 'control',
                        'data': 'discovered:arg1:arg2',
                    },
                    {
                        'type': 'message',
                        'channel': 'control',
                        'data': 'discovered:arg1:arg2',
                    },
                ]
            })
        db = mock.Mock(**{'pubsub.return_value': pubsub})
        mock_get_database.return_value = db
        cd = control.ControlDaemon('middleware', config.Config())

        cd.listen()

        mock_get_database.assert_called_once_with('control')
        db.pubsub.assert_called_once_with()
        pubsub.assert_has_calls([
            mock.call.subscribe('control'),
            mock.call.listen(),
        ])
        mock_error.assert_has_calls([
            mock.call("Cannot call internal command '_ping'"),
            mock.call("Cannot call internal command '_ping'"),
            mock.call("No such command 'nosuch'"),
            mock.call("No such command 'nosuch'"),
        ])
        mock_exception.assert_has_calls([
            mock.call("Failed to execute command 'fail' arguments []"),
            mock.call("Failed to execute command 'fail' arguments []"),
            mock.call("Failed to execute command 'fail' arguments "
                      "['arg1', 'arg2']"),
            mock.call("Failed to execute command 'fail' arguments "
                      "['arg1', 'arg2']"),
        ])
        control.ControlDaemon._commands['ping'].assert_has_calls([
            mock.call(cd),
            mock.call(cd),
            mock.call(cd, 'arg1', 'arg2'),
            mock.call(cd, 'arg1', 'arg2'),
        ])
        self.assertFalse(control.ControlDaemon._commands['_ping'].called)
        control.ControlDaemon._commands['fail'].assert_has_calls([
            mock.call(cd),
            mock.call(cd),
            mock.call(cd, 'arg1', 'arg2'),
            mock.call(cd, 'arg1', 'arg2'),
        ])
        entrypoints['discovered'].assert_has_calls([
            mock.call(cd),
            mock.call(cd),
            mock.call(cd, 'arg1', 'arg2'),
            mock.call(cd, 'arg1', 'arg2'),
        ])
        mock_find_entrypoint.assert_has_calls([
            mock.call('turnstile.command', 'nosuch', compat=False),
            mock.call('turnstile.command', 'discovered', compat=False),
        ])
        self.assertEqual(len(mock_find_entrypoint.mock_calls), 2)
        self.assertEqual(control.ControlDaemon._commands['discovered'],
                         entrypoints['discovered'])
        self.assertEqual(control.ControlDaemon._commands['nosuch'], None)