class TestUploadFileToTenderWorker(unittest.TestCase):
    __test__ = True

    def setUp(self):
        self.tender_id = uuid.uuid4().hex
        self.award_id = uuid.uuid4().hex
        self.qualification_id = uuid.uuid4().hex
        self.document_id = generate_doc_id()
        self.process_tracker = ProcessTracker(db=MagicMock())
        self.process_tracker.set_item(self.tender_id, self.award_id, 1)
        self.upload_to_tender_queue = Queue(10)
        self.url = 'http://127.0.0.1:20604'
        self.sleep_change_value = APIRateController()
        self.data = Data(self.tender_id, self.award_id, '123', 'awards', {
            'meta': {
                'id': self.document_id
            },
            'test_data': 'test_data'
        })
        self.qualification_data = Data(self.tender_id, self.qualification_id,
                                       '123', 'qualifications', {
                                           'meta': {
                                               'id': self.document_id
                                           },
                                           'test_data': 'test_data'
                                       })
        self.client = MagicMock()
        self.worker = UploadFileToTender(self.client,
                                         self.upload_to_tender_queue,
                                         self.process_tracker, MagicMock(),
                                         self.sleep_change_value)
        self.worker.retry_upload_to_tender_queue = Queue(10)

    def tearDown(self):
        del self.worker
        del self.upload_to_tender_queue

    @staticmethod
    def get_tender():
        return {
            'data': {
                'id': uuid.uuid4().hex,
                'documentOf': 'tender',
                'documentType': DOC_TYPE,
                'url': 'url'
            }
        }

    def is_working(self, worker):
        return self.upload_to_tender_queue.qsize(
        ) or worker.retry_upload_to_tender_queue.qsize()

    def shutdown_when_done(self, worker):
        worker.start()
        while self.is_working(worker):
            sleep(0.1)
        worker.shutdown()

    @patch('gevent.sleep')
    def test_upload_to_tender_429(self, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        self.client._create_tender_resource_item = MagicMock(side_effect=[
            ResourceError(http_code=429),
            ResourceError(http_code=429),
            ResourceError(http_code=403)
        ])
        self.upload_to_tender_queue.put(self.data)
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.upload_to_tender_queue.qsize(), 0,
                         'Queue should be empty')
        self.assertEqual(self.worker.sleep_change_value.time_between_requests,
                         1)

    @patch('gevent.sleep')
    def test_upload_to_tender_exception(self, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        self.upload_to_tender_queue.put(self.data)
        self.client._create_tender_resource_item = MagicMock(
            side_effect=[Exception()])
        self.worker.do_upload_to_tender_with_retry = MagicMock(
            side_effect=ResourceError(http_code=403))
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.upload_to_tender_queue.qsize(), 0,
                         'Queue should be empty')
        self.assertEqual(self.worker.sleep_change_value.time_between_requests,
                         0)

    @patch('gevent.sleep')
    def test_upload_to_tender_exception_status_int_none(self, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        self.upload_to_tender_queue.put(self.data)
        client = MagicMock()
        client._create_tender_resource_item = MagicMock(
            side_effect=[Unauthorized()])
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.upload_to_tender_queue.qsize(), 0,
                         'Queue should be empty')
        self.assertEqual(self.worker.sleep_change_value.time_between_requests,
                         0)

    @patch('gevent.sleep')
    def test_retry_upload_to_tender(self, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        self.client._create_tender_resource_item.side_effect = [
            Unauthorized(http_code=401),
            Unauthorized(http_code=403),
            Unauthorized(http_code=429),
            self.get_tender()
        ]
        self.upload_to_tender_queue.put(self.data)
        self.assertItemsEqual(self.process_tracker.processing_items.keys(),
                              [item_key(self.tender_id, self.award_id)])
        self.assertEqual(self.upload_to_tender_queue.qsize(), 1)
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.upload_to_tender_queue.qsize(), 0,
                         'Queue should be empty')
        self.assertEqual(self.process_tracker.processing_items,
                         {})  # test that item removed from processing_items
        self.assertEqual(self.client._create_tender_resource_item.call_count,
                         4)  # check upload to tender

    @patch('gevent.sleep')
    def test_retry_upload_to_tender_422(self, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        self.client_upload_to_tender = MagicMock(side_effect=ResourceError(
            http_code=422))
        self.worker.retry_upload_to_tender_queue = Queue(10)
        self.worker.retry_upload_to_tender_queue.put(self.data)
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.upload_to_tender_queue.qsize(), 0,
                         'Queue should be empty')

    @patch('gevent.sleep')
    def test_retry_upload_to_tender_429(self, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        self.client.client_upload_to_tender = MagicMock(side_effect=[
            ResourceError(http_code=429),
            ResourceError(http_code=403)
        ])
        self.worker.retry_upload_to_tender_queue = Queue(10)
        self.worker.retry_upload_to_tender_queue.put(self.data)
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.upload_to_tender_queue.qsize(), 0,
                         'Queue should be empty')

    @patch('gevent.sleep')
    def test_retry_upload_to_tender_exception(self, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        self.worker.do_upload_to_tender_with_retry = MagicMock(
            side_effect=[Exception(),
                         ResourceError(http_code=403)])
        self.worker.retry_upload_to_tender_queue.put(self.data)
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.upload_to_tender_queue.qsize(), 0,
                         'Queue should be empty')

    @patch('gevent.sleep')
    def test_upload_to_tender_queue_loop_exit(self, gevent_sleep):
        """ Test LoopExit for upload_to_tender_queue """
        gevent_sleep.side_effect = custom_sleep
        self.client._create_tender_resource_item.side_effect = [
            self.get_tender() for _ in range(2)
        ]
        self.process_tracker.set_item(self.tender_id, self.award_id, 2)
        self.worker.upload_to_tender_queue = MagicMock()
        self.worker.upload_to_tender_queue.peek.side_effect = generate_answers(
            answers=[LoopExit(), self.datum(),
                     self.datum()],
            default=LoopExit())
        self.worker.start()
        sleep(1)
        self.worker.shutdown()
        self.assertEqual(self.process_tracker.processing_items, {})
        self.assertIsNotNone(
            self.client.request_history[0].headers['X-Client-Request-ID'])
        self.assertIsNotNone(
            self.client.request_history[1].headers['X-Client-Request-ID'])
        self.assertEqual(self.client._create_tender_resource_item.call_count,
                         2)  # check that processed just 1 request

    def datum(self):
        return Data(
            self.tender_id, self.award_id, '123', 'awards', {
                u'meta': {
                    u'id': self.document_id
                },
                u'url':
                u'http://docs-sandbox.openprocurement.org/get/8ccbfde0c6804143b119d9168452cb6f',
                u'format': u'application/yaml',
                u'hash': u'md5:9a0364b9e99bb480dd25e1f0284c8555',
                u'title': file_name
            })

    @patch('gevent.sleep')
    def test_retry_upload_to_tender_queue_loop_exit(self, gevent_sleep):
        """ Test LoopExit for retry_upload_to_tender_queue """
        gevent_sleep.side_effect = custom_sleep
        self.client._create_tender_resource_item.side_effect = [
            self.get_tender() for _ in range(2)
        ]
        self.worker.retry_upload_to_tender_queue = MagicMock()
        self.worker.retry_upload_to_tender_queue.peek.side_effect = generate_answers(
            answers=[LoopExit(), self.datum(),
                     self.datum()],
            default=LoopExit())
        self.process_tracker.set_item(self.tender_id, self.award_id, 2)
        self.worker.start()
        sleep(1)
        self.worker.shutdown()
        self.assertEqual(self.process_tracker.processing_items, {})
        self.assertIsNotNone(
            self.client.request_history[0].headers['X-Client-Request-ID'])
        self.assertIsNotNone(
            self.client.request_history[1].headers['X-Client-Request-ID'])
        self.assertEqual(self.client._create_tender_resource_item.call_count,
                         2)  # check that processed just 1 request

    @patch('gevent.sleep')
    def test_request_failed_in_retry_item_status(self, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        self.client._create_tender_resource_item.side_effect = [
            ResourceError(http_code=429)
        ] + [ResourceError(http_code=403) for _ in range(4)]
        self.worker.retry_upload_to_tender_queue.put(self.data)
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.upload_to_tender_queue.qsize(), 0,
                         'Queue should be empty')

    @patch('gevent.sleep')
    def test_request_failed_in_retry(self, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        self.worker.do_upload_to_tender_with_retry = MagicMock()
        self.worker.do_upload_to_tender_with_retry.side_effect = [
            ResourceError(http_code=429) for _ in range(5)
        ] + [ResourceError(http_code=403)]
        self.sleep_change_value.increment_step = 3
        self.sleep_change_value.decrement_step = 1.5
        self.worker.retry_upload_to_tender_queue.put(self.data)
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.upload_to_tender_queue.qsize(), 0,
                         'Queue should be empty')
        self.assertEqual(self.worker.sleep_change_value.time_between_requests,
                         13.5)

    @patch('gevent.sleep')
    def test_process_412(self, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        api_server_bottle = Bottle()
        api_server = WSGIServer(('127.0.0.1', 20604),
                                api_server_bottle,
                                log=None)
        setup_routing(api_server_bottle, response_spore)
        setup_routing(api_server_bottle,
                      response_412,
                      path='/api/2.3/tenders/{}/awards/{}/documents'.format(
                          self.tender_id, self.award_id),
                      method='POST')
        api_server.start()
        self.worker.client = TendersClient('',
                                           host_url='http://127.0.0.1:20604',
                                           api_version='2.3')
        setup_routing(api_server_bottle,
                      generate_response,
                      path='/api/2.3/tenders/{}/awards/{}/documents'.format(
                          self.tender_id, self.award_id),
                      method='POST')
        self.assertEqual(self.worker.client.headers['Cookie'],
                         'SERVER_ID={}'.format(SPORE_COOKIES))
        self.upload_to_tender_queue.put(self.data)
        self.assertItemsEqual(self.process_tracker.processing_items.keys(),
                              [item_key(self.tender_id, self.award_id)])
        self.assertEqual(self.upload_to_tender_queue.qsize(), 1)
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.worker.client.headers['Cookie'],
                         'SERVER_ID={}'.format(COOKIES_412))
        self.assertEqual(self.upload_to_tender_queue.qsize(), 0,
                         'Queue should be empty')
        self.assertEqual(self.worker.retry_upload_to_tender_queue.qsize(), 0,
                         'Queue should be empty')
        self.assertItemsEqual(self.process_tracker.processing_items.keys(), [])
        api_server.stop()

    @patch('gevent.sleep')
    def test_upload_worker(self, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        self.worker.services_not_available = MagicMock(wait=MagicMock())
        self.worker.try_peek_data_and_upload_to_tender = MagicMock()
        with patch.object(self.worker, 'exit', AlmostAlwaysFalse()):
            self.worker.upload_worker()
        self.worker.services_not_available.wait.assert_called_once()
        self.worker.try_peek_data_and_upload_to_tender.assert_called_once_with(
            False)

    @patch('gevent.sleep')
    def test_retry_upload_worker(self, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        self.worker.services_not_available = MagicMock(wait=MagicMock())
        self.worker.try_peek_data_and_upload_to_tender = MagicMock()
        with patch.object(self.worker, 'exit', AlmostAlwaysFalse()):
            self.worker.retry_upload_worker()
        self.worker.services_not_available.wait.assert_called_once()
        self.worker.try_peek_data_and_upload_to_tender.assert_called_once_with(
            True)

    def test_peek_from_tender_queue(self):
        self.worker.upload_to_tender_queue.put(self.data)
        self.assertEqual(self.worker.peek_from_tender_queue(False), self.data)

    def test_peek_from_tender_queue_retry(self):
        self.worker.retry_upload_to_tender_queue.put(self.data)
        self.assertEqual(self.worker.peek_from_tender_queue(True), self.data)

    def test_peek_from_tender_queue_empty(self):
        self.worker.upload_to_tender_queue = MagicMock(peek=MagicMock(
            side_effect=LoopExit))
        with self.assertRaises(LoopExit):
            self.worker.peek_from_tender_queue(False)

    def test_peek_from_tender_queue_retry_empty(self):
        self.worker.retry_upload_to_tender_queue = MagicMock(peek=MagicMock(
            side_effect=LoopExit))
        with self.assertRaises(LoopExit):
            self.worker.peek_from_tender_queue(True)

    def test_try_peek_data_and_upload_to_tender(self):
        self.worker.upload_to_tender_queue.put(self.data)
        self.worker.try_upload_to_tender = MagicMock()
        self.worker.try_peek_data_and_upload_to_tender(False)
        self.worker.try_upload_to_tender.assert_called_once_with(
            self.data, False)

    def test_try_peek_data_and_upload_to_tender_retry(self):
        self.worker.retry_upload_to_tender_queue.put(self.data)
        self.worker.try_upload_to_tender = MagicMock()
        self.worker.try_peek_data_and_upload_to_tender(True)
        self.worker.try_upload_to_tender.assert_called_once_with(
            self.data, True)

    def test_try_upload_to_tender(self):
        self.worker.update_headers_and_upload_to_tender = MagicMock()
        self.worker.successfully_uploaded_to_tender = MagicMock()
        self.worker.try_upload_to_tender(self.data, False)
        self.worker.update_headers_and_upload_to_tender.assert_called_once_with(
            self.data, False)
        self.worker.successfully_uploaded_to_tender.assert_called_once_with(
            self.data, False)

    def test_try_upload_to_tender_retry(self):
        self.worker.update_headers_and_upload_to_tender = MagicMock()
        self.worker.successfully_uploaded_to_tender = MagicMock()
        self.worker.try_upload_to_tender(self.data, True)
        self.worker.update_headers_and_upload_to_tender.assert_called_once_with(
            self.data, True)
        self.worker.successfully_uploaded_to_tender.assert_called_once_with(
            self.data, True)

    def test_try_upload_to_tender_no_mock(self):
        self.upload_to_tender_queue.put(self.data)
        self.worker.try_upload_to_tender(self.data, False)
        self.assertEqual(self.upload_to_tender_queue.qsize(), 0)
        self.assertEqual(self.process_tracker.processing_items, {})

    def test_try_upload_to_tender_no_mock_retry(self):
        self.worker.retry_upload_to_tender_queue.put(self.data)
        self.worker.try_upload_to_tender(self.data, True)
        self.assertEqual(self.upload_to_tender_queue.qsize(), 0)
        self.assertEqual(self.process_tracker.processing_items, {})

    def test_try_upload_to_tender_resource_error(self):
        re = ResourceError("test resource error")
        self.worker.update_headers_and_upload_to_tender = MagicMock(
            side_effect=re)
        self.worker.remove_data_or_increase_wait = MagicMock()
        self.worker.try_upload_to_tender(self.data, False)
        self.worker.remove_data_or_increase_wait.assert_called_once_with(
            re, self.data, False)

    def test_try_upload_to_tender_exception(self):
        e = Exception("exception")
        self.worker.update_headers_and_upload_to_tender = MagicMock(
            side_effect=e)
        self.worker.handle_error = MagicMock()
        self.worker.try_upload_to_tender(self.data, False)
        self.worker.handle_error.assert_called_once_with(e, self.data, False)

    def test_update_headers_and_upload_to_tender(self):
        self.worker.do_upload_to_tender = MagicMock()
        self.worker.update_headers_and_upload_to_tender(self.data, False)
        self.worker.do_upload_to_tender.assert_called_once_with(self.data)

    def test_update_headers_and_upload_to_tender_retry(self):
        self.worker.do_upload_to_tender_with_retry = MagicMock()
        self.worker.update_headers_and_upload_to_tender(self.data, True)
        self.worker.do_upload_to_tender_with_retry.assert_called_once_with(
            self.data)

    def test_do_upload_to_tender(self):
        api_server_bottle = Bottle()
        api_server = WSGIServer(('127.0.0.1', 20604),
                                api_server_bottle,
                                log=None)
        setup_routing(api_server_bottle, response_spore)
        api_server.start()
        self.worker.client = TendersClientSync(
            '', host_url='http://127.0.0.1:20604', api_version='2.3')
        setup_routing(api_server_bottle,
                      response_get_tender,
                      path='/api/2.3/tenders/{}/awards/{}/documents'.format(
                          self.tender_id, self.award_id),
                      method='POST')
        self.worker.do_upload_to_tender(self.data)
        api_server.stop()

    def test_do_upload_to_tender_failure(self):
        api_server_bottle = Bottle()
        api_server = WSGIServer(('127.0.0.1', 20604),
                                api_server_bottle,
                                log=None)
        setup_routing(api_server_bottle, response_spore)
        api_server.start()
        self.worker.client = TendersClientSync(
            '', host_url='http://127.0.0.1:20604', api_version='2.3')
        setup_routing(api_server_bottle,
                      response_412,
                      path='/api/2.3/tenders/{}/awards/{}/documents'.format(
                          self.tender_id, self.award_id),
                      method='POST')
        with self.assertRaises(ResourceError):
            self.worker.do_upload_to_tender(self.data)
        api_server.stop()

    def test_do_upload_to_tender_with_retry(self):
        api_server_bottle = Bottle()
        api_server = WSGIServer(('127.0.0.1', 20604),
                                api_server_bottle,
                                log=None)
        setup_routing(api_server_bottle, response_spore)
        api_server.start()
        self.worker.client = TendersClientSync(
            '', host_url='http://127.0.0.1:20604', api_version='2.3')
        setup_routing(api_server_bottle,
                      response_get_tender,
                      path='/api/2.3/tenders/{}/awards/{}/documents'.format(
                          self.tender_id, self.award_id),
                      method='POST')
        self.worker.do_upload_to_tender_with_retry(self.data)
        api_server.stop()

    def test_do_upload_to_tender_with_retry_fail_then_success(self):
        api_server_bottle = Bottle()
        api_server = WSGIServer(('127.0.0.1', 20604),
                                api_server_bottle,
                                log=None)
        setup_routing(api_server_bottle, response_spore)
        api_server.start()
        self.worker.client = TendersClientSync(
            '', host_url='http://127.0.0.1:20604', api_version='2.3')
        setup_routing(api_server_bottle,
                      generate_response_retry,
                      path='/api/2.3/tenders/{}/awards/{}/documents'.format(
                          self.tender_id, self.award_id),
                      method='POST')
        self.worker.do_upload_to_tender_with_retry(self.data)
        api_server.stop()

    def test_do_upload_to_tender_with_retry_fail(self):
        api_server_bottle = Bottle()
        api_server = WSGIServer(('127.0.0.1', 20604),
                                api_server_bottle,
                                log=None)
        setup_routing(api_server_bottle, response_spore)
        api_server.start()
        self.worker.client = TendersClientSync(
            '', host_url='http://127.0.0.1:20604', api_version='2.3')
        setup_routing(api_server_bottle,
                      response_412,
                      path='/api/2.3/tenders/{}/awards/{}/documents'.format(
                          self.tender_id, self.award_id),
                      method='POST')
        with self.assertRaises(ResourceError):
            self.worker.do_upload_to_tender_with_retry(self.data)
        api_server.stop()

    def test_remove_data_or_increase_wait(self):
        re = ResourceError("error")
        self.worker.removing_data = MagicMock()
        self.worker.remove_data_or_increase_wait(re, self.data, False)
        self.worker.removing_data.assert_called_once_with(re, self.data, False)

    def test_remove_data_or_increase_wait_429(self):
        re = ResourceError("error", http_code=429)
        self.worker.decrease_request_frequency = MagicMock()
        self.worker.remove_data_or_increase_wait(re, self.data, False)
        self.worker.decrease_request_frequency.assert_called_once_with(
            re, self.data)

    def test_remove_data_or_increase_wait_else(self):
        re = ResourceError("error", http_code=404)
        self.worker.handle_error = MagicMock()
        self.worker.remove_data_or_increase_wait(re, self.data, False)
        self.worker.handle_error.assert_called_once_with(re, self.data, False)

    def test_removing_data(self):
        re = ResourceError("error")
        self.worker.sleep_change_value.time_between_requests = 1
        self.worker.upload_to_tender_queue.put(self.data)
        self.worker.removing_data(re, self.data, False)
        self.assertEqual(self.worker.process_tracker.processing_items, {})
        self.assertEqual(self.worker.upload_to_tender_queue.qsize(), 0)
        self.assertEqual(self.worker.sleep_change_value.time_between_requests,
                         0)

    def test_removing_data_retry(self):
        re = ResourceError("error")
        self.worker.sleep_change_value.time_between_requests = 1
        self.worker.retry_upload_to_tender_queue.put(self.data)
        self.worker.removing_data(re, self.data, True)
        self.assertEqual(self.worker.process_tracker.processing_items, {})
        self.assertEqual(self.worker.upload_to_tender_queue.qsize(), 0)
        self.assertEqual(self.worker.retry_upload_to_tender_queue.qsize(), 0)
        self.assertEqual(self.worker.sleep_change_value.time_between_requests,
                         0)

    def test_decrease_request_frequency(self):
        re = ResourceError("error", 429)
        self.worker.decrease_request_frequency(re, self.data)
        self.assertEqual(self.worker.sleep_change_value.time_between_requests,
                         1)

    def test_handle_error(self):
        re = ResourceError("error", 404)
        self.worker.upload_to_tender_queue.put(self.data)
        self.worker.handle_error(re, self.data, False)
        self.assertEqual(self.worker.upload_to_tender_queue.qsize(), 0)
        self.assertEqual(self.worker.retry_upload_to_tender_queue.get(),
                         self.data)
        self.assertEqual(self.worker.retry_upload_to_tender_queue.qsize(), 0)

    def test_handle_error_retry(self):
        re = ResourceError("error", 404)
        self.worker.upload_to_tender_queue.put(self.data)
        self.worker.handle_error(re, self.data, True)
        self.assertEqual(self.worker.upload_to_tender_queue.qsize(), 1)
        self.assertEqual(self.worker.retry_upload_to_tender_queue.qsize(), 0)

    def test_successfully_uploaded_to_tender(self):
        self.worker.upload_to_tender_queue.put(self.data)
        self.assertEqual(self.worker.process_tracker.processing_items,
                         {item_key(self.tender_id, self.award_id): 1})
        self.worker.successfully_uploaded_to_tender(self.data, False)
        self.assertEqual(self.worker.upload_to_tender_queue.qsize(), 0)
        self.assertEqual(self.worker.process_tracker.processing_items, {})

    def test_successfully_uploaded_to_tender_retry(self):
        self.worker.retry_upload_to_tender_queue.put(self.data)
        self.assertEqual(self.worker.process_tracker.processing_items,
                         {item_key(self.tender_id, self.award_id): 1})
        self.worker.successfully_uploaded_to_tender(self.data, True)
        self.assertEqual(self.worker.retry_upload_to_tender_queue.qsize(), 0)
        self.assertEqual(self.worker.process_tracker.processing_items, {})

    def test_run(self):
        self.worker.delay = 1
        upload_worker, retry_upload_worker = MagicMock(), MagicMock()
        self.worker.upload_worker = upload_worker
        self.worker.retry_upload_worker = retry_upload_worker
        with patch.object(self.worker, 'exit', AlmostAlwaysFalse()):
            self.worker._run()
        self.assertEqual(self.worker.upload_worker.call_count, 1)
        self.assertEqual(self.worker.retry_upload_worker.call_count, 1)

    @patch('gevent.killall')
    @patch('gevent.sleep')
    def test_run_exception(self, gevent_sleep, killlall):
        gevent_sleep.side_effect = custom_sleep
        self.worker._start_jobs = MagicMock(return_value={"a": 1})
        self.worker.check_and_revive_jobs = MagicMock(
            side_effect=Exception("test error"))
        self.worker._run()
        killlall.assert_called_once_with([1], timeout=5)

    @patch('gevent.killall')
    def test_run_exception(self, killlall):
        self.worker.delay = 1
        self.worker._start_jobs = MagicMock(return_value={"a": 1})
        self.worker.check_and_revive_jobs = MagicMock(
            side_effect=Exception("test error"))
        self.worker._run()
        killlall.assert_called_once_with([1], timeout=5)
示例#2
0
class TestUtils(TestCase):
    __test__ = True

    relative_to = os.path.dirname(__file__)  # crafty line
    redis = None
    redis_process = None
    PORT = 16379

    @classmethod
    def setUpClass(cls):
        cls.redis_process = subprocess.Popen(
            ['redis-server', '--port',
             str(cls.PORT), '--logfile /dev/null'])
        time.sleep(0.1)
        cls.db = Db(config)
        cls.redis = StrictRedis(port=cls.PORT)

    def setUp(self):
        self.process_tracker = ProcessTracker(self.db)
        self.tender_id = "111"
        self.item_id = "222"
        self.document_id = "333"

    @classmethod
    def tearDownClass(cls):
        cls.redis_process.terminate()
        cls.redis_process.wait()

    def tearDown(self):
        self.redis.flushall()

    def test_db_init(self):
        self.assertEqual(self.db._backend, "redis")
        self.assertEqual(self.db._db_name, 0)
        self.assertEqual(self.db._port, "16379")
        self.assertEqual(self.db._host, "127.0.0.1")

    def test_db_init_no_redis(self):
        db = Db(config_no_redis)
        self.assertIsNone(db._backend)
        self.assertIsNone(db._db_name, 0)
        self.assertIsNone(db._port)
        self.assertIsNone(db._host)

    def test_db_get(self):
        self.assertIsNone(self.db.get("111"))
        self.db.put("111", "test data")
        self.assertEqual(self.db.get("111"), "test data")

    def test_db_set(self):
        self.db.put("111", "test data")
        self.assertEqual(self.db.get("111"), "test data")

    def test_db_has(self):
        self.assertFalse(self.db.has("111"))
        self.db.put("111", "test data")
        self.assertTrue(self.db.has("111"))

    def test_set_item(self):
        self.assertEqual(self.process_tracker.processing_items, {})
        self.assertEqual(self.process_tracker.tender_documents_to_process, {})
        self.process_tracker.set_item(self.tender_id, self.item_id, 1)
        self.assertEqual(self.process_tracker.processing_items,
                         {item_key(self.tender_id, self.item_id): 1})
        self.assertEqual(self.process_tracker.tender_documents_to_process,
                         {db_key(self.tender_id): 1})

    def test_add_docs_amount_to_tender(self):
        self.assertEqual(self.process_tracker.tender_documents_to_process, {})
        self.process_tracker._add_docs_amount_to_tender(self.tender_id, 2)
        self.assertEqual(self.process_tracker.tender_documents_to_process,
                         {db_key(self.tender_id): 2})
        self.process_tracker._add_docs_amount_to_tender(self.tender_id, 3)
        self.assertEqual(self.process_tracker.tender_documents_to_process,
                         {db_key(self.tender_id): 5})

    def test_remove_docs_amount_from_tender(self):
        self.assertEqual(self.process_tracker.tender_documents_to_process, {})
        self.process_tracker.tender_documents_to_process = {
            db_key(self.tender_id): 2
        }
        self.assertEqual(self.process_tracker.tender_documents_to_process,
                         {db_key(self.tender_id): 2})
        self.process_tracker._remove_docs_amount_from_tender(self.tender_id)
        self.assertEqual(self.process_tracker.tender_documents_to_process,
                         {db_key(self.tender_id): 1})
        self.process_tracker._remove_docs_amount_from_tender(self.tender_id)
        self.assertEqual(self.process_tracker.tender_documents_to_process, {})

    def test_check_processing_item(self):
        self.assertEqual(self.process_tracker.processing_items, {})
        self.assertFalse(
            self.process_tracker.check_processing_item(self.tender_id,
                                                       self.item_id))
        self.process_tracker.set_item(self.tender_id, self.item_id)
        self.assertTrue(
            self.process_tracker.check_processing_item(self.tender_id,
                                                       self.item_id))

    def test_check_processed_item(self):
        self.assertEqual(self.process_tracker.processed_items, {})
        self.assertFalse(
            self.process_tracker.check_processed_item(self.tender_id,
                                                      self.item_id))
        self.process_tracker.set_item(self.tender_id, self.item_id)
        self.process_tracker.update_items_and_tender(self.tender_id,
                                                     self.item_id,
                                                     self.document_id)
        self.assertTrue(
            self.process_tracker.check_processed_item(self.tender_id,
                                                      self.item_id))

    def test_check_processed_tender(self):
        self.assertFalse(
            self.process_tracker.check_processed_tenders(self.tender_id))
        self.redis.set(self.tender_id, "333")
        self.assertTrue(
            self.process_tracker.check_processed_tenders(self.tender_id))

    def test_update_processing_items(self):
        self.process_tracker.processing_items = {
            item_key(self.tender_id, self.item_id): 2
        }
        self.assertEqual(self.process_tracker.processing_items,
                         {item_key(self.tender_id, self.item_id): 2})
        self.process_tracker._update_processing_items(self.tender_id,
                                                      self.item_id,
                                                      self.document_id)
        self.assertEqual(self.process_tracker.processing_items,
                         {item_key(self.tender_id, self.item_id): 1})
        self.process_tracker._update_processing_items(self.tender_id,
                                                      self.item_id,
                                                      self.document_id)
        self.assertEqual(self.process_tracker.processing_items, {})

    def test_check_412_function(self):
        func = check_412(
            MagicMock(side_effect=ResourceError(
                http_code=412, response=MagicMock(headers={'Set-Cookie': 1}))))
        with self.assertRaises(ResourceError):
            func(MagicMock(headers={'Cookie': 1}))
        func = check_412(
            MagicMock(side_effect=ResourceError(
                http_code=403, response=MagicMock(headers={'Set-Cookie': 1}))))
        with self.assertRaises(ResourceError):
            func(MagicMock(headers={'Cookie': 1}))
        f = check_412(MagicMock(side_effect=[1]))
        self.assertEqual(f(1), 1)
示例#3
0
class TestUploadFileWorker(unittest.TestCase):
    __test__ = True

    def setUp(self):
        self.tender_id = uuid.uuid4().hex
        self.award_id = uuid.uuid4().hex
        self.qualification_id = uuid.uuid4().hex
        self.document_id = generate_doc_id()
        self.process_tracker = ProcessTracker(db=MagicMock())
        self.process_tracker.set_item(self.tender_id, self.award_id, 1)
        self.upload_to_doc_service_queue = Queue(10)
        self.upload_to_tender_queue = Queue(10)
        self.sleep_change_value = APIRateController()
        self.sna = event.Event()
        self.sna.set()
        self.data = Data(self.tender_id, self.award_id, '123', 'awards', {
            'meta': {
                'id': self.document_id
            },
            'test_data': 'test_data'
        })
        self.qualification_data = Data(self.tender_id, self.qualification_id,
                                       '123', 'qualifications', {
                                           'meta': {
                                               'id': self.document_id
                                           },
                                           'test_data': 'test_data'
                                       })
        self.doc_service_client = DocServiceClient(host='127.0.0.1',
                                                   port='80',
                                                   user='',
                                                   password='')
        self.worker = UploadFileToDocService(self.upload_to_doc_service_queue,
                                             self.upload_to_tender_queue,
                                             self.process_tracker,
                                             self.doc_service_client, self.sna,
                                             self.sleep_change_value)
        self.url = '{url}'.format(url=self.doc_service_client.url)

    @staticmethod
    def stat_200():
        return {
            'data': {
                'url':
                'http://docs-sandbox.openprocurement.org/get/8ccbfde0c6804143b119d9168452cb6f',
                'format': 'application/yaml',
                'hash': 'md5:9a0364b9e99bb480dd25e1f0284c8555',
                'title': file_name
            }
        }

    @staticmethod
    def get_tender():
        return {
            'data': {
                'id': uuid.uuid4().hex,
                'documentOf': 'tender',
                'documentType': DOC_TYPE,
                'url': 'url'
            }
        }

    def tearDown(self):
        del self.worker

    def is_working(self, worker):
        return self.upload_to_doc_service_queue.qsize(
        ) or worker.retry_upload_to_doc_service_queue.qsize()

    def shutdown_when_done(self, worker):
        worker.start()
        while self.is_working(worker):
            sleep(0.1)
        worker.shutdown()

    def test_init(self):
        worker = UploadFileToDocService.spawn(None, None, None, None, self.sna,
                                              None)
        self.assertGreater(datetime.datetime.now().isoformat(),
                           worker.start_time.isoformat())
        self.assertEqual(worker.upload_to_doc_service_queue, None)
        self.assertEqual(worker.upload_to_tender_queue, None)
        self.assertEqual(worker.process_tracker, None)
        self.assertEqual(worker.doc_service_client, None)
        self.assertEqual(worker.services_not_available, self.sna)
        self.assertEqual(worker.sleep_change_value, None)
        self.assertEqual(worker.delay, 15)
        self.assertEqual(worker.exit, False)
        worker.shutdown()
        self.assertEqual(worker.exit, True)
        del worker

    @requests_mock.Mocker()
    @patch('gevent.sleep')
    def test_successful_upload(self, mrequest, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        mrequest.post(self.url, json=self.stat_200(), status_code=200)
        self.upload_to_doc_service_queue.put(self.data)
        self.assertItemsEqual(self.process_tracker.processing_items.keys(),
                              [item_key(self.tender_id, self.award_id)])
        self.assertEqual(self.upload_to_doc_service_queue.qsize(), 1)
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.upload_to_doc_service_queue.qsize(), 0,
                         'Queue should be empty')
        self.assertEqual(self.upload_to_tender_queue.qsize(), 1,
                         'Queue should be have 1 element')
        self.assertEqual(mrequest.call_count, 1)
        self.assertEqual(mrequest.request_history[0].url,
                         u'127.0.0.1:80/upload')
        self.assertIsNotNone(
            mrequest.request_history[0].headers['X-Client-Request-ID'])
        self.assertItemsEqual(self.process_tracker.processing_items.keys(),
                              [item_key(self.tender_id, self.award_id)])

    @requests_mock.Mocker()
    @patch('gevent.sleep')
    def test_retry_doc_service(self, mrequest, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        doc_service_client = DocServiceClient(host='127.0.0.1',
                                              port='80',
                                              user='',
                                              password='')
        mrequest.post(self.url, [{
            'text': '',
            'status_code': 401
        } for _ in range(6)] + [{
            'json': {
                'data': {
                    'url': 'test url',
                    'format': 'application/yaml',
                    'hash': 'md5:9a0364b9e99bb480dd25e1f0284c8555',
                    'title': file_name
                }
            },
            'status_code': 200
        }])
        self.upload_to_doc_service_queue.put(self.data)
        self.assertItemsEqual(self.process_tracker.processing_items.keys(),
                              [item_key(self.tender_id, self.award_id)])
        self.assertEqual(self.upload_to_doc_service_queue.qsize(), 1)
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.upload_to_doc_service_queue.qsize(), 0,
                         'Queue should be empty')
        self.assertEqual(self.upload_to_tender_queue.qsize(), 1,
                         'Queue should be have 1 element')
        self.assertEqual(mrequest.call_count, 7)
        self.assertEqual(mrequest.request_history[0].url,
                         u'127.0.0.1:80/upload')
        self.assertIsNotNone(
            mrequest.request_history[0].headers['X-Client-Request-ID'])

    @requests_mock.Mocker()
    @patch('gevent.sleep')
    def test_request_failed(self, mrequest, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        mrequest.post(self.url, json=self.stat_200(), status_code=200)
        self.upload_to_doc_service_queue.put(self.data)
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.upload_to_doc_service_queue.qsize(), 0,
                         'Queue should be empty')
        self.assertEqual(self.upload_to_tender_queue.get(), self.data)
        self.assertEqual(self.process_tracker.processing_items,
                         {item_key(self.tender_id, self.award_id): 1})
        self.assertEqual(mrequest.call_count, 1)
        self.assertEqual(mrequest.request_history[0].url,
                         u'127.0.0.1:80/upload')
        self.assertIsNotNone(
            mrequest.request_history[0].headers['X-Client-Request-ID'])

    @requests_mock.Mocker()
    @patch('gevent.sleep')
    def test_request_failed_item_status_change(self, mrequest, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        mrequest.post(self.url, json=self.stat_200(), status_code=200)
        self.process_tracker.set_item(self.tender_id, self.qualification_id, 1)
        self.upload_to_doc_service_queue.put(self.data)
        self.upload_to_doc_service_queue.put(self.qualification_data)
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.upload_to_doc_service_queue.qsize(), 0,
                         'Queue should be empty')
        self.assertEqual(self.upload_to_tender_queue.get(), self.data)
        self.assertEqual(self.upload_to_tender_queue.get(),
                         self.qualification_data)
        self.assertEqual(mrequest.call_count, 2)
        self.assertEqual(mrequest.request_history[0].url,
                         u'127.0.0.1:80/upload')
        self.assertIsNotNone(
            mrequest.request_history[0].headers['X-Client-Request-ID'])
        self.assertEqual(
            self.process_tracker.processing_items, {
                item_key(self.tender_id, self.award_id): 1,
                item_key(self.tender_id, self.qualification_id): 1
            })

    @requests_mock.Mocker()
    @patch('gevent.sleep')
    def test_processing_items(self, mrequest, gevent_sleep):
        gevent_sleep.side_effect = custom_sleep
        mrequest.post(self.url, [{
            'json': self.stat_200(),
            'status_code': 200
        } for _ in range(2)])
        self.process_tracker.set_item(self.tender_id, self.award_id, 2)
        self.upload_to_doc_service_queue.put(self.data)
        self.upload_to_doc_service_queue.put(self.data)
        self.shutdown_when_done(self.worker)
        self.assertEqual(self.upload_to_tender_queue.get(), self.data)
        self.assertEqual(self.upload_to_tender_queue.get(), self.data)
        self.assertEqual(mrequest.request_history[0].url,
                         u'127.0.0.1:80/upload')
        self.assertIsNotNone(
            mrequest.request_history[0].headers['X-Client-Request-ID'])

    @requests_mock.Mocker()
    @patch('gevent.sleep')
    def test_upload_to_doc_service_queue_loop_exit(self, mrequest,
                                                   gevent_sleep):
        """ Test LoopExit for upload_to_doc_service_queue """
        gevent_sleep.side_effect = custom_sleep
        self.process_tracker.set_item(self.tender_id, self.award_id, 2)
        self.worker.upload_to_doc_service_queue = MagicMock()
        self.worker.upload_to_doc_service_queue.peek.side_effect = generate_answers(
            answers=[LoopExit(), self.data, self.data], default=LoopExit())
        mrequest.post(self.url, [{
            'json': self.stat_200(),
            'status_code': 200
        } for _ in range(2)])
        self.worker.start()
        sleep(1)
        self.assertEqual(self.upload_to_tender_queue.get(), self.data)
        self.assertIsNotNone(
            mrequest.request_history[0].headers['X-Client-Request-ID'])
        self.assertIsNotNone(
            mrequest.request_history[1].headers['X-Client-Request-ID'])
        self.assertEqual(self.process_tracker.processing_items,
                         {item_key(self.tender_id, self.award_id): 2})

    @requests_mock.Mocker()
    @patch('gevent.sleep')
    def test_retry_upload_to_doc_service_queue_loop_exit(
            self, mrequest, gevent_sleep):
        """ Test LoopExit for retry_upload_to_doc_service_queue """
        gevent_sleep.side_effect = custom_sleep
        mrequest.post(self.url, [{
            'json': self.stat_200(),
            'status_code': 200
        } for _ in range(2)])
        self.process_tracker.set_item(self.tender_id, self.award_id, 2)
        self.worker.retry_upload_to_doc_service_queue = MagicMock()
        self.worker.retry_upload_to_doc_service_queue.peek.side_effect = generate_answers(
            answers=[LoopExit(), self.data, self.data], default=LoopExit())
        self.worker.start()
        sleep(1)
        self.worker.shutdown()
        self.assertEqual(self.upload_to_tender_queue.get(), self.data)
        self.assertEqual(self.process_tracker.processing_items,
                         {item_key(self.tender_id, self.award_id): 2})
        self.assertEqual(mrequest.request_history[0].url,
                         u'127.0.0.1:80/upload')
        self.assertIsNotNone(
            mrequest.request_history[0].headers['X-Client-Request-ID'])

    def test_remove_bad_data(self):
        self.worker.upload_to_doc_service_queue = MagicMock(get=MagicMock())
        self.worker.process_tracker = MagicMock(
            update_items_and_tender=MagicMock())

        self.worker.remove_bad_data(self.data, Exception("test message"),
                                    False)

        self.worker.upload_to_doc_service_queue.get.assert_called_once()
        self.assertEqual(self.worker.retry_upload_to_doc_service_queue.get(),
                         self.data)

    def test_remove_bad_data_retry(self):
        self.worker.retry_upload_to_doc_service_queue = MagicMock(
            get=MagicMock())
        self.worker.process_tracker = MagicMock(
            update_items_and_tender=MagicMock())

        with self.assertRaises(Exception):
            self.worker.remove_bad_data(self.data, Exception("test message"),
                                        True)

        self.worker.retry_upload_to_doc_service_queue.get.assert_called_once()
        self.worker.process_tracker.update_items_and_tender.assert_called_with(
            self.data.tender_id, self.data.item_id, self.document_id)

    def test_try_upload_to_doc_service(self):
        e = Exception("test error")
        self.worker.update_headers_and_upload = MagicMock(side_effect=e)
        self.worker.remove_bad_data = MagicMock()

        self.worker.try_upload_to_doc_service(self.data, False)

        self.worker.update_headers_and_upload.assert_called_once()
        self.worker.remove_bad_data.assert_called_once_with(
            self.data, e, False)

    def test_try_upload_to_doc_service_retry(self):
        e = Exception("test error")
        self.worker.update_headers_and_upload = MagicMock(side_effect=e)
        self.worker.remove_bad_data = MagicMock()

        self.worker.try_upload_to_doc_service(self.data, True)

        self.worker.update_headers_and_upload.assert_called_once()
        self.worker.remove_bad_data.assert_called_with(self.data, e, True)

    def test_run(self):
        self.worker.delay = 1
        upload_worker, retry_upload_worker = MagicMock(), MagicMock()
        self.worker.upload_worker = upload_worker
        self.worker.retry_upload_worker = retry_upload_worker
        with patch.object(self.worker, 'exit', AlmostAlwaysFalse()):
            self.worker._run()
        self.assertEqual(self.worker.upload_worker.call_count, 1)
        self.assertEqual(self.worker.retry_upload_worker.call_count, 1)

    @patch('gevent.killall')
    def test_run_exception(self, killlall):
        self.worker.delay = 1
        self.worker._start_jobs = MagicMock(return_value={"a": 1})
        self.worker.check_and_revive_jobs = MagicMock(
            side_effect=Exception("test error"))
        self.worker._run()
        killlall.assert_called_once_with([1], timeout=5)

    @patch('gevent.killall')
    @patch('gevent.sleep')
    def test_run_exception(self, gevent_sleep, killlall):
        gevent_sleep.side_effect = custom_sleep
        self.worker._start_jobs = MagicMock(return_value={"a": 1})
        self.worker.check_and_revive_jobs = MagicMock(
            side_effect=Exception("test error"))

        self.worker._run()

        killlall.assert_called_once_with([1], timeout=5)