def mk_worker(self, config):
        config['redis_pefix'] = 'test'
        worker = RetryApiWorker(config)

        yield worker.setup()
        self.addCleanup(worker.teardown)

        returnValue(worker)
 def start_server(self):
     self.app = RetryApiWorker({
         'redis_prefix': 'test',
         'request_limit': 500,
     })
     yield self.app.setup()
     self.server = yield reactor.listenTCP(0, Site(self.app.app.resource()))
     addr = self.server.getHost()
     self.url = "http://%s:%s" % (addr.host, addr.port)
class TestRetryApiWorker(TestCase):
    @inlineCallbacks
    def setUp(self):
        self.time = 10
        yield self.start_server()
        self.patch(time, 'time', lambda: self.time)

    @inlineCallbacks
    def tearDown(self):
        yield delete(self.app.redis, 'test.*')
        yield self.stop_server()

    @inlineCallbacks
    def mk_worker(self, config):
        config['redis_pefix'] = 'test'
        worker = RetryApiWorker(config)

        yield worker.setup()
        self.addCleanup(worker.teardown)

        returnValue(worker)

    @inlineCallbacks
    def start_server(self):
        self.app = RetryApiWorker({
            'redis_prefix': 'test',
            'request_limit': 500,
        })
        yield self.app.setup()
        self.server = yield reactor.listenTCP(0, Site(self.app.app.resource()))
        addr = self.server.getHost()
        self.url = "http://%s:%s" % (addr.host, addr.port)

    @inlineCallbacks
    def stop_server(self):
        yield self.app.teardown()
        yield self.server.loseConnection()

    def get(self, url):
        return treq.get("%s%s" % (self.url, url), persistent=False)

    def post(self, url, data, headers=None):
        return treq.post(
            "%s%s" % (self.url, url),
            json.dumps(data),
            persistent=False,
            headers=headers)

    def patch_log(self):
        msgs = []

        def logger(*msg):
            msgs.append(msg)

        self.patch(RetryApiWorker, 'log', staticmethod(logger))
        return msgs

    @inlineCallbacks
    def test_health(self):
        resp = yield self.get('/health')
        self.assertEqual(resp.code, http.OK)
        self.assertEqual((yield resp.content()), json.dumps({}))

    @inlineCallbacks
    def test_requests(self):
        msgs = self.patch_log()
        k = pending_key('test')

        self.assertEqual(
            (yield get_req_count(self.app.redis, 'test', '1234')), 0)

        pop_all(msgs)
        resp = yield self.post('/requests/', {
            'intervals': [30, 90],
            'request': {
                'url': 'http://www.example.org',
                'method': 'GET',
            }
        }, headers={'X-Owner-ID': '1234'})

        self.assertEqual(resp.code, http.OK)
        self.assertEqual((yield resp.content()), json.dumps({}))

        self.assertEqual(pop_all(msgs), [
            ("Adding request to pending set", {
                'owner_id': '1234',
                'timestamp': 10,
                'intervals': [30, 90],
                'request': {
                    'url': 'http://www.example.org',
                    'method': 'GET',
                },
            })
        ])

        self.assertEqual(
            (yield get_req_count(self.app.redis, 'test', '1234')), 1)

        self.assertEqual((yield zitems(self.app.redis, k)), [
            (self.time + 30, {
                'attempts': 0,
                'timestamp': 10,
                'owner_id': '1234',
                'intervals': [30, 90],
                'request': {
                    'url': 'http://www.example.org',
                    'method': 'GET',
                }
            }),
        ])

    @inlineCallbacks
    def test_requests_limit_reached(self):
        msgs = self.patch_log()
        yield set_req_count(self.app.redis, 'test', '1234', 500)

        pop_all(msgs)
        resp = yield self.post('/requests/', {
            'intervals': [30, 90],
            'request': {
                'url': 'http://www.example.org',
                'method': 'GET',
            }
        }, headers={'X-Owner-ID': '1234'})

        self.assertEqual(pop_all(msgs), [
            ("Request limit reached for 1234 at a request count of 500",)
        ])

        self.assertEqual(resp.code, 429)
        self.assertEqual(json.loads((yield resp.content())), {
            'errors': [{
                'type': 'too_many_requests',
                'message': "Only 500 unfinished requests are "
                           "allowed per owner"
            }]
        })

    @inlineCallbacks
    def test_requests_no_owner_id(self):
        resp = yield self.post('/requests/', {
            'intervals': [30, 90],
            'request': {
                'url': 'http://www.example.org',
                'method': 'GET',
            }
        })

        self.assertEqual(resp.code, http.BAD_REQUEST)
        self.assertEqual(json.loads((yield resp.content())), {
            'errors': [{
                'type': 'header_missing',
                'message': "Header 'X-Owner-ID' is missing"
            }]
        })

    @inlineCallbacks
    def test_requests_bad_body(self):
        resp = yield self.post(
            '/requests/',
            'foo',
            headers={'X-Owner-ID': '1234'})

        self.assertEqual(resp.code, http.BAD_REQUEST)
        self.assertEqual(json.loads((yield resp.content())), {
            'errors': [{
                'message': "u'foo' is not of type 'object'",
                'type': 'invalid_body'
            }]
        })

        resp = yield self.post('/requests/', {
            'intervals': 'foo',
            'request': {
                'url': 23,
                'method': 'GET',
                'headers': 'bar'
            }
        }, headers={'X-Owner-ID': '1234'})

        self.assertEqual(resp.code, http.BAD_REQUEST)
        self.assertEqual(json.loads((yield resp.content())), {
            'errors': [{
                'message': "u'foo' is not of type 'array'",
                'type': 'invalid_body'
            }, {
                'message': "23 is not of type 'string'",
                'type': 'invalid_body'
            }, {
                'message': "u'bar' is not of type 'object'",
                'type': 'invalid_body'
            }]
        })

        resp = yield self.post('/requests/', {
            'intervals': 'foo',
            'request': {
                'url': 'www.example.com',
                'method': 'GET',
                'headers': {
                    'foo': 'bar'
                }
            }
        }, headers={'X-Owner-ID': '1234'})

        self.assertEqual(resp.code, http.BAD_REQUEST)
        self.assertEqual(json.loads((yield resp.content())), {
            'errors': [{
                'message': u"u'foo' is not of type 'array'",
                'type': "invalid_body"
            }, {
                'message': u"u'bar' is not of type 'array'",
                'type': "invalid_body"
            }]
        })

        resp = yield self.post('/requests/', {
            'intervals': 'foo',
            'request': {
                'url': 'www.example.com',
                'method': 'GET',
                'headers': {
                    'foo': [23]
                }
            }
        }, headers={'X-Owner-ID': '1234'})

        self.assertEqual(resp.code, http.BAD_REQUEST)
        self.assertEqual(json.loads((yield resp.content())), {
            'errors': [{
                'message': u"u'foo' is not of type 'array'",
                'type': "invalid_body"
            }, {
                'message': u"23 is not of type 'string'",
                'type': "invalid_body"
            }]
        })

    @inlineCallbacks
    def test_config_redis_db(self):
        worker = yield self.mk_worker({
            'redis_prefix': 'test',
            'redis_db': 1,
        })

        yield worker.redis.set('test.foo', 'bar')
        yield worker.redis.select(1)
        self.assertEqual((yield worker.redis.get('test.foo')), 'bar')