def test_body_schema(self):
        class Api(object):
            app = Klein()

            @app.route('/')
            @json_body
            @validate(body_schema({'properties': {'foo': {'type': 'string'}}}))
            def route(self, req, body):
                pass

        srv = yield ToyServer.from_test(self, Api().app)
        resp = yield treq.get(
            srv.url,
            persistent=False,
            data=json.dumps({'foo': 23}))

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

        resp = yield treq.get(
            srv.url,
            persistent=False,
            data=json.dumps({'foo': 'bar'}))

        self.assertEqual(resp.code, http.OK)
    def test_retry(self):
        srv = yield ToyServer.from_test(self)
        reqs = []

        @srv.app.route('/foo')
        def route(req):
            reqs.append(req)
            return 'ok'

        resp = yield retry({
            'owner_id': '1234',
            'timestamp': 5,
            'attempts': 0,
            'intervals': [10],
            'request': {
                'url': "%s/foo" % (srv.url,),
                'method': 'POST'
            }
        }, persistent=False)

        self.assertEqual(resp.code, 200)
        self.assertEqual((yield resp.content()), 'ok')

        [req] = reqs
        self.assertEqual(req.method, 'POST')
    def test_retry(self):
        msgs = self.patch_log()
        worker = yield self.mk_worker()
        yield worker.stop()
        srv = yield ToyServer.from_test(self)
        reqs = []

        @srv.app.route('/foo')
        def route(req):
            reqs.append(req)

        req = {
            'owner_id': '1234',
            'timestamp': 5,
            'attempts': 0,
            'intervals': [10],
            'request': {
                'url': "%s/foo" % (srv.url,),
                'method': 'POST'
            }
        }

        pop_all(msgs)
        yield worker.retry(req)

        self.assertEqual(pop_all(msgs), [
            ('Retrying request', req),
            ('Retry successful (200)', req),
        ])

        [req] = reqs
        self.assertEqual(req.method, 'POST')
        self.assertEqual((yield zitems(worker.redis, pending_key('test'))), [])
    def test_has_header(self):
        class Api(object):
            app = Klein()

            @app.route('/')
            @validate(has_header('X-Foo'))
            def route(self, req):
                pass

        srv = yield ToyServer.from_test(self, Api().app)
        resp = yield treq.get(srv.url, persistent=False)

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

        resp = yield treq.get(
            srv.url,
            headers={'X-Foo': ['bar']},
            persistent=False)

        self.assertEqual(resp.code, http.OK)
    def test_validate_fail(self):
        class Api(object):
            app = Klein()

            @app.route('/')
            @validate(
                lambda _: errs1,
                lambda _: None,
                lambda _: errs2)
            def route(self, req):
                pass

        errs1 = [{
            'type': '1',
            'message': 'a'
        }]

        errs2 = [{
            'type': 'b',
            'message': 'B'
        }]

        srv = yield ToyServer.from_test(self, Api().app)
        resp = yield treq.get(srv.url, persistent=False)
        self.assertEqual(resp.code, http.BAD_REQUEST)
        self.assertEqual(json.loads((yield resp.content())), {
            'errors': errs1 + errs2
        })
    def test_retry_unicode(self):
        srv = yield ToyServer.from_test(self)
        reqs = []

        @srv.app.route('/')
        def route(req):
            reqs.append({
                'method': req.method,
                'body': req.content.read(),
                'headers': {
                    'X-Bar': req.requestHeaders.getRawHeaders('X-Bar'),
                }
            })

        yield retry({
            'owner_id': '1234',
            'timestamp': 5,
            'attempts': 0,
            'intervals': [10, 20, 30],
            'request': {
                'url': u"%s" % (srv.url,),
                'method': u'POST',
                'body': u'foo',
                'headers': {u'X-Bar': [u'baz', u'quux']}
            }
        }, persistent=False)

        [req] = reqs
        self.assertEqual(req, {
            'method': 'POST',
            'body': 'foo',
            'headers': {'X-Bar': ['baz', 'quux']}
        })
    def test_retry_inc_attempts(self):
        srv = yield ToyServer.from_test(self)

        @srv.app.route('/foo')
        def route(_):
            pass

        req = {
            'owner_id': '1234',
            'timestamp': 5,
            'attempts': 0,
            'intervals': [10, 20, 30],
            'request': {
                'url': "%s/foo" % (srv.url,),
                'method': 'GET'
            }
        }

        yield retry(req, persistent=False)
        self.assertEqual(req['attempts'], 1)

        yield retry(req, persistent=False)
        self.assertEqual(req['attempts'], 2)

        yield retry(req, persistent=False)
        self.assertEqual(req['attempts'], 3)
    def test_retry_headers(self):
        srv = yield ToyServer.from_test(self)
        headers = []

        @srv.app.route('/foo')
        def route(req):
            headers.append({
                'X-Foo': req.requestHeaders.getRawHeaders('X-Foo'),
                'X-Bar': req.requestHeaders.getRawHeaders('X-Bar')
            })

        yield retry({
            'owner_id': '1234',
            'timestamp': 5,
            'attempts': 0,
            'intervals': [10],
            'request': {
                'url': "%s/foo" % (srv.url,),
                'method': 'POST',
                'headers': {
                    'X-Foo': ['a', 'b'],
                    'X-Bar': ['c', 'd'],
                }
            }
        }, persistent=False)

        self.assertEqual(headers, [{
            'X-Foo': ['a', 'b'],
            'X-Bar': ['c', 'd'],
        }])
    def test_response_code(self):
        srv = yield ToyServer.from_test(self)

        @srv.app.route('/')
        def route(req):
            return response(req, {}, http.BAD_REQUEST)

        resp = yield treq.get(srv.url, persistent=False)
        self.assertEqual(resp.code, http.BAD_REQUEST)
    def test_response_data(self):
        srv = yield ToyServer.from_test(self)

        @srv.app.route('/')
        def route(req):
            return response(req, {'foo': 23})

        resp = yield treq.get(srv.url, persistent=False)
        self.assertEqual(json.loads((yield resp.content())), {'foo': 23})
    def test_response_content_type(self):
        srv = yield ToyServer.from_test(self)

        @srv.app.route('/')
        def route(req):
            return response(req, {})

        resp = yield treq.get(srv.url, persistent=False)

        self.assertEqual(
            resp.headers.getRawHeaders('Content-Type'),
            ['application/json'])
    def test_retry_end(self):
        msgs = self.patch_log()
        worker = yield self.mk_worker()
        srv = yield ToyServer.from_test(self)
        yield worker.stop()

        @srv.app.route('/foo')
        def route(req):
            req.setResponseCode(500)

        req1 = {
            'owner_id': '1234',
            'timestamp': 5,
            'attempts': 1,
            'intervals': [10, 20],
            'request': {
                'url': "%s/foo" % (srv.url,),
                'method': 'POST'
            }
        }

        req2 = {
            'owner_id': '1234',
            'timestamp': 10,
            'attempts': 2,
            'intervals': [10, 30, 40],
            'request': {
                'url': "%s/foo" % (srv.url,),
                'method': 'POST'
            }
        }

        pop_all(msgs)
        yield worker.retry(req1)

        self.assertEqual(pop_all(msgs), [
            ('Retrying request', req1),
            ('Retry failed (500)', req1),
            ('No remaining retry intervals, discarding request', req1),
        ])

        yield worker.retry(req2)

        self.assertEqual(pop_all(msgs), [
            ('Retrying request', req2),
            ('Retry failed (500)', req2),
            ('No remaining retry intervals, discarding request', req2),
        ])

        self.assertEqual((yield zitems(worker.redis, pending_key('test'))), [])
    def test_retry_timeout_reschedule(self):
        k = pending_key('test')
        msgs = self.patch_log()
        worker = yield self.mk_worker({'timeout': 3})
        srv = yield ToyServer.from_test(self)
        self.patch_reactor_call_later(worker.clock)
        yield worker.stop()

        @srv.app.route('/foo')
        def route(req):
            return Deferred()

        req = {
            'owner_id': '1234',
            'timestamp': 5,
            'attempts': 0,
            'intervals': [10, 20],
            'request': {
                'url': "%s/foo" % (srv.url,),
                'method': 'POST'
            }
        }

        pop_all(msgs)
        d = worker.retry(req)
        worker.clock.advance(2)
        self.assertEqual((yield zitems(worker.redis, k)), [])

        worker.clock.advance(4)
        yield d

        self.assertEqual(pop_all(msgs), [
            ('Retrying request', req),
            ('Retry timed out', req),
            ('Rescheduling retry', req),
        ])

        self.assertEqual((yield zitems(worker.redis, k)), [
            (5 + 20, {
                'owner_id': '1234',
                'timestamp': 5,
                'attempts': 1,
                'intervals': [10, 20],
                'request': {
                    'url': "%s/foo" % (srv.url,),
                    'method': 'POST'
                }
            }),
        ])
    def test_validate_pass(self):
        class Api(object):
            app = Klein()

            @app.route('/')
            @validate(
                lambda _: None,
                lambda _: None)
            def route(self, req):
                return 'ok'

        srv = yield ToyServer.from_test(self, Api().app)
        resp = yield treq.get(srv.url, persistent=False)
        self.assertEqual(resp.code, http.OK)
        self.assertEqual((yield resp.content()), 'ok')
    def test_retry_failed(self):
        srv = yield ToyServer.from_test(self)

        @srv.app.route('/<int:code>')
        def route(req, code):
            req.setResponseCode(code)

        def send(code):
            return treq.get("%s/%s" % (srv.url, code), persistent=False)

        self.assertFalse(retry_failed((yield send(200))))
        self.assertFalse(retry_failed((yield send(201))))
        self.assertFalse(retry_failed((yield send(400))))
        self.assertFalse(retry_failed((yield send(404))))
        self.assertTrue(retry_failed((yield send(500))))
        self.assertTrue(retry_failed((yield send(504))))
        self.assertTrue(retry_failed((yield send(599))))
    def test_json_body(self):
        class Api(object):
            app = Klein()

            @app.route('/')
            @json_body
            def route(self, req, body):
                bodies.append(body)

        bodies = []
        srv = yield ToyServer.from_test(self, Api().app)

        yield treq.get(
            srv.url,
            persistent=False,
            data=json.dumps({'foo': 23}))

        self.assertEqual(bodies, [{'foo': 23}])
    def test_retry_data(self):
        srv = yield ToyServer.from_test(self)
        contents = []

        @srv.app.route('/foo')
        def route(req):
            contents.append(req.content.read())

        yield retry({
            'owner_id': '1234',
            'timestamp': 5,
            'attempts': 0,
            'intervals': [10],
            'request': {
                'url': "%s/foo" % (srv.url,),
                'method': 'POST',
                'body': 'hi'
            }
        }, persistent=False)

        self.assertEqual(contents, ['hi'])
    def test_retry_no_dec_req_count_on_reattempt(self):
        worker = yield self.mk_worker()
        srv = yield ToyServer.from_test(self)

        @srv.app.route('/')
        def route(req):
            req.setResponseCode(500)

        yield set_req_count(worker.redis, 'test', '1234', 3)

        yield worker.retry({
            'owner_id': '1234',
            'timestamp': 5,
            'attempts': 0,
            'intervals': [10, 20],
            'request': {
                'url': srv.url,
                'method': 'GET'
            }
        })

        self.assertEqual(
            (yield get_req_count(worker.redis, 'test', '1234')), 3)