class BatchEventProcessorTest(base.BaseTest):

    DEFAULT_QUEUE_CAPACITY = 1000
    MAX_BATCH_SIZE = 10
    MAX_DURATION_SEC = 0.2
    MAX_TIMEOUT_INTERVAL_SEC = 0.1
    TEST_TIMEOUT = 0.3

    def setUp(self, *args, **kwargs):
        base.BaseTest.setUp(self, 'config_dict_with_multiple_experiments')
        self.test_user_id = 'test_user'
        self.event_name = 'test_event'
        self.event_queue = queue.Queue(maxsize=self.DEFAULT_QUEUE_CAPACITY)
        self.optimizely.logger = NoOpLogger()
        self.notification_center = self.optimizely.notification_center

    def tearDown(self):
        self.event_processor.stop()

    def _build_conversion_event(self, event_name, project_config=None):
        config = project_config or self.project_config
        return UserEventFactory.create_conversion_event(
            config, event_name, self.test_user_id, {}, {})

    def _set_event_processor(self, event_dispatcher, logger):
        self.event_processor = BatchEventProcessor(
            event_dispatcher,
            logger,
            True,
            self.event_queue,
            self.MAX_BATCH_SIZE,
            self.MAX_DURATION_SEC,
            self.MAX_TIMEOUT_INTERVAL_SEC,
            self.optimizely.notification_center,
        )

    def test_drain_on_stop(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        user_event = self._build_conversion_event(self.event_name)
        self.event_processor.process(user_event)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        time.sleep(self.TEST_TIMEOUT)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.assertEqual(0, self.event_processor.event_queue.qsize())

    def test_flush_on_max_timeout(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        user_event = self._build_conversion_event(self.event_name)
        self.event_processor.process(user_event)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        time.sleep(self.TEST_TIMEOUT)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.assertEqual(0, self.event_processor.event_queue.qsize())

    def test_flush_once_max_timeout(self):
        event_dispatcher = CustomEventDispatcher()

        self.optimizely.logger = NoOpLogger()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        user_event = self._build_conversion_event(self.event_name)
        self.event_processor.process(user_event)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        time.sleep(self.TEST_TIMEOUT)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.assertEqual(0, self.event_processor.event_queue.qsize())
        self.assertTrue(mock_config_logging.debug.called)
        mock_config_logging.debug.assert_any_call(
            'Received event of type ConversionEvent for user test_user.')
        mock_config_logging.debug.assert_any_call('Flushing batch size 1')
        mock_config_logging.debug.assert_any_call(
            'Flush interval deadline. Flushed batch.')
        self.assertTrue(mock_config_logging.debug.call_count == 3)
        self.optimizely.logger = NoOpLogger()

    def test_flush_max_batch_size(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        for i in range(0, self.MAX_BATCH_SIZE):
            user_event = self._build_conversion_event(self.event_name)
            self.event_processor.process(user_event)
            event_dispatcher.expect_conversion(self.event_name,
                                               self.test_user_id)

        time.sleep(self.TEST_TIMEOUT)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.assertEqual(0, self.event_processor.event_queue.qsize())

    def test_flush(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        user_event = self._build_conversion_event(self.event_name)
        self.event_processor.process(user_event)
        self.event_processor.flush()
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        self.event_processor.process(user_event)
        self.event_processor.flush()
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        time.sleep(self.TEST_TIMEOUT)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.assertEqual(0, self.event_processor.event_queue.qsize())

    def test_flush_on_mismatch_revision(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        self.project_config.revision = 1
        self.project_config.project_id = 'X'

        user_event_1 = self._build_conversion_event(self.event_name,
                                                    self.project_config)
        self.event_processor.process(user_event_1)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        self.project_config.revision = 2
        self.project_config.project_id = 'X'

        user_event_2 = self._build_conversion_event(self.event_name,
                                                    self.project_config)
        self.event_processor.process(user_event_2)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        time.sleep(self.TEST_TIMEOUT)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.assertEqual(0, self.event_processor.event_queue.qsize())

    def test_flush_on_mismatch_project_id(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        self.project_config.revision = 1
        self.project_config.project_id = 'X'

        user_event_1 = self._build_conversion_event(self.event_name,
                                                    self.project_config)
        self.event_processor.process(user_event_1)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        self.project_config.revision = 1
        self.project_config.project_id = 'Y'

        user_event_2 = self._build_conversion_event(self.event_name,
                                                    self.project_config)
        self.event_processor.process(user_event_2)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        time.sleep(self.TEST_TIMEOUT)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.assertEqual(0, self.event_processor.event_queue.qsize())

    def test_stop_and_start(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        user_event = self._build_conversion_event(self.event_name,
                                                  self.project_config)
        self.event_processor.process(user_event)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        time.sleep(self.TEST_TIMEOUT)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.event_processor.stop()

        self.event_processor.process(user_event)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        self.event_processor.start()
        self.assertStrictTrue(self.event_processor.is_running)

        self.event_processor.stop()
        self.assertStrictFalse(self.event_processor.is_running)

        self.assertEqual(0, self.event_processor.event_queue.qsize())

    def test_init__invalid_batch_size(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                self.optimizely.logger,
                True,
                self.event_queue,
                5.5,
                self.MAX_DURATION_SEC,
                self.MAX_TIMEOUT_INTERVAL_SEC,
            )

        # default batch size is 10.
        self.assertEqual(10, self.event_processor.batch_size)
        mock_config_logging.info.assert_called_with(
            'Using default value 10 for batch_size.')

    def test_init__NaN_batch_size(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                self.optimizely.logger,
                True,
                self.event_queue,
                'batch_size',
                self.MAX_DURATION_SEC,
                self.MAX_TIMEOUT_INTERVAL_SEC,
            )

        # default batch size is 10.
        self.assertEqual(10, self.event_processor.batch_size)
        mock_config_logging.info.assert_called_with(
            'Using default value 10 for batch_size.')

    def test_init__invalid_flush_interval(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                mock_config_logging,
                True,
                self.event_queue,
                self.MAX_BATCH_SIZE,
                0,
                self.MAX_TIMEOUT_INTERVAL_SEC,
            )

        # default flush interval is 30s.
        self.assertEqual(datetime.timedelta(seconds=30),
                         self.event_processor.flush_interval)
        mock_config_logging.info.assert_called_with(
            'Using default value 30 for flush_interval.')

    def test_init__float_flush_interval(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                mock_config_logging,
                True,
                self.event_queue,
                self.MAX_BATCH_SIZE,
                0.5,
                self.MAX_TIMEOUT_INTERVAL_SEC,
            )

        # default flush interval is 30s.
        self.assertEqual(datetime.timedelta(seconds=0.5),
                         self.event_processor.flush_interval)

    def test_init__float_flush_deadline(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                mock_config_logging,
                True,
                self.event_queue,
                self.MAX_BATCH_SIZE,
                0.5,
                self.MAX_TIMEOUT_INTERVAL_SEC,
            )

        # default flush interval is 30s.
        self.assertTrue(
            isinstance(self.event_processor.flushing_interval_deadline, float))

    def test_init__bool_flush_interval(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                self.optimizely.logger,
                True,
                self.event_queue,
                self.MAX_BATCH_SIZE,
                True,
                self.MAX_TIMEOUT_INTERVAL_SEC,
            )

        # default flush interval is 30s.
        self.assertEqual(datetime.timedelta(seconds=30),
                         self.event_processor.flush_interval)
        mock_config_logging.info.assert_called_with(
            'Using default value 30 for flush_interval.')

    def test_init__string_flush_interval(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                self.optimizely.logger,
                True,
                self.event_queue,
                self.MAX_BATCH_SIZE,
                'True',
                self.MAX_TIMEOUT_INTERVAL_SEC,
            )

        # default flush interval is 30s.
        self.assertEqual(datetime.timedelta(seconds=30),
                         self.event_processor.flush_interval)
        mock_config_logging.info.assert_called_with(
            'Using default value 30 for flush_interval.')

    def test_init__invalid_timeout_interval(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                self.optimizely.logger,
                True,
                self.event_queue,
                self.MAX_BATCH_SIZE,
                self.MAX_DURATION_SEC,
                -100,
            )

        # default timeout interval is 5s.
        self.assertEqual(datetime.timedelta(seconds=5),
                         self.event_processor.timeout_interval)
        mock_config_logging.info.assert_called_with(
            'Using default value 5 for timeout_interval.')

    def test_init__NaN_timeout_interval(self):
        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                self.optimizely.logger,
                True,
                self.event_queue,
                self.MAX_BATCH_SIZE,
                self.MAX_DURATION_SEC,
                False,
            )

        # default timeout interval is 5s.
        self.assertEqual(datetime.timedelta(seconds=5),
                         self.event_processor.timeout_interval)
        mock_config_logging.info.assert_called_with(
            'Using default value 5 for timeout_interval.')

    def test_notification_center__on_log_event(self):

        mock_event_dispatcher = mock.Mock()
        callback_hit = [False]

        def on_log_event(log_event):
            self.assertStrictTrue(isinstance(log_event, LogEvent))
            callback_hit[0] = True

        self.optimizely.notification_center.add_notification_listener(
            enums.NotificationTypes.LOG_EVENT, on_log_event)

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self._set_event_processor(mock_event_dispatcher,
                                      mock_config_logging)

        user_event = self._build_conversion_event(self.event_name,
                                                  self.project_config)
        self.event_processor.process(user_event)

        self.event_processor.stop()

        self.assertEqual(True, callback_hit[0])
        self.assertEqual(
            1,
            len(self.optimizely.notification_center.notification_listeners[
                enums.NotificationTypes.LOG_EVENT]),
        )

    def test_warning_log_level_on_queue_overflow(self):
        """ Test that a warning log is created when events overflow the queue. """

        # create scenario where the batch size (MAX_BATCH_SIZE) is significantly larger than the queue size
        # use smaller batch size and higher timeout to avoid test flakiness
        test_max_queue_size = 10
        self.MAX_BATCH_SIZE = 1000

        event_dispatcher = CustomEventDispatcher()

        with mock.patch.object(self.optimizely,
                               'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                self.optimizely.logger,
                True,
                queue.Queue(maxsize=test_max_queue_size),
            )

        for i in range(0, self.MAX_BATCH_SIZE):
            user_event = self._build_conversion_event(self.event_name)
            self.event_processor.process(user_event)
            event_dispatcher.expect_conversion(self.event_name,
                                               self.test_user_id)

        time.sleep(self.TEST_TIMEOUT)

        # queue is flushed, even though events overflow
        self.assertEqual(0, self.event_processor.event_queue.qsize())
        mock_config_logging.warning.assert_called_with(
            'Payload not accepted by the queue. Current size: {}'.format(
                str(test_max_queue_size)))
예제 #2
0
class BatchEventProcessorTest(base.BaseTest):

    DEFAULT_QUEUE_CAPACITY = 1000
    MAX_BATCH_SIZE = 10
    MAX_DURATION_SEC = 1
    MAX_TIMEOUT_INTERVAL_SEC = 5

    def setUp(self, *args, **kwargs):
        base.BaseTest.setUp(self, 'config_dict_with_multiple_experiments')
        self.test_user_id = 'test_user'
        self.event_name = 'test_event'
        self.event_queue = queue.Queue(maxsize=self.DEFAULT_QUEUE_CAPACITY)
        self.optimizely.logger = SimpleLogger()
        self.notification_center = self.optimizely.notification_center

    def tearDown(self):
        self.event_processor.stop()

    def _build_conversion_event(self, event_name, project_config=None):
        config = project_config or self.project_config
        return UserEventFactory.create_conversion_event(config, event_name, self.test_user_id, {}, {})

    def _set_event_processor(self, event_dispatcher, logger):
        self.event_processor = BatchEventProcessor(
            event_dispatcher,
            logger,
            True,
            self.event_queue,
            self.MAX_BATCH_SIZE,
            self.MAX_DURATION_SEC,
            self.MAX_TIMEOUT_INTERVAL_SEC,
            self.optimizely.notification_center,
        )

    def test_drain_on_stop(self):
        event_dispatcher = TestEventDispatcher()

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        user_event = self._build_conversion_event(self.event_name)
        self.event_processor.process(user_event)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        time.sleep(5)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.assertEqual(0, self.event_processor.event_queue.qsize())

    def test_flush_on_max_timeout(self):
        event_dispatcher = TestEventDispatcher()

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        user_event = self._build_conversion_event(self.event_name)
        self.event_processor.process(user_event)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        time.sleep(3)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.assertEqual(0, self.event_processor.event_queue.qsize())

    def test_flush_max_batch_size(self):
        event_dispatcher = TestEventDispatcher()

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        for i in range(0, self.MAX_BATCH_SIZE):
            user_event = self._build_conversion_event(self.event_name)
            self.event_processor.process(user_event)
            event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        time.sleep(1)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.assertEqual(0, self.event_processor.event_queue.qsize())

    def test_flush(self):
        event_dispatcher = TestEventDispatcher()

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        user_event = self._build_conversion_event(self.event_name)
        self.event_processor.process(user_event)
        self.event_processor.flush()
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        self.event_processor.process(user_event)
        self.event_processor.flush()
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        time.sleep(3)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.assertEqual(0, self.event_processor.event_queue.qsize())

    def test_flush_on_mismatch_revision(self):
        event_dispatcher = TestEventDispatcher()

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        self.project_config.revision = 1
        self.project_config.project_id = 'X'

        user_event_1 = self._build_conversion_event(self.event_name, self.project_config)
        self.event_processor.process(user_event_1)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        self.project_config.revision = 2
        self.project_config.project_id = 'X'

        user_event_2 = self._build_conversion_event(self.event_name, self.project_config)
        self.event_processor.process(user_event_2)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        time.sleep(3)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.assertEqual(0, self.event_processor.event_queue.qsize())

    def test_flush_on_mismatch_project_id(self):
        event_dispatcher = TestEventDispatcher()

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        self.project_config.revision = 1
        self.project_config.project_id = 'X'

        user_event_1 = self._build_conversion_event(self.event_name, self.project_config)
        self.event_processor.process(user_event_1)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        self.project_config.revision = 1
        self.project_config.project_id = 'Y'

        user_event_2 = self._build_conversion_event(self.event_name, self.project_config)
        self.event_processor.process(user_event_2)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        time.sleep(3)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.assertEqual(0, self.event_processor.event_queue.qsize())

    def test_stop_and_start(self):
        event_dispatcher = TestEventDispatcher()

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self._set_event_processor(event_dispatcher, mock_config_logging)

        user_event = self._build_conversion_event(self.event_name, self.project_config)
        self.event_processor.process(user_event)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        time.sleep(3)

        self.assertStrictTrue(event_dispatcher.compare_events())
        self.event_processor.stop()

        self.event_processor.process(user_event)
        event_dispatcher.expect_conversion(self.event_name, self.test_user_id)

        self.event_processor.start()
        self.assertStrictTrue(self.event_processor.is_running)

        self.event_processor.stop()
        self.assertStrictFalse(self.event_processor.is_running)

        self.assertEqual(0, self.event_processor.event_queue.qsize())

    def test_init__invalid_batch_size(self):
        event_dispatcher = TestEventDispatcher()

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                self.optimizely.logger,
                True,
                self.event_queue,
                5.5,
                self.MAX_DURATION_SEC,
                self.MAX_TIMEOUT_INTERVAL_SEC,
            )

        # default batch size is 10.
        self.assertEqual(10, self.event_processor.batch_size)
        mock_config_logging.info.assert_called_with('Using default value 10 for batch_size.')

    def test_init__NaN_batch_size(self):
        event_dispatcher = TestEventDispatcher()

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                self.optimizely.logger,
                True,
                self.event_queue,
                'batch_size',
                self.MAX_DURATION_SEC,
                self.MAX_TIMEOUT_INTERVAL_SEC,
            )

        # default batch size is 10.
        self.assertEqual(10, self.event_processor.batch_size)
        mock_config_logging.info.assert_called_with('Using default value 10 for batch_size.')

    def test_init__invalid_flush_interval(self):
        event_dispatcher = TestEventDispatcher()

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                mock_config_logging,
                True,
                self.event_queue,
                self.MAX_BATCH_SIZE,
                0,
                self.MAX_TIMEOUT_INTERVAL_SEC,
            )

        # default flush interval is 30s.
        self.assertEqual(datetime.timedelta(seconds=30), self.event_processor.flush_interval)
        mock_config_logging.info.assert_called_with('Using default value 30 for flush_interval.')

    def test_init__bool_flush_interval(self):
        event_dispatcher = TestEventDispatcher()

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                self.optimizely.logger,
                True,
                self.event_queue,
                self.MAX_BATCH_SIZE,
                True,
                self.MAX_TIMEOUT_INTERVAL_SEC,
            )

        # default flush interval is 30s.
        self.assertEqual(datetime.timedelta(seconds=30), self.event_processor.flush_interval)
        mock_config_logging.info.assert_called_with('Using default value 30 for flush_interval.')

    def test_init__string_flush_interval(self):
        event_dispatcher = TestEventDispatcher()

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                self.optimizely.logger,
                True,
                self.event_queue,
                self.MAX_BATCH_SIZE,
                'True',
                self.MAX_TIMEOUT_INTERVAL_SEC,
            )

        # default flush interval is 30s.
        self.assertEqual(datetime.timedelta(seconds=30), self.event_processor.flush_interval)
        mock_config_logging.info.assert_called_with('Using default value 30 for flush_interval.')

    def test_init__invalid_timeout_interval(self):
        event_dispatcher = TestEventDispatcher()

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                self.optimizely.logger,
                True,
                self.event_queue,
                self.MAX_BATCH_SIZE,
                self.MAX_DURATION_SEC,
                -100,
            )

        # default timeout interval is 5s.
        self.assertEqual(datetime.timedelta(seconds=5), self.event_processor.timeout_interval)
        mock_config_logging.info.assert_called_with('Using default value 5 for timeout_interval.')

    def test_init__NaN_timeout_interval(self):
        event_dispatcher = TestEventDispatcher()

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self.event_processor = BatchEventProcessor(
                event_dispatcher,
                self.optimizely.logger,
                True,
                self.event_queue,
                self.MAX_BATCH_SIZE,
                self.MAX_DURATION_SEC,
                False,
            )

        # default timeout interval is 5s.
        self.assertEqual(datetime.timedelta(seconds=5), self.event_processor.timeout_interval)
        mock_config_logging.info.assert_called_with('Using default value 5 for timeout_interval.')

    def test_notification_center__on_log_event(self):

        mock_event_dispatcher = mock.Mock()
        callback_hit = [False]

        def on_log_event(log_event):
            self.assertStrictTrue(isinstance(log_event, LogEvent))
            callback_hit[0] = True

        self.optimizely.notification_center.add_notification_listener(enums.NotificationTypes.LOG_EVENT, on_log_event)

        with mock.patch.object(self.optimizely, 'logger') as mock_config_logging:
            self._set_event_processor(mock_event_dispatcher, mock_config_logging)

        user_event = self._build_conversion_event(self.event_name, self.project_config)
        self.event_processor.process(user_event)

        self.event_processor.stop()

        self.assertEqual(True, callback_hit[0])
        self.assertEqual(
            1, len(self.optimizely.notification_center.notification_listeners[enums.NotificationTypes.LOG_EVENT]),
        )