def test_latency_threshold_application(self):
        selector = SelectionStoreSelector()

        scenario_def = {
            'topology_description': {
                'type':
                'ReplicaSetWithPrimary',
                'servers': [
                    {
                        'address': 'b:27017',
                        'avg_rtt_ms': 10000,
                        'type': 'RSSecondary',
                        'tag': {}
                    },
                    {
                        'address': 'c:27017',
                        'avg_rtt_ms': 20000,
                        'type': 'RSSecondary',
                        'tag': {}
                    },
                    {
                        'address': 'a:27017',
                        'avg_rtt_ms': 30000,
                        'type': 'RSPrimary',
                        'tag': {}
                    },
                ]
            }
        }

        # Create & populate Topology such that all but one server is too slow.
        rtt_times = [
            srv['avg_rtt_ms']
            for srv in scenario_def['topology_description']['servers']
        ]
        min_rtt_idx = rtt_times.index(min(rtt_times))
        seeds, hosts = get_addresses(
            scenario_def["topology_description"]["servers"])
        settings = get_topology_settings_dict(heartbeat_frequency=1,
                                              local_threshold_ms=1,
                                              seeds=seeds,
                                              server_selector=selector)
        topology = Topology(TopologySettings(**settings))
        topology.open()
        for server in scenario_def['topology_description']['servers']:
            server_description = make_server_description(server, hosts)
            topology.on_change(server_description)

        # Invoke server selection and assert no filtering based on latency
        # prior to custom server selection logic kicking in.
        server = topology.select_server(ReadPreference.NEAREST)
        self.assertEqual(len(selector.selection),
                         len(topology.description.server_descriptions()))

        # Ensure proper filtering based on latency after custom selection.
        self.assertEqual(server.description.address, seeds[min_rtt_idx])
    def test_server_selector_bypassed(self):
        selector = FunctionCallCounter(lambda x: x)

        scenario_def = {
            'topology_description': {
                'type':
                'ReplicaSetNoPrimary',
                'servers': [
                    {
                        'address': 'b:27017',
                        'avg_rtt_ms': 10000,
                        'type': 'RSSecondary',
                        'tag': {}
                    },
                    {
                        'address': 'c:27017',
                        'avg_rtt_ms': 20000,
                        'type': 'RSSecondary',
                        'tag': {}
                    },
                    {
                        'address': 'a:27017',
                        'avg_rtt_ms': 30000,
                        'type': 'RSSecondary',
                        'tag': {}
                    },
                ]
            }
        }

        # Create & populate Topology such that no server is writeable.
        seeds, hosts = get_addresses(
            scenario_def["topology_description"]["servers"])
        settings = get_topology_settings_dict(heartbeat_frequency=1,
                                              local_threshold_ms=1,
                                              seeds=seeds,
                                              server_selector=selector)
        topology = Topology(TopologySettings(**settings))
        topology.open()
        for server in scenario_def['topology_description']['servers']:
            server_description = make_server_description(server, hosts)
            topology.on_change(server_description)

        # Invoke server selection and assert no calls to our custom selector.
        with self.assertRaisesRegex(ServerSelectionTimeoutError,
                                    'No primary available for writes'):
            topology.select_server(writable_server_selector,
                                   server_selection_timeout=0.1)
        self.assertEqual(selector.call_count, 0)
    def test_latency_threshold_application(self):
        selector = SelectionStoreSelector()

        scenario_def = {
            'topology_description': {
                'type': 'ReplicaSetWithPrimary', 'servers': [
                    {'address': 'b:27017',
                     'avg_rtt_ms': 10000,
                     'type': 'RSSecondary',
                     'tag': {}},
                    {'address': 'c:27017',
                     'avg_rtt_ms': 20000,
                     'type': 'RSSecondary',
                     'tag': {}},
                    {'address': 'a:27017',
                     'avg_rtt_ms': 30000,
                     'type': 'RSPrimary',
                     'tag': {}},
                ]}}

        # Create & populate Topology such that all but one server is too slow.
        rtt_times = [srv['avg_rtt_ms'] for srv in
                     scenario_def['topology_description']['servers']]
        min_rtt_idx = rtt_times.index(min(rtt_times))
        seeds, hosts = get_addresses(
            scenario_def["topology_description"]["servers"])
        settings = get_topology_settings_dict(
            heartbeat_frequency=1, local_threshold_ms=1, seeds=seeds,
            server_selector=selector)
        topology = Topology(TopologySettings(**settings))
        topology.open()
        for server in scenario_def['topology_description']['servers']:
            server_description = make_server_description(server, hosts)
            topology.on_change(server_description)

        # Invoke server selection and assert no filtering based on latency
        # prior to custom server selection logic kicking in.
        server = topology.select_server(ReadPreference.NEAREST)
        self.assertEqual(
            len(selector.selection),
            len(topology.description.server_descriptions()))

        # Ensure proper filtering based on latency after custom selection.
        self.assertEqual(
            server.description.address, seeds[min_rtt_idx])
    def test_server_selector_bypassed(self):
        selector = CallCountSelector()

        scenario_def = {
            'topology_description': {
                'type': 'ReplicaSetNoPrimary', 'servers': [
                    {'address': 'b:27017',
                     'avg_rtt_ms': 10000,
                     'type': 'RSSecondary',
                     'tag': {}},
                    {'address': 'c:27017',
                     'avg_rtt_ms': 20000,
                     'type': 'RSSecondary',
                     'tag': {}},
                    {'address': 'a:27017',
                     'avg_rtt_ms': 30000,
                     'type': 'RSSecondary',
                     'tag': {}},
                ]}}

        # Create & populate Topology such that no server is writeable.
        seeds, hosts = get_addresses(
            scenario_def["topology_description"]["servers"])
        settings = get_topology_settings_dict(
            heartbeat_frequency=1, local_threshold_ms=1, seeds=seeds,
            server_selector=selector)
        topology = Topology(TopologySettings(**settings))
        topology.open()
        for server in scenario_def['topology_description']['servers']:
            server_description = make_server_description(server, hosts)
            topology.on_change(server_description)

        # Invoke server selection and assert no calls to our custom selector.
        with self.assertRaisesRegex(
            ServerSelectionTimeoutError, 'No primary available for writes'):
            topology.select_server(
                writable_server_selector, server_selection_timeout=0.1)
        self.assertEqual(selector.call_count, 0)