Example #1
0
class TestAuthenticatedCORS(TestCase):
    def setUp(self):

        def check_cred(username, *args, **kwargs):
            return [username]

        @implementer(IAuthorizationPolicy)
        class AuthorizationPolicy(object):
            def permits(self, context, principals, permission):
                return permission in principals

        self.config = testing.setUp()
        self.config.include('cornice')
        self.config.add_route('noservice', '/noservice')
        self.config.set_authorization_policy(AuthorizationPolicy())
        self.config.set_authentication_policy(BasicAuthAuthenticationPolicy(
            check_cred))
        self.config.set_default_permission('readwrite')
        self.config.scan('cornice.tests.test_cors')
        self.app = TestApp(CatchErrors(self.config.make_wsgi_app()))

    def tearDown(self):
        testing.tearDown()

    def test_post_on_spam_should_be_forbidden(self):
        self.app.post('/spam', status=403)

    def test_preflight_does_not_need_authentication(self):
        self.app.options('/spam', status=200,
                         headers={'Origin': 'notmyidea.org',
                                  'Access-Control-Request-Method': 'POST'})
Example #2
0
class TestAuthenticatedCORS(TestCase):
    def setUp(self):
        def check_cred(username, *args, **kwargs):
            return [username]

        @implementer(IAuthorizationPolicy)
        class AuthorizationPolicy(object):
            def permits(self, context, principals, permission):
                return permission in principals

        self.config = testing.setUp()
        self.config.include('cornice')
        self.config.add_route('noservice', '/noservice')
        self.config.set_authorization_policy(AuthorizationPolicy())
        self.config.set_authentication_policy(
            BasicAuthAuthenticationPolicy(check_cred))
        self.config.set_default_permission('readwrite')
        self.config.scan('cornice.tests.test_cors')
        self.app = TestApp(CatchErrors(self.config.make_wsgi_app()))

    def tearDown(self):
        testing.tearDown()

    def test_post_on_spam_should_be_forbidden(self):
        self.app.post('/spam', status=403)

    def test_preflight_does_not_need_authentication(self):
        self.app.options('/spam',
                         status=200,
                         headers={
                             'Origin': 'notmyidea.org',
                             'Access-Control-Request-Method': 'POST'
                         })
 def test_collection_options(self):
     wsgiapp = self.config.make_wsgi_app()
     app = TestApp(wsgiapp)
     request = testing.DummyRequest()
     apply_request_extensions(request)
     self._fixture(request)
     headers = (('Access-Control-Request-Method', 'POST'),
                ('Origin', 'http://localhost'))
     app.options('/api/1/walls/2/collections', status=200, headers=headers)
Example #4
0
    def test_singular_resource(self, *a):
        View = get_test_view_class()
        config = _create_config()
        root = config.get_root_resource()
        root.add('thing', view=View)
        grandpa = root.add('grandpa', 'grandpas', view=View)
        wife = grandpa.add('wife', view=View, renderer='string')
        wife.add('child', 'children', view=View)

        config.begin()
        app = TestApp(config.make_wsgi_app())

        self.assertEqual(
            '/grandpas/1/wife',
            route_path('grandpa:wife', testing.DummyRequest(), grandpa_id=1)
        )

        self.assertEqual(
            '/grandpas/1',
            route_path('grandpa', testing.DummyRequest(), id=1)
        )

        self.assertEqual(
            '/grandpas/1/wife/children/2',
            route_path('grandpa_wife:child', testing.DummyRequest(),
                       grandpa_id=1, id=2)
        )

        self.assertEqual(app.put('/grandpas').body, six.b('update_many'))
        self.assertEqual(app.head('/grandpas').body, six.b(''))
        self.assertEqual(app.options('/grandpas').body, six.b(''))

        self.assertEqual(app.delete('/grandpas/1').body, six.b('delete'))
        self.assertEqual(app.head('/grandpas/1').body, six.b(''))
        self.assertEqual(app.options('/grandpas/1').body, six.b(''))

        self.assertEqual(app.put('/thing').body, six.b('replace'))
        self.assertEqual(app.patch('/thing').body, six.b('update'))
        self.assertEqual(app.delete('/thing').body, six.b('delete'))
        self.assertEqual(app.head('/thing').body, six.b(''))
        self.assertEqual(app.options('/thing').body, six.b(''))

        self.assertEqual(app.put('/grandpas/1/wife').body, six.b('replace'))
        self.assertEqual(app.patch('/grandpas/1/wife').body, six.b('update'))
        self.assertEqual(app.delete('/grandpas/1/wife').body, six.b('delete'))
        self.assertEqual(app.head('/grandpas/1/wife').body, six.b(''))
        self.assertEqual(app.options('/grandpas/1/wife').body, six.b(''))

        self.assertEqual(six.b('show'), app.get('/grandpas/1').body)
        self.assertEqual(six.b('show'), app.get('/grandpas/1/wife').body)
        self.assertEqual(
            six.b('show'), app.get('/grandpas/1/wife/children/1').body)
Example #5
0
    def test_singular_resource(self, *a):
        View = get_test_view_class()
        config = _create_config()
        root = config.get_root_resource()
        root.add('thing', view=View)
        grandpa = root.add('grandpa', 'grandpas', view=View)
        wife = grandpa.add('wife', view=View, renderer='string')
        wife.add('child', 'children', view=View)

        config.begin()
        app = TestApp(config.make_wsgi_app())

        self.assertEqual(
            '/grandpas/1/wife',
            route_path('grandpa:wife', testing.DummyRequest(), grandpa_id=1)
        )

        self.assertEqual(
            '/grandpas/1',
            route_path('grandpa', testing.DummyRequest(), id=1)
        )

        self.assertEqual(
            '/grandpas/1/wife/children/2',
            route_path('grandpa_wife:child', testing.DummyRequest(),
                       grandpa_id=1, id=2)
        )

        self.assertEqual(app.put('/grandpas').body, six.b('update_many'))
        self.assertEqual(app.head('/grandpas').body, six.b(''))
        self.assertEqual(app.options('/grandpas').body, six.b(''))

        self.assertEqual(app.delete('/grandpas/1').body, six.b('delete'))
        self.assertEqual(app.head('/grandpas/1').body, six.b(''))
        self.assertEqual(app.options('/grandpas/1').body, six.b(''))

        self.assertEqual(app.put('/thing').body, six.b('replace'))
        self.assertEqual(app.patch('/thing').body, six.b('update'))
        self.assertEqual(app.delete('/thing').body, six.b('delete'))
        self.assertEqual(app.head('/thing').body, six.b(''))
        self.assertEqual(app.options('/thing').body, six.b(''))

        self.assertEqual(app.put('/grandpas/1/wife').body, six.b('replace'))
        self.assertEqual(app.patch('/grandpas/1/wife').body, six.b('update'))
        self.assertEqual(app.delete('/grandpas/1/wife').body, six.b('delete'))
        self.assertEqual(app.head('/grandpas/1/wife').body, six.b(''))
        self.assertEqual(app.options('/grandpas/1/wife').body, six.b(''))

        self.assertEqual(six.b('show'), app.get('/grandpas/1').body)
        self.assertEqual(six.b('show'), app.get('/grandpas/1/wife').body)
        self.assertEqual(
            six.b('show'), app.get('/grandpas/1/wife/children/1').body)
Example #6
0
def test_web_basic():
    """The web basic test
    """
    class TestService(Service):
        """The test service
        """
        @get('/test/get')
        @post('/test/post')
        @put('/test/put')
        @delete('/test/delete')
        @head('/test/head')
        @patch('/test/patch')
        @options('/test/options')
        @endpoint()
        def test(self):
            """Test
            """
            return 'OK'

        @get('/test2')
        @endpoint()
        def test2(self, param):
            """Test 2
            """
            return 'OK'

    adapter = WebAdapter()
    server = Server([ TestService() ], [ adapter ])
    server.start()
    # Test
    app = TestApp(adapter)
    rsp = app.get('/test', expect_errors = True)
    assert rsp.status_int == 404
    rsp = app.get('/test/get')
    assert rsp.status_int == 200 and rsp.content_type == 'text/plain' and rsp.text == 'OK'
    rsp = app.post('/test/post')
    assert rsp.status_int == 200 and rsp.content_type == 'text/plain' and rsp.text == 'OK'
    rsp = app.put('/test/put')
    assert rsp.status_int == 200 and rsp.content_type == 'text/plain' and rsp.text == 'OK'
    rsp = app.delete('/test/delete')
    assert rsp.status_int == 200 and rsp.content_type == 'text/plain' and rsp.text == 'OK'
    rsp = app.head('/test/head')
    assert rsp.status_int == 200 and rsp.content_type == 'text/plain'
    rsp = app.patch('/test/patch')
    assert rsp.status_int == 200 and rsp.content_type == 'text/plain' and rsp.text == 'OK'
    rsp = app.options('/test/options')
    assert rsp.status_int == 200 and rsp.content_type == 'text/plain' and rsp.text == 'OK'
    # Too many parameters
    rsp = app.get('/test/get?a=1', expect_errors = True)
    assert rsp.status_int == 400
    # Lack of parameters
    rsp = app.get('/test2', expect_errors = True)
    assert rsp.status_int == 400
    rsp = app.get('/test2?param=1')
    assert rsp.status_int == 200 and rsp.text == 'OK'
Example #7
0
class PezWebAppTestCase(FreshSchemaTestCase):

    def __init__(self, *args, **kwargs):
        super(PezWebAppTestCase, self).__init__(*args, **kwargs)
        self.app = TestApp(application)

    def tearDown(self):
        super(PezWebAppTestCase, self).tearDown()
        self.app.reset()

    @parameterized.expand(route_response_map)
    def test_smoke(self, uri, result):
        response = self.app.get(uri)
        self.assertEqual(response.status, HTTP_OK)
        self.assertEqual(response.json, result)

    def test_shall_fail_on_le_zero_v2(self):
        response = self.app.get(
            '/v2/forward?count=0',
            expect_errors=True
        )
        self.assertEqual(response.status, HTTP_BAD_REQUEST)

    def test_shall_fail_on_gt_max_count_v2(self):
        max_count = configuration.get('max_count')
        response = self.app.get(
            '/v2/forward?count={0}'.format(max_count + 1),
            expect_errors=True
        )
        self.assertEqual(response.status, HTTP_BAD_REQUEST)
    
    def test_shall_fail_with_404(self):
        response = self.app.get('/WRONG_BANANAS', expect_errors=True)
        self.assertEqual(response.status, HTTP_NOT_FOUND)

    def test_shall_return_help_on_options(self):
        response = self.app.options('/v2/forward')
        self.assertEqual(response.status, HTTP_OK)
        self.assertIn(u'description', response.json)
        self.assertIn(u'verbs', response.json)

    def test_shall_return_503_on_dead_db(self):
        with patch('sqlalchemy.orm.session.Session.execute') as mock_exec:
            mock_exec.side_effect = DBAPIError('Boom', 'mock', 'mock')
            response = self.app.get(
                '/v1/forward',
                expect_errors=True
            )
            self.assertEqual(response.status, HTTP_SERVICE_UNAVAILABLE)
Example #8
0
    def test_option_handler(self):

        def raises_app(environ, start_response):
            raise IOError('bad')

        option_app = cors_option_filter_factory(raises_app)
        app = cors_filter_factory(option_app)

        testapp = TestApp(app)
        res = testapp.options('/the_path_doesnt_matter',
                              extra_environ={
                                  'HTTP_ORIGIN': 'http://example.org'},
                              status=200)

        assert_that(res.headers, has_key('Access-Control-Allow-Methods'))

        # Non-options pass through
        res = testapp.get('/',
                          extra_environ={
                              'HTTP_ORIGIN': 'http://example.org'},
                          status=500)
        assert_that(res.headers, has_key('Access-Control-Allow-Origin'))
Example #9
0
class RouterTestCase(unittest.TestCase):
    def __init__(self, methodName='runTest'):
        super().__init__(methodName)

    def setRouter(self, app):
        self._app = TestApp(app.make_handler())

    def get(self, path_info, headers=[]):
        resp = self._app.get(path_info, headers=headers, status='*')
        return It(resp, self)

    def options(self, path_info, headers=[]):
        resp = self._app.options(path_info, headers=headers, status='*')
        return It(resp, self)

    def delete(self, path_info, headers=[]):
        resp = self._app.delete(path_info, headers=headers, status='*')
        return It(resp, self)

    def post(self, path_info, json=None, headers=[]):
        if json:
            resp = self._app.post_json(path_info, json,
                                       headers=headers, status='*')
        return It(resp, self)
Example #10
0
class TestCorkscrew(unittest.TestCase):
    def setUp(self):
        database.initialize(SqliteDatabase(":memory:"))
        insertFixtures()

        app = CorkscrewApplication(PHF)
        app.register(Comment, endpoint="/comments")

        app.register(Person, related={"articles": Article}, endpoint="/people")

        app.register(Photo,
                     related={"tags": Link(Tag, via=PhotoTag)},
                     endpoint="/photos")

        app.register(Article,
                     related={
                         "comments": Comment,
                         "revisions": Link(Revision, on="parent")
                     },
                     endpoint="/articles")

        self.app = TestApp(app)

    def tearDown(self):
        database.close()

    def testList(self):
        result = self.app.get("/articles")
        self.assertEqual(result.status, "200 OK")

        JsonAPIValidator.validate_content_type(result.content_type)
        self.assertIsNotNone(result.json)

        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIn("data", result.json)
        self.assertIs(len(result.json["data"]), len(ARTICLE_TITLES))
        for row in result.json["data"]:
            self.assertEqual(row["type"], "article")
            self.assertIn("attributes", row)

            # expected attributes: title, created
            self.assertIs(len(row["attributes"]), 2)
            self.assertIn("title", row["attributes"])
            self.assertIn("created", row["attributes"])
            self.assertNotIn("author", row["attributes"])
            self.assertIn(row["attributes"]["title"], ARTICLE_TITLES)
            self.assertIn("relationships", row)

            for key, relationship in row["relationships"].iteritems():
                self.assertIn(key,
                              ["comments", "cover", "author", "revisions"])
                self.assertIn("links", relationship)
                self.assertIn("related", relationship["links"])
                self.assertIn("self", relationship["links"])

    def testGet(self):
        result = self.app.get("/articles/1")
        self.assertEqual(result.status, "200 OK")
        self.assertIsNotNone(result.json)

        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIn("data", result.json)

        # we want a single result
        self.assertEqual(type(result.json["data"]), type({}))
        self.assertIn("attributes", result.json["data"])

        attributes = result.json["data"]["attributes"]
        self.assertEqual(attributes["title"], ARTICLE_TITLES[0])

        self.assertIn("relationships", result.json["data"])

        for key, rel in result.json["data"]["relationships"].iteritems():
            self.assertIn(key, ["comments", "cover", "author", "revisions"])
            self.assertIn("links", rel)
            self.assertIn("related", rel["links"])
            self.assertIn("self", rel["links"])

        self.assertIsInstance(
            result.json["data"]["relationships"]["comments"]["data"], list)

    def testPost(self):
        request = {
            u"data": {
                u"type": u"article",
                u"attributes": {
                    u"title": u"Test entry"
                },
                u"relationships": {
                    u"author": {
                        u"data": {
                            u"id": u"1",
                            u"type": u"person"
                        }
                    }
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request, True)

        result = self.app.post_json("/articles", params=request)
        self.assertEqual(result.status, "200 OK")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIn("relationships", result.json["data"])

        for key, rel in result.json["data"]["relationships"].iteritems():
            self.assertIn(key, ["comments", "cover", "author", "revisions"])
            self.assertIn("links", rel)
            self.assertIn("related", rel["links"])
            self.assertIn("self", rel["links"])

    def testPatch(self):
        request = {
            u"data": {
                u"type": u"article",
                u"id": u"1",
                u"attributes": {
                    u"title": u"Changed First Entry"
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request)

        result = self.app.patch_json("/articles/1", params=request)
        self.assertIn(result.status,
                      ["202 Accepted", "200 OK", "204 No Content"])

        if result.status == "204 No Content":
            # nothing more to test
            return

        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertEqual(result.json["data"]["attributes"]["title"],
                         "Changed First Entry")

    def testDelete(self):
        result = self.app.delete("/articles/1")
        self.assertIn(result.status,
                      ["202 Accepted", "204 No Content", "200 OK"])

    def testGetRelated(self):
        result = self.app.get("/articles/1")
        self.assertEqual(result.status, "200 OK")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        result = self.app.get(
            result.json["data"]["relationships"]["author"]["links"]["related"])

        self.assertEqual(result.status, "200 OK")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIn("data", result.json)
        self.assertEqual(type(result.json["data"]), type({}))
        self.assertEqual(result.json["data"]["type"], "person")
        self.assertEqual(result.json["data"]["id"], "1")

    def testGetRelationship(self):
        result = self.app.get("/articles/1")
        self.assertEqual(result.status, "200 OK")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        result = self.app.get(
            result.json["data"]["relationships"]["author"]["links"]["self"])

        self.assertEqual(result.status, "200 OK")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIn("data", result.json)
        self.assertIsInstance(result.json["data"], dict)
        self.assertEqual(result.json["data"]["type"], "person")
        self.assertEqual(result.json["data"]["id"], "1")

    def testPatchRelationship(self):
        result = self.app.get("/articles/1")
        self.assertEqual(result.status, "200 OK")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        rel = result.json["data"]["relationships"]["author"]["links"]["self"]

        request = {u"data": {u"type": u"person", u"id": u"2"}}

        JsonAPIValidator.validate_jsonapi(request)

        result = self.app.patch_json(rel, params=request)
        self.assertIn(result.status,
                      ["200 OK", "202 Accepted", "204 No Content"])

        if result.status == "204 No Content":
            self.assertIs(len(result.body), 0)
        elif result.status == "200 OK":
            self.assertIsNotNone(result.json)
            JsonAPIValidator.validate_jsonapi(result.json)

    def testFetchingDataCollection(self):
        result = self.app.get("/articles")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertEqual(result.status, "200 OK")
        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIs(len(result.json["data"]), 2)
        for entry in result.json["data"]:
            self.assertEqual(entry["type"], "article")
            self.assertIsInstance(entry["id"], unicode)
            self.assertIn(entry["attributes"]["title"], ARTICLE_TITLES)

        Article.delete().where(True).execute()
        result = self.app.get("/articles")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertEqual(result.status, "200 OK")
        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIs(len(result.json["data"]), 0)

    def testFetchingNullRelationship(self):
        result = self.app.get("/articles/1")
        rel = result.json["data"]["relationships"]["cover"]["links"]["related"]

        result = self.app.get(rel)
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertEqual(result.status, "200 OK")
        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIsNone(result.json["data"])

    def testFetchingMissingSingleResource(self):
        result = self.app.get("/article/1337", status=404)
        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

    def testCreatingResourceWithReferences(self):
        request = {
            u"data": {
                u"type": u"photo",
                u"attributes": {
                    u"title": u"Ember Hamster",
                    u"src": u"http://example.com/images/productivity.png"
                },
                u"relationships": {
                    u"photographer": {
                        u"data": {
                            u"type": u"people",
                            u"id": u"2"
                        }
                    }
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request, True)

        result = self.app.post_json("/photos", params=request)
        JsonAPIValidator.validate_content_type(result.content_type)
        JsonAPIValidator.validate_jsonapi(result.json)

        if not result.location:
            warnings.warn(
                "The response SHOULD include a Location header identifying the"
                "location of the newly created resource.")

        else:
            res = self.app.get(result.location)
            self.assertIsNotNone(res.json)
            JsonAPIValidator.validate_jsonapi(res.json)

    def testCreatingResourceWithMissingRequiredAttributeShouldFail(self):
        request = {
            u"data": {
                u"type": u"person",
                u"attributes": {
                    u"name": u"Eve Bobbington"
                    # attribute 'age' is missing
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request, True)

        result = self.app.post_json("/people", params=request, status=400)
        JsonAPIValidator.validate_content_type(result.content_type)
        JsonAPIValidator.validate_jsonapi(result.json)

    def testCreateResourceWithAlreadyExistingId(self):
        request = {
            u"data": {
                u"type": u"person",
                u"id": u"1",
                u"attributes": {
                    u"name": "Jimmy Cricket",
                    u"age": 12
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request)

        # expect this to fail
        result = self.app.post_json("/people", params=request, status=409)
        JsonAPIValidator.validate_jsonapi(result.json)

    def testUpdatingResourceViaSelfLink(self):
        UPDATE_TITLE = u"Five Ways You Have Never Tried To Access Your Data"

        result = self.app.get("/articles/1")
        update_uri = result.json["data"]["links"]["self"]

        request = {
            u"data": {
                u"type": u"article",
                u"id": u"1",
                u"attributes": {
                    u"title": UPDATE_TITLE
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request)
        res = self.app.patch_json(update_uri, params=request)

        if "204" not in res.status:
            JsonAPIValidator.validate_content_type(res.content_type)

        res = self.app.get("/articles/1")
        self.assertEqual(res.json["data"]["attributes"]["title"], UPDATE_TITLE)

    def testUpdatingResourceRelationships(self):
        result = self.app.get("/articles/1")
        request = result.json

        # Person(1) is the current author
        self.assertEqual(
            result.json["data"]["relationships"]["author"]["data"]["id"], "1")

        # don't update attributes, server must ignore missing attributes
        del request["data"]["attributes"]

        # do not update the 'comments' and 'cover' relationships
        del request["data"]["relationships"]["comments"]
        del request["data"]["relationships"]["cover"]
        del request["data"]["relationships"]["revisions"]

        ptype = request["data"]["relationships"]["author"]["data"]["type"]

        # change author to Person(2)
        request["data"]["relationships"]["author"] = {
            u"data": {
                u"id": u"2",
                u"type": ptype
            }
        }

        JsonAPIValidator.validate_jsonapi(request)

        result = self.app.patch_json("/articles/1", params=request)
        result = self.app.get("/articles/1")

        self.assertNotEqual(
            result.json["data"]["relationships"]["author"]["data"]["id"], "1")

        self.assertEqual(
            result.json["data"]["relationships"]["author"]["data"]["id"], "2")

        self.assertEqual(result.json["data"]["attributes"]["title"],
                         ARTICLE_TITLES[0])

    def testDeletingIndividualResource(self):
        result = self.app.get("/photos/1")
        JsonAPIValidator.validate_jsonapi(result.json)

        result = self.app.delete("/photos/1")

        if result.status_int not in [202, 204, 200]:
            warnings.warn("Delete: A server MAY respond with other HTTP status"
                          "codes. This code is unknown to the specification.")

        if result.status_int == 200:
            JsonAPIValidator.validate_jsonapi(result.json)

        # the resource should be gone now
        self.app.get("/photos/1", status=404)

    def testFetchingRelatedOneToNResource(self):
        result = self.app.get("/articles/1/comments")
        JsonAPIValidator.validate_jsonapi(result.json)

        for entry in result.json["data"]:
            self.assertIn(entry["attributes"]["body"], COMMENT_BODIES)
            self.assertEqual(entry["relationships"]["author"]["data"]["id"],
                             "2")
            self.assertEqual(entry["relationships"]["article"]["data"]["id"],
                             "1")

    def testListingRelatedOneToNResource(self):
        result = self.app.get("/articles/1/relationships/comments")
        JsonAPIValidator.validate_jsonapi(result.json)

        for entry in result.json["data"]:
            JsonAPIValidator.validate_resource_identifier(entry)

    def testPatchingRelatedOneToNResourceShouldFail(self):
        result = self.app.get("/people/1/articles")

        self.assertIs(len(result.json["data"]), 2)
        for entry in result.json["data"]:
            self.assertIn(entry["attributes"]["title"], ARTICLE_TITLES)

        # it is not allowed to orphan an article
        request = {
            u"data": {
                u"id": u"1",
                u"type": u"person",
                u"relationships": {
                    u"articles": {
                        u"data": []
                    }
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request)

        result = self.app.patch_json("/people/1", params=request, status=400)
        JsonAPIValidator.validate_jsonapi(result.json)
        self.assertIn("orphan", result.json["errors"][0]["title"])

    def testPatchingRelatedOneToNResourceShouldSucceed(self):
        result = self.app.get("/articles/2/cover")
        self.assertIsInstance(result.json["data"], dict)

        request = {
            u"data": {
                u"id": u"2",
                u"type": u"article",
                u"relationships": {
                    u"cover": {
                        u"data": None
                    }
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request)

        result = self.app.patch_json("/articles/2", params=request)

        result = self.app.get("/articles/2/cover")
        self.assertIsNone(result.json["data"])

    def testPatchingRelatedOneToMResource(self):
        result = self.app.get("/articles/1/relationships/comments")
        self.assertIsInstance(result.json["data"], list)

        del result.json["data"][0]

        request = {u"data": result.json["data"]}

        JsonAPIValidator.validate(request)
        result = self.app.patch_json("/articles/1/relationships/comments",
                                     params=request)

    def testPatchingRelatedNToMResource(self):
        result = self.app.get("/photos/1/relationships/tags")
        self.assertIsInstance(result.json["data"], list)
        self.assertIs(len(result.json["data"]), 2)

        request = {u"data": result.json["data"]}

        # remove one tag
        request["data"].pop()

        self.app.patch_json("/photos/1/relationships/tags", params=request)

        result = self.app.get("/photos/1/relationships/tags")
        self.assertIsInstance(result.json["data"], list)
        self.assertIs(len(result.json["data"]), 1)

    def testGetNToMRelationship(self):
        result = self.app.get("/photos/1/tags")
        JsonAPIValidator.validate(result.json)

        self.assertIs(len(result.json["data"]), 2)

        for tag in result.json["data"]:
            self.assertIn(tag["attributes"]["name"], TAG_NAMES)

    def testValidateForwardRelationship(self):
        result = self.app.get("/photos")
        # Photo has a forward relationship to Person (photographer)
        for entry in result.json["data"]:
            self.assertIn("relationships", entry)
            for name, relationship in entry["relationships"].iteritems():
                self.assertIn(name, ["photographer", "tags"])

            relationship = entry["relationships"]["photographer"]
            data = relationship["data"]

            # retrieve the /relationships link
            subresult = self.app.get(relationship["links"]["self"])
            self.assertEqual(subresult.json["data"], data)

            # retrieve the related object
            subresult = self.app.get(relationship["links"]["related"])

            # type and id must match
            self.assertEqual(subresult.json["data"]["id"], data["id"])
            self.assertEqual(subresult.json["data"]["type"], data["type"])

            # retrieve the related self link and test if it's the same object
            subsubresult = self.app.get(
                subresult.json["data"]["links"]["self"])
            self.assertEqual(subresult.json["data"], subsubresult.json["data"])

    def testValidateReverseRelationships(self):
        result = self.app.get("/photos")
        # Photo has a reverse relationship to Tag (tags, via PhotoTag)
        for entry in result.json["data"]:
            self.assertIn("relationships", entry)
            for name, relationship in entry["relationships"].iteritems():
                self.assertIn(name, ["photographer", "tags"])

            relationship = entry["relationships"]["tags"]
            data = relationship["data"]

            # retrieve the /relationships link
            subresult = self.app.get(relationship["links"]["self"])
            self.assertEqual(subresult.json["data"], data)

            # retrieve the related objects
            subresult = self.app.get(relationship["links"]["related"])

            # type and id must match
            for subentry in subresult.json["data"]:
                self.assertIn({
                    "id": subentry["id"],
                    "type": subentry["type"]
                }, data)

                if "links" in subentry and "self" in subentry["links"]:
                    subsubresult = self.app.get(subentry["links"]["self"])
                    self.assertEqual(subentry, subsubresult.json["data"])

    def testIncludeParameterForwardRelationship(self):
        result = self.app.get("/articles/2?include=cover")
        JsonAPIValidator.validate(result.json)
        self.assertIn("included", result.json)

        # the server must not return any other fields than requested
        self.assertIs(len(result.json["included"]), 1)

        ref = result.json["data"]["relationships"]["cover"]["data"]
        inc = result.json["included"][0]

        self.assertEqual(inc["type"], ref["type"])
        self.assertEqual(inc["id"], ref["id"])

        # the self link must be valid and refer to the same object
        subresult = self.app.get(inc["links"]["self"])
        self.assertEqual(subresult.json["data"], inc)

    def testIncludeParameterReverseRelationship(self):
        result = self.app.get("/articles/1?include=comments")
        JsonAPIValidator.validate(result.json)
        self.assertIn("included", result.json)

        # the server must not return any other fields than requested
        self.assertIs(len(result.json["included"]), len(COMMENT_BODIES))

        refs = result.json["data"]["relationships"]["comments"]["data"]

        for inc in result.json["included"]:
            self.assertIn({"id": inc["id"], "type": inc["type"]}, refs)

            # the self link must be valid and refer to the same object
            subresult = self.app.get(inc["links"]["self"])
            self.assertEqual(subresult.json["data"], inc)
            self.assertEqual(subresult.json["links"]["self"],
                             inc["links"]["self"])

    def testIncludeParameterWithInvalidFields(self):
        self.app.get("/articles/1?include=invalid-field", status=400)
        self.app.get("/articles/1?include=author,invalid-field", status=400)

    def testIncludeParameterWithCircularRelationships(self):
        self.app.get("/articles/1?include=comments.articles", status=400)
        self.app.get("/articles/1?include=comments.articles.comments",
                     status=400)

    def testSparseFieldsets(self):
        result = self.app.get("/people/1?fields[person]=age")
        JsonAPIValidator.validate(result.json)

        self.assertNotIn("name", result.json["data"]["attributes"])
        self.assertIn("age", result.json["data"]["attributes"])

    def testSparseFieldsetsWithIncludedObjects(self):
        result = self.app.get("/articles/1?include=comments&fields[comment]=")
        JsonAPIValidator.validate(result.json)

        for inc in result.json["included"]:
            self.assertNotIn("attributes", inc)

        result = self.app.get(
            "/comments/1?include=article.author&fields[person]=age")
        JsonAPIValidator.validate(result.json)

        is_included = False
        for inc in result.json["included"]:
            if inc["type"] == "person":
                is_included = True
                self.assertIn("age", inc["attributes"])
                self.assertNotIn("name", inc["attributes"])

        self.assertIsNotNone(is_included)

    def testLinkWithSpecifiedField(self):
        result = self.app.get("/articles/1/relationships/revisions")
        JsonAPIValidator.validate(result.json)

    def testOPTIONSRequest(self):
        # all of these should return all 200 OK code
        self.app.options("/articles")
        self.app.options("/articles/1")
        self.app.options("/articles/1/cover")
        self.app.options("/articles/1/relationships/cover")
        self.app.options("/photos/2/tags")
        self.app.options("/articles/1?include=author")

    def testCORSHeaders(self):
        # simulate a browser
        result = self.app.options("/articles",
                                  headers={
                                      "Origin":
                                      "http://example.org",
                                      "Access-Control-Request-Method":
                                      "GET",
                                      "Access-Control-Request-Headers":
                                      "X-Requested-With"
                                  })

        self.assertIn("Access-Control-Allow-Origin", result.headers)
        self.assertIn("Access-Control-Allow-Methods", result.headers)
        self.assertEqual(result.headers["Access-Control-Allow-Origin"], "*")
        methods = map(
            lambda x: x.strip(),
            result.headers["Access-Control-Allow-Methods"].split(","))

        self.assertIn("GET", methods)
        self.assertIn("X-Requested-With".lower(),
                      result.headers["Access-Control-Allow-Headers"].lower())
Example #11
0
class TestAppMethods(unittest.TestCase):
    def setUp(self):
        self.app = TestApp(app)

    def test_get_index(self):
        resp = self.app.get('/')
        self.assertEqual('200 OK', resp.status)

        resp.mustcontain('<button>submit</button>')
        resp.mustcontain(no='<h2>Duplicated!!!</h2>')
        resp.mustcontain(no='<h2>Saved</h2>')

    def test_post_data_to_index(self):
        data = uid()
        for status in ['save', 'duplicated']:
            form = self.app.get('/').form

            form['data'] = data
            resp = form.submit()

            self.assertEqual('200 OK', resp.status)
            resp.mustcontain('<button>submit</button>')

            if status == 'save':
                resp.mustcontain('<h2>Saved</h2>')
                resp.mustcontain(no='<h2>Duplicated!!!</h2>')
            else:
                resp.mustcontain(no='<h2>Saved</h2>')
                resp.mustcontain('<h2>Duplicated!!!</h2>')

    def test_add_allow_origin(self):
        resp = self.app.get('/')
        self.assertEqual(('Access-Control-Allow-Origin', '*'), resp.headerlist[0])

        resp = self.app.post(
            '/api/exist',
            content_type='application/json',
            params=json.dumps({
                'data': ['a', 'b']
            })
        )
        self.assertEqual(('Access-Control-Allow-Origin', '*'), resp.headerlist[1])

    def test_options_work(self):
        resp = self.app.options('/')
        self.assertEqual('200 OK', resp.status)
        self.assertEqual({}, resp.json)

    def test_api_exist_work(self):
        data = [uid(), uid(), uid()]

        form = self.app.get('/').form
        form['data'] = data[0]
        resp = form.submit()
        self.assertEqual('200 OK', resp.status)

        form['data'] = data[2]
        resp = form.submit()
        self.assertEqual('200 OK', resp.status)

        resp = self.app.post(
            '/api/exist',
            content_type='application/json',
            params=json.dumps({
                'data': data
            })
        )
        self.assertEqual({'data': [1, 0, 1]}, resp.json)

    def test_api_exist_substring(self):
        data = ['1234', '123']

        form = self.app.get('/').form
        form['data'] = data[0]
        resp = form.submit()
        self.assertEqual('200 OK', resp.status)

        resp = self.app.post(
            '/api/exist',
            content_type='application/json',
            params=json.dumps({
                'data': data
            })
        )
        self.assertEqual({'data': [1, 1]}, resp.json)

    def test_api_post(self):
        data = [uid(), uid(), uid()]

        resp = self.app.post(
            '/api/post',
            content_type='application/json',
            params=json.dumps({
                'data': data
            })
        )
        self.assertEqual('200 OK', resp.status)

        resp = self.app.post(
            '/api/exist',
            content_type='application/json',
            params=json.dumps({
                'data': data
            })
        )
        self.assertEqual({'data': [1, 1, 1]}, resp.json)
Example #12
0
class TestCORS(TestCase):

    def setUp(self):
        self.config = testing.setUp()
        self.config.include("cornice")
        self.config.add_route('noservice', '/noservice')

        self.config.scan("cornice.tests.test_cors")
        self.app = TestApp(CatchErrors(self.config.make_wsgi_app()))

        def tearDown(self):
            testing.tearDown()

    def test_preflight_cors_klass_post(self):
        resp = self.app.options('/cors_klass',
                                status=200,
                                headers={
                                    'Origin': 'lolnet.org',
                                    'Access-Control-Request-Method': 'POST'})

    def test_preflight_cors_klass_put(self):
        resp = self.app.options('/cors_klass',
                                status=400,
                                headers={
                                    'Origin': 'lolnet.org',
                                    'Access-Control-Request-Method': 'PUT'})

    def test_preflight_missing_headers(self):
        # we should have an OPTION method defined.
        # If we just try to reach it, without using correct headers:
        # "Access-Control-Request-Method"or without the "Origin" header,
        # we should get a 400.
        resp = self.app.options('/squirel', status=400)
        self.assertEqual(len(resp.json['errors']), 2)

    def test_preflight_missing_origin(self):

        resp = self.app.options(
            '/squirel',
            headers={'Access-Control-Request-Method': 'GET'},
            status=400)
        self.assertEqual(len(resp.json['errors']), 1)

    def test_preflight_missing_request_method(self):

        resp = self.app.options(
            '/squirel',
            headers={'Origin': 'foobar.org'},
            status=400)

        self.assertEqual(len(resp.json['errors']), 1)

    def test_preflight_incorrect_origin(self):
        # we put "lolnet.org" where only "notmyidea.org" is authorized
        resp = self.app.options(
            '/squirel',
            headers={'Origin': 'lolnet.org',
                     'Access-Control-Request-Method': 'GET'},
            status=400)
        self.assertEqual(len(resp.json['errors']), 1)

    def test_preflight_correct_origin(self):
        resp = self.app.options(
            '/squirel',
            headers={'Origin': 'notmyidea.org',
                     'Access-Control-Request-Method': 'GET'})
        self.assertEqual(
            resp.headers['Access-Control-Allow-Origin'],
            'notmyidea.org')

        allowed_methods = (resp.headers['Access-Control-Allow-Methods']
                           .split(','))

        self.assertNotIn('POST', allowed_methods)
        self.assertIn('GET', allowed_methods)
        self.assertIn('PUT', allowed_methods)
        self.assertIn('HEAD', allowed_methods)

        allowed_headers = (resp.headers['Access-Control-Allow-Headers']
                           .split(','))

        self.assertIn('X-My-Header', allowed_headers)
        self.assertNotIn('X-Another-Header', allowed_headers)

    def test_preflight_deactivated_method(self):
        self.app.options('/squirel',
            headers={'Origin': 'notmyidea.org',
                     'Access-Control-Request-Method': 'POST'},
            status=400)

    def test_preflight_origin_not_allowed_for_method(self):
        self.app.options('/squirel',
            headers={'Origin': 'notmyidea.org',
                     'Access-Control-Request-Method': 'PUT'},
            status=400)

    def test_preflight_credentials_are_supported(self):
        resp = self.app.options('/spam',
            headers={'Origin': 'notmyidea.org',
                     'Access-Control-Request-Method': 'GET'})

        self.assertIn('Access-Control-Allow-Credentials', resp.headers)
        self.assertEqual(resp.headers['Access-Control-Allow-Credentials'],
                          'true')

    def test_preflight_credentials_header_not_included_when_not_needed(self):
        resp = self.app.options('/spam',
            headers={'Origin': 'notmyidea.org',
                     'Access-Control-Request-Method': 'POST'})

        self.assertNotIn('Access-Control-Allow-Credentials', resp.headers)

    def test_preflight_contains_max_age(self):
        resp = self.app.options('/spam',
                headers={'Origin': 'notmyidea.org',
                         'Access-Control-Request-Method': 'GET'})

        self.assertIn('Access-Control-Max-Age', resp.headers)
        self.assertEqual(resp.headers['Access-Control-Max-Age'], '42')

    def test_resp_dont_include_allow_origin(self):
        resp = self.app.get('/squirel')  # omit the Origin header
        self.assertNotIn('Access-Control-Allow-Origin', resp.headers)
        self.assertEqual(resp.json, 'squirels')

    def test_responses_include_an_allow_origin_header(self):
        resp = self.app.get('/squirel', headers={'Origin': 'notmyidea.org'})
        self.assertIn('Access-Control-Allow-Origin', resp.headers)
        self.assertEqual(resp.headers['Access-Control-Allow-Origin'],
                          'notmyidea.org')

    def test_credentials_are_included(self):
        resp = self.app.get('/spam', headers={'Origin': 'notmyidea.org'})
        self.assertIn('Access-Control-Allow-Credentials', resp.headers)
        self.assertEqual(resp.headers['Access-Control-Allow-Credentials'],
                          'true')

    def test_headers_are_exposed(self):
        resp = self.app.get('/squirel', headers={'Origin': 'notmyidea.org'})
        self.assertIn('Access-Control-Expose-Headers', resp.headers)

        headers = resp.headers['Access-Control-Expose-Headers'].split(',')
        self.assertIn('X-My-Header', headers)

    def test_preflight_request_headers_are_included(self):
        resp = self.app.options('/squirel',
            headers={'Origin': 'notmyidea.org',
                     'Access-Control-Request-Method': 'GET',
                     'Access-Control-Request-Headers': 'foo,    bar,baz  '})
        # The specification says we can have any number of LWS (Linear white
        # spaces) in the values and that it should be removed.

        # per default, they should be authorized, and returned in the list of
        # authorized headers
        headers = resp.headers['Access-Control-Allow-Headers'].split(',')
        self.assertIn('foo', headers)
        self.assertIn('bar', headers)
        self.assertIn('baz', headers)

    def test_preflight_request_headers_isnt_too_permissive(self):
        self.app.options('/eggs',
            headers={'Origin': 'notmyidea.org',
                     'Access-Control-Request-Method': 'GET',
                     'Access-Control-Request-Headers': 'foo,bar,baz'},
            status=400)

    def test_preflight_headers_arent_case_sensitive(self):
        self.app.options('/spam', headers={
            'Origin': 'notmyidea.org',
            'Access-Control-Request-Method': 'GET',
            'Access-Control-Request-Headers': 'x-my-header', })

    def test_400_returns_CORS_headers(self):
        resp = self.app.get('/bacon/not', status=400,
                            headers={'Origin': 'notmyidea.org'})
        self.assertIn('Access-Control-Allow-Origin', resp.headers)

    def test_404_returns_CORS_headers(self):
        resp = self.app.get('/bacon/notgood', status=404,
                            headers={'Origin': 'notmyidea.org'})
        self.assertIn('Access-Control-Allow-Origin', resp.headers)

    def test_existing_non_service_route(self):
        resp = self.app.get('/noservice', status=200,
                            headers={'Origin': 'notmyidea.org'})
        self.assertEqual(resp.body, b'No Service here.')
Example #13
0
class AppUser:
    """:class:`webtest.TestApp` wrapper for backend functional testing."""
    def __init__(self,
                 app,
                 rest_url: str = 'http://localhost',
                 base_path: str = '/',
                 header: dict = None):
        """Initialize self."""
        self.app = TestApp(app)
        """:class:`webtest.TestApp`to send requests to the backend server."""
        self.rest_url = rest_url
        """backend server url to generate request urls."""
        self.base_path = base_path
        """path prefix to generate request urls."""
        self.header = header or {}
        """default header for requests, mostly for authentication."""
        self._resolver = DottedNameResolver()

    def post_resource(self, path: str, iresource: IInterface,
                      cstruct: dict) -> TestResponse:
        """Build and post request to create a new resource."""
        url = self._build_url(path)
        props = self._build_post_body(iresource, cstruct)
        resp = self.app.post_json(url,
                                  props,
                                  headers=self.header,
                                  expect_errors=True)
        return resp

    def put(self, path: str, cstruct: dict = {}) -> TestResponse:
        """Put request to modify a resource."""
        url = self._build_url(path)
        resp = self.app.put_json(url,
                                 cstruct,
                                 headers=self.header,
                                 expect_errors=True)
        return resp

    def post(self, path: str, cstruct: dict = {}) -> TestResponse:
        """Post request to create a new resource."""
        url = self._build_url(path)
        resp = self.app.post_json(url,
                                  cstruct,
                                  headers=self.header,
                                  expect_errors=True)
        return resp

    def _build_post_body(self, iresource: IInterface, cstruct: dict) -> dict:
        return {'content_type': iresource.__identifier__, 'data': cstruct}

    def _build_url(self, path: str) -> str:
        if path.startswith('http'):
            return path
        return self.rest_url + self.base_path + path

    def batch(self, subrequests: list):
        """Build and post batch request to the backend rest server."""
        resp = self.app.post_json(batch_url,
                                  subrequests,
                                  headers=self.header,
                                  expect_errors=True)
        return resp

    def get(self, path: str, params={}) -> TestResponse:
        """Send get request to the backend rest server."""
        url = self._build_url(path)
        resp = self.app.get(url,
                            headers=self.header,
                            params=params,
                            expect_errors=True)
        return resp

    def options(self, path: str) -> TestResponse:
        """Send options request to the backend rest server."""
        url = self._build_url(path)
        resp = self.app.options(url, headers=self.header, expect_errors=True)
        return resp

    def get_postable_types(self, path: str) -> []:
        """Send options request and return the postable content types."""
        resp = self.options(path)
        if 'POST' not in resp.json:
            return []
        post_request_body = resp.json['POST']['request_body']
        type_names = sorted([r['content_type'] for r in post_request_body])
        iresources = [self._resolver.resolve(t) for t in type_names]
        return iresources
Example #14
0
class TestHttplib(unittest.TestCase):

    client = 'httplib'
    client_options = {}

    def setUp(self):
        self.server = StopableWSGIServer.create(debug_app)
        self.application_url = self.server.application_url.rstrip('/')
        self.proxy = proxies.HostProxy(self.application_url,
                                       client=self.client,
                                       **self.client_options)
        self.app = TestApp(self.proxy)

    def test_form(self):
        resp = self.app.get('/form.html')
        resp.mustcontain('</form>')
        form = resp.form
        form['name'] = 'gawel'
        resp = form.submit()
        resp.mustcontain('name=gawel')

    def test_head(self):
        resp = self.app.head('/form.html')
        self.assertEqual(resp.status_int, 200)
        self.assertEqual(len(resp.body), 0)

    def test_webob_error(self):
        req = Request.blank('/')
        req.content_length = '-1'
        resp = req.get_response(self.proxy)
        self.assertEqual(resp.status_int, 500, resp)

    def test_not_allowed_method(self):
        resp = self.app.options('/', status='*')
        self.assertEqual(resp.status_int, 405)

    def test_status(self):
        resp = self.app.get('/?status=404', status='*')
        self.assertEqual(resp.status_int, 404)

    def test_redirect(self):
        location = self.application_url + '/form.html'
        resp = self.app.get(
                '/?status=301%20Redirect&header-location=' + location,
                status='*')
        self.assertEqual(resp.status_int, 301, resp)
        self.assertEqual(resp.location, location)

        location = 'http://foo.com'
        resp = self.app.get(
                '/?status=301%20Redirect&header-location=' + location,
                status='*')
        self.assertEqual(resp.status_int, 301, resp)
        self.assertEqual(resp.location, location)

        location = '/foo'
        resp = self.app.get(
                '/?status=301%20Redirect&header-location=' + location,
                status='*')
        self.assertEqual(resp.status_int, 301, resp)
        self.assertEqual(resp.location, self.application_url + location)

        location = self.application_url + '/script_name/form.html'
        self.proxy.strip_script_name = False
        resp = self.app.get(
                '/?status=301%20Redirect&header-Location=' + location,
                status='*', extra_environ={'SCRIPT_NAME': '/script_name'})
        self.assertEqual(resp.status_int, 301, resp)
        self.assertEqual(resp.location, location)

    def test_chunked(self):
        resp = self.app.get('/',
                            headers=[('Transfer-Encoding', 'chunked')])
        resp.mustcontain(no='chunked')

    def tearDown(self):
        self.server.shutdown()
Example #15
0
class TestCorkscrew(unittest.TestCase):

    def setUp(self):
        database.initialize(SqliteDatabase(":memory:"))
        insertFixtures()

        app = CorkscrewApplication(PHF)
        app.register(Comment, endpoint="/comments")

        app.register(
            Person,
            related={"articles": Article},
            endpoint="/people"
        )

        app.register(
            Photo,
            related={"tags": Link(Tag, via=PhotoTag)},
            endpoint="/photos"
        )

        app.register(
            Article,
            related={
                "comments": Comment,
                "revisions": Link(Revision, on="parent")
            },
            endpoint="/articles"
        )

        self.app = TestApp(app)

    def tearDown(self):
        database.close()

    def testList(self):
        result = self.app.get("/articles")
        self.assertEqual(result.status, "200 OK")

        JsonAPIValidator.validate_content_type(result.content_type)
        self.assertIsNotNone(result.json)

        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIn("data", result.json)
        self.assertIs(len(result.json["data"]), len(ARTICLE_TITLES))
        for row in result.json["data"]:
            self.assertEqual(row["type"], "article")
            self.assertIn("attributes", row)

            # expected attributes: title, created
            self.assertIs(len(row["attributes"]), 2)
            self.assertIn("title", row["attributes"])
            self.assertIn("created", row["attributes"])
            self.assertNotIn("author", row["attributes"])
            self.assertIn(row["attributes"]["title"], ARTICLE_TITLES)
            self.assertIn("relationships", row)

            for key, relationship in row["relationships"].iteritems():
                self.assertIn(
                    key,
                    ["comments", "cover", "author", "revisions"]
                )
                self.assertIn("links", relationship)
                self.assertIn("related", relationship["links"])
                self.assertIn("self", relationship["links"])

    def testGet(self):
        result = self.app.get("/articles/1")
        self.assertEqual(result.status, "200 OK")
        self.assertIsNotNone(result.json)

        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIn("data", result.json)

        # we want a single result
        self.assertEqual(type(result.json["data"]), type({}))
        self.assertIn("attributes", result.json["data"])

        attributes = result.json["data"]["attributes"]
        self.assertEqual(attributes["title"], ARTICLE_TITLES[0])

        self.assertIn("relationships", result.json["data"])

        for key, rel in result.json["data"]["relationships"].iteritems():
            self.assertIn(key, ["comments", "cover", "author", "revisions"])
            self.assertIn("links", rel)
            self.assertIn("related", rel["links"])
            self.assertIn("self", rel["links"])

        self.assertIsInstance(
            result.json["data"]["relationships"]["comments"]["data"],
            list
        )

    def testPost(self):
        request = {
            u"data": {
                u"type": u"article",
                u"attributes": {
                    u"title": u"Test entry"
                },
                u"relationships": {
                    u"author": {
                        u"data": {u"id": u"1", u"type": u"person"}
                    }
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request, True)

        result = self.app.post_json("/articles", params=request)
        self.assertEqual(result.status, "200 OK")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIn("relationships", result.json["data"])

        for key, rel in result.json["data"]["relationships"].iteritems():
            self.assertIn(key, ["comments", "cover", "author", "revisions"])
            self.assertIn("links", rel)
            self.assertIn("related", rel["links"])
            self.assertIn("self", rel["links"])

    def testPatch(self):
        request = {
            u"data": {
                u"type": u"article",
                u"id": u"1",
                u"attributes": {
                    u"title": u"Changed First Entry"
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request)

        result = self.app.patch_json("/articles/1", params=request)
        self.assertIn(
            result.status,
            ["202 Accepted", "200 OK", "204 No Content"]
        )

        if result.status == "204 No Content":
            # nothing more to test
            return

        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertEqual(
            result.json["data"]["attributes"]["title"],
            "Changed First Entry"
        )

    def testDelete(self):
        result = self.app.delete("/articles/1")
        self.assertIn(
            result.status,
            ["202 Accepted", "204 No Content", "200 OK"]
        )

    def testGetRelated(self):
        result = self.app.get("/articles/1")
        self.assertEqual(result.status, "200 OK")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        result = self.app.get(
            result.json["data"]["relationships"]["author"]["links"]["related"]
        )

        self.assertEqual(result.status, "200 OK")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIn("data", result.json)
        self.assertEqual(type(result.json["data"]), type({}))
        self.assertEqual(result.json["data"]["type"], "person")
        self.assertEqual(result.json["data"]["id"], "1")

    def testGetRelationship(self):
        result = self.app.get("/articles/1")
        self.assertEqual(result.status, "200 OK")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        result = self.app.get(
            result.json["data"]["relationships"]["author"]["links"]["self"]
        )

        self.assertEqual(result.status, "200 OK")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIn("data", result.json)
        self.assertIsInstance(result.json["data"], dict)
        self.assertEqual(result.json["data"]["type"], "person")
        self.assertEqual(result.json["data"]["id"], "1")

    def testPatchRelationship(self):
        result = self.app.get("/articles/1")
        self.assertEqual(result.status, "200 OK")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        rel = result.json["data"]["relationships"]["author"]["links"]["self"]

        request = {
            u"data": {u"type": u"person", u"id": u"2"}
        }

        JsonAPIValidator.validate_jsonapi(request)

        result = self.app.patch_json(rel, params=request)
        self.assertIn(
            result.status,
            ["200 OK", "202 Accepted", "204 No Content"]
        )

        if result.status == "204 No Content":
            self.assertIs(len(result.body), 0)
        elif result.status == "200 OK":
            self.assertIsNotNone(result.json)
            JsonAPIValidator.validate_jsonapi(result.json)

    def testFetchingDataCollection(self):
        result = self.app.get("/articles")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertEqual(result.status, "200 OK")
        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIs(len(result.json["data"]), 2)
        for entry in result.json["data"]:
            self.assertEqual(entry["type"], "article")
            self.assertIsInstance(entry["id"], unicode)
            self.assertIn(entry["attributes"]["title"], ARTICLE_TITLES)

        Article.delete().where(True).execute()
        result = self.app.get("/articles")
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertEqual(result.status, "200 OK")
        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIs(len(result.json["data"]), 0)

    def testFetchingNullRelationship(self):
        result = self.app.get("/articles/1")
        rel = result.json["data"]["relationships"]["cover"]["links"]["related"]

        result = self.app.get(rel)
        JsonAPIValidator.validate_content_type(result.content_type)

        self.assertEqual(result.status, "200 OK")
        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

        self.assertIsNone(result.json["data"])

    def testFetchingMissingSingleResource(self):
        result = self.app.get("/article/1337", status=404)
        self.assertIsNotNone(result.json)
        JsonAPIValidator.validate_jsonapi(result.json)

    def testCreatingResourceWithReferences(self):
        request = {
            u"data": {
                u"type": u"photo",
                u"attributes": {
                    u"title": u"Ember Hamster",
                    u"src": u"http://example.com/images/productivity.png"
                },
                u"relationships": {
                    u"photographer": {
                        u"data": {u"type": u"people", u"id": u"2"}
                    }
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request, True)

        result = self.app.post_json("/photos", params=request)
        JsonAPIValidator.validate_content_type(result.content_type)
        JsonAPIValidator.validate_jsonapi(result.json)

        if not result.location:
            warnings.warn(
                "The response SHOULD include a Location header identifying the"
                "location of the newly created resource."
            )

        else:
            res = self.app.get(result.location)
            self.assertIsNotNone(res.json)
            JsonAPIValidator.validate_jsonapi(res.json)

    def testCreatingResourceWithMissingRequiredAttributeShouldFail(self):
        request = {
            u"data": {
                u"type": u"person",
                u"attributes": {
                    u"name": u"Eve Bobbington"
                    # attribute 'age' is missing
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request, True)

        result = self.app.post_json("/people", params=request, status=400)
        JsonAPIValidator.validate_content_type(result.content_type)
        JsonAPIValidator.validate_jsonapi(result.json)

    def testCreateResourceWithAlreadyExistingId(self):
        request = {
            u"data": {
                u"type": u"person",
                u"id": u"1",
                u"attributes": {
                    u"name": "Jimmy Cricket",
                    u"age": 12
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request)

        # expect this to fail
        result = self.app.post_json("/people", params=request, status=409)
        JsonAPIValidator.validate_jsonapi(result.json)

    def testUpdatingResourceViaSelfLink(self):
        UPDATE_TITLE = u"Five Ways You Have Never Tried To Access Your Data"

        result = self.app.get("/articles/1")
        update_uri = result.json["data"]["links"]["self"]

        request = {
            u"data": {
                u"type": u"article",
                u"id": u"1",
                u"attributes": {
                    u"title": UPDATE_TITLE
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request)
        res = self.app.patch_json(update_uri, params=request)

        if "204" not in res.status:
            JsonAPIValidator.validate_content_type(res.content_type)

        res = self.app.get("/articles/1")
        self.assertEqual(res.json["data"]["attributes"]["title"], UPDATE_TITLE)

    def testUpdatingResourceRelationships(self):
        result = self.app.get("/articles/1")
        request = result.json

        # Person(1) is the current author
        self.assertEqual(
            result.json["data"]["relationships"]["author"]["data"]["id"],
            "1"
        )

        # don't update attributes, server must ignore missing attributes
        del request["data"]["attributes"]

        # do not update the 'comments' and 'cover' relationships
        del request["data"]["relationships"]["comments"]
        del request["data"]["relationships"]["cover"]
        del request["data"]["relationships"]["revisions"]

        ptype = request["data"]["relationships"]["author"]["data"]["type"]

        # change author to Person(2)
        request["data"]["relationships"]["author"] = {
            u"data": {
                u"id": u"2",
                u"type": ptype
            }
        }

        JsonAPIValidator.validate_jsonapi(request)

        result = self.app.patch_json("/articles/1", params=request)
        result = self.app.get("/articles/1")

        self.assertNotEqual(
            result.json["data"]["relationships"]["author"]["data"]["id"],
            "1"
        )

        self.assertEqual(
            result.json["data"]["relationships"]["author"]["data"]["id"],
            "2"
        )

        self.assertEqual(
            result.json["data"]["attributes"]["title"],
            ARTICLE_TITLES[0]
        )

    def testDeletingIndividualResource(self):
        result = self.app.get("/photos/1")
        JsonAPIValidator.validate_jsonapi(result.json)

        result = self.app.delete("/photos/1")

        if result.status_int not in [202, 204, 200]:
            warnings.warn("Delete: A server MAY respond with other HTTP status"
                          "codes. This code is unknown to the specification.")

        if result.status_int == 200:
            JsonAPIValidator.validate_jsonapi(result.json)

        # the resource should be gone now
        self.app.get("/photos/1", status=404)

    def testFetchingRelatedOneToNResource(self):
        result = self.app.get("/articles/1/comments")
        JsonAPIValidator.validate_jsonapi(result.json)

        for entry in result.json["data"]:
            self.assertIn(entry["attributes"]["body"], COMMENT_BODIES)
            self.assertEqual(
                entry["relationships"]["author"]["data"]["id"],
                "2"
            )
            self.assertEqual(
                entry["relationships"]["article"]["data"]["id"],
                "1"
            )

    def testListingRelatedOneToNResource(self):
        result = self.app.get("/articles/1/relationships/comments")
        JsonAPIValidator.validate_jsonapi(result.json)

        for entry in result.json["data"]:
            JsonAPIValidator.validate_resource_identifier(entry)

    def testPatchingRelatedOneToNResourceShouldFail(self):
        result = self.app.get("/people/1/articles")

        self.assertIs(len(result.json["data"]), 2)
        for entry in result.json["data"]:
            self.assertIn(entry["attributes"]["title"], ARTICLE_TITLES)

        # it is not allowed to orphan an article
        request = {
            u"data": {
                u"id": u"1",
                u"type": u"person",
                u"relationships": {
                    u"articles": {
                        u"data": []
                    }
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request)

        result = self.app.patch_json("/people/1", params=request, status=400)
        JsonAPIValidator.validate_jsonapi(result.json)
        self.assertIn("orphan", result.json["errors"][0]["title"])

    def testPatchingRelatedOneToNResourceShouldSucceed(self):
        result = self.app.get("/articles/2/cover")
        self.assertIsInstance(result.json["data"], dict)

        request = {
            u"data": {
                u"id": u"2",
                u"type": u"article",
                u"relationships": {
                    u"cover": {
                        u"data": None
                    }
                }
            }
        }

        JsonAPIValidator.validate_jsonapi(request)

        result = self.app.patch_json("/articles/2", params=request)

        result = self.app.get("/articles/2/cover")
        self.assertIsNone(result.json["data"])

    def testPatchingRelatedOneToMResource(self):
        result = self.app.get("/articles/1/relationships/comments")
        self.assertIsInstance(result.json["data"], list)

        del result.json["data"][0]

        request = {
            u"data": result.json["data"]
        }

        JsonAPIValidator.validate(request)
        result = self.app.patch_json(
            "/articles/1/relationships/comments",
            params=request
        )

    def testPatchingRelatedNToMResource(self):
        result = self.app.get("/photos/1/relationships/tags")
        self.assertIsInstance(result.json["data"], list)
        self.assertIs(len(result.json["data"]), 2)

        request = {
            u"data": result.json["data"]
        }

        # remove one tag
        request["data"].pop()

        self.app.patch_json("/photos/1/relationships/tags", params=request)

        result = self.app.get("/photos/1/relationships/tags")
        self.assertIsInstance(result.json["data"], list)
        self.assertIs(len(result.json["data"]), 1)

    def testGetNToMRelationship(self):
        result = self.app.get("/photos/1/tags")
        JsonAPIValidator.validate(result.json)

        self.assertIs(len(result.json["data"]), 2)

        for tag in result.json["data"]:
            self.assertIn(tag["attributes"]["name"], TAG_NAMES)

    def testValidateForwardRelationship(self):
        result = self.app.get("/photos")
        # Photo has a forward relationship to Person (photographer)
        for entry in result.json["data"]:
            self.assertIn("relationships", entry)
            for name, relationship in entry["relationships"].iteritems():
                self.assertIn(name, ["photographer", "tags"])

            relationship = entry["relationships"]["photographer"]
            data = relationship["data"]

            # retrieve the /relationships link
            subresult = self.app.get(relationship["links"]["self"])
            self.assertEqual(subresult.json["data"], data)

            # retrieve the related object
            subresult = self.app.get(relationship["links"]["related"])

            # type and id must match
            self.assertEqual(subresult.json["data"]["id"], data["id"])
            self.assertEqual(subresult.json["data"]["type"], data["type"])

            # retrieve the related self link and test if it's the same object
            subsubresult = self.app.get(
                subresult.json["data"]["links"]["self"]
            )
            self.assertEqual(subresult.json["data"], subsubresult.json["data"])

    def testValidateReverseRelationships(self):
        result = self.app.get("/photos")
        # Photo has a reverse relationship to Tag (tags, via PhotoTag)
        for entry in result.json["data"]:
            self.assertIn("relationships", entry)
            for name, relationship in entry["relationships"].iteritems():
                self.assertIn(name, ["photographer", "tags"])

            relationship = entry["relationships"]["tags"]
            data = relationship["data"]

            # retrieve the /relationships link
            subresult = self.app.get(relationship["links"]["self"])
            self.assertEqual(subresult.json["data"], data)

            # retrieve the related objects
            subresult = self.app.get(relationship["links"]["related"])

            # type and id must match
            for subentry in subresult.json["data"]:
                self.assertIn(
                    {"id": subentry["id"], "type": subentry["type"]},
                    data
                )

                if "links" in subentry and "self" in subentry["links"]:
                    subsubresult = self.app.get(subentry["links"]["self"])
                    self.assertEqual(subentry, subsubresult.json["data"])

    def testIncludeParameterForwardRelationship(self):
        result = self.app.get("/articles/2?include=cover")
        JsonAPIValidator.validate(result.json)
        self.assertIn("included", result.json)

        # the server must not return any other fields than requested
        self.assertIs(len(result.json["included"]), 1)

        ref = result.json["data"]["relationships"]["cover"]["data"]
        inc = result.json["included"][0]

        self.assertEqual(inc["type"], ref["type"])
        self.assertEqual(inc["id"], ref["id"])

        # the self link must be valid and refer to the same object
        subresult = self.app.get(inc["links"]["self"])
        self.assertEqual(subresult.json["data"], inc)

    def testIncludeParameterReverseRelationship(self):
        result = self.app.get("/articles/1?include=comments")
        JsonAPIValidator.validate(result.json)
        self.assertIn("included", result.json)

        # the server must not return any other fields than requested
        self.assertIs(len(result.json["included"]), len(COMMENT_BODIES))

        refs = result.json["data"]["relationships"]["comments"]["data"]

        for inc in result.json["included"]:
            self.assertIn({"id": inc["id"], "type": inc["type"]}, refs)

            # the self link must be valid and refer to the same object
            subresult = self.app.get(inc["links"]["self"])
            self.assertEqual(subresult.json["data"], inc)
            self.assertEqual(
                subresult.json["links"]["self"],
                inc["links"]["self"]
            )

    def testIncludeParameterWithInvalidFields(self):
        self.app.get("/articles/1?include=invalid-field", status=400)
        self.app.get("/articles/1?include=author,invalid-field", status=400)

    def testIncludeParameterWithCircularRelationships(self):
        self.app.get("/articles/1?include=comments.articles", status=400)
        self.app.get(
            "/articles/1?include=comments.articles.comments",
            status=400
        )

    def testSparseFieldsets(self):
        result = self.app.get("/people/1?fields[person]=age")
        JsonAPIValidator.validate(result.json)

        self.assertNotIn("name", result.json["data"]["attributes"])
        self.assertIn("age", result.json["data"]["attributes"])

    def testSparseFieldsetsWithIncludedObjects(self):
        result = self.app.get("/articles/1?include=comments&fields[comment]=")
        JsonAPIValidator.validate(result.json)

        for inc in result.json["included"]:
            self.assertNotIn("attributes", inc)

        result = self.app.get(
            "/comments/1?include=article.author&fields[person]=age"
        )
        JsonAPIValidator.validate(result.json)

        is_included = False
        for inc in result.json["included"]:
            if inc["type"] == "person":
                is_included = True
                self.assertIn("age", inc["attributes"])
                self.assertNotIn("name", inc["attributes"])

        self.assertIsNotNone(is_included)

    def testLinkWithSpecifiedField(self):
        result = self.app.get("/articles/1/relationships/revisions")
        JsonAPIValidator.validate(result.json)

    def testOPTIONSRequest(self):
        # all of these should return all 200 OK code
        self.app.options("/articles")
        self.app.options("/articles/1")
        self.app.options("/articles/1/cover")
        self.app.options("/articles/1/relationships/cover")
        self.app.options("/photos/2/tags")
        self.app.options("/articles/1?include=author")

    def testCORSHeaders(self):
        # simulate a browser
        result = self.app.options("/articles", headers={
            "Origin": "http://example.org",
            "Access-Control-Request-Method": "GET",
            "Access-Control-Request-Headers": "X-Requested-With"
        })

        self.assertIn("Access-Control-Allow-Origin", result.headers)
        self.assertIn("Access-Control-Allow-Methods", result.headers)
        self.assertEqual(result.headers["Access-Control-Allow-Origin"], "*")
        methods = map(
            lambda x: x.strip(),
            result.headers["Access-Control-Allow-Methods"].split(",")
        )

        self.assertIn("GET", methods)
        self.assertIn(
            "X-Requested-With".lower(),
            result.headers["Access-Control-Allow-Headers"].lower()
        )
Example #16
0
class AppUser:
    """:class:`webtest.TestApp` wrapper for backend functional testing."""

    def __init__(self, app,
                 rest_url: str='http://localhost',
                 base_path: str='/',
                 header: dict=None):
        """Initialize self."""
        self.app = TestApp(app)
        """:class:`webtest.TestApp`to send requests to the backend server."""
        self.rest_url = rest_url
        """backend server url to generate request urls."""
        self.base_path = base_path
        """path prefix to generate request urls."""
        self.header = header or {}
        """default header for requests, mostly for authentication."""
        self._resolver = DottedNameResolver()

    def post_resource(self, path: str,
                      iresource: IInterface,
                      cstruct: dict) -> TestResponse:
        """Build and post request to create a new resource."""
        url = self._build_url(path)
        props = self._build_post_body(iresource, cstruct)
        resp = self.app.post_json(url, props, headers=self.header,
                                  expect_errors=True)
        return resp

    def put(self, path: str, cstruct: dict={}) -> TestResponse:
        """Put request to modify a resource."""
        url = self._build_url(path)
        resp = self.app.put_json(url, cstruct, headers=self.header,
                                 expect_errors=True)
        return resp

    def post(self, path: str, cstruct: dict={}) -> TestResponse:
        """Post request to create a new resource."""
        url = self._build_url(path)
        resp = self.app.post_json(url, cstruct, headers=self.header,
                                  expect_errors=True)
        return resp

    def _build_post_body(self, iresource: IInterface, cstruct: dict) -> dict:
        return {'content_type': iresource.__identifier__,
                'data': cstruct}

    def _build_url(self, path: str) -> str:
        if path.startswith('http'):
            return path
        return self.rest_url + self.base_path + path

    def batch(self, subrequests: list):
        """Build and post batch request to the backend rest server."""
        resp = self.app.post_json(batch_url, subrequests, headers=self.header,
                                  expect_errors=True)
        return resp

    def get(self, path: str, params={}) -> TestResponse:
        """Send get request to the backend rest server."""
        url = self._build_url(path)
        resp = self.app.get(url,
                            headers=self.header,
                            params=params,
                            expect_errors=True)
        return resp

    def options(self, path: str) -> TestResponse:
        """Send options request to the backend rest server."""
        url = self._build_url(path)
        resp = self.app.options(url, headers=self.header, expect_errors=True)
        return resp

    def get_postable_types(self, path: str) -> []:
        """Send options request and return the postable content types."""
        resp = self.options(path)
        if 'POST' not in resp.json:
            return []
        post_request_body = resp.json['POST']['request_body']
        type_names = sorted([r['content_type'] for r in post_request_body])
        iresources = [self._resolver.resolve(t) for t in type_names]
        return iresources
Example #17
0
class TestHttplib(unittest.TestCase):

    client = "httplib"
    client_options = {}

    def setUp(self):
        self.server = StopableWSGIServer.create(debug_app)
        self.application_url = self.server.application_url.rstrip("/")
        self.proxy = proxies.HostProxy(self.application_url, client=self.client, **self.client_options)
        self.app = TestApp(self.proxy)

    def test_form(self):
        resp = self.app.get("/form.html")
        resp.mustcontain("</form>")
        form = resp.form
        form["name"] = "gawel"
        resp = form.submit()
        resp.mustcontain("name=gawel")

    def test_head(self):
        resp = self.app.head("/form.html")
        self.assertEqual(resp.status_int, 200)
        self.assertEqual(len(resp.body), 0)

    def test_webob_error(self):
        req = Request.blank("/")
        req.content_length = "-1"
        resp = req.get_response(self.proxy)
        self.assertEqual(resp.status_int, 500, resp)

    def test_not_allowed_method(self):
        resp = self.app.options("/", status="*")
        self.assertEqual(resp.status_int, 405)

    def test_status(self):
        resp = self.app.get("/?status=404", status="*")
        self.assertEqual(resp.status_int, 404)

    def test_redirect(self):
        location = self.application_url + "/form.html"
        resp = self.app.get("/?status=301%20Redirect&header-location=" + location, status="*")
        self.assertEqual(resp.status_int, 301, resp)
        self.assertEqual(resp.location, location)

        location = "http://foo.com"
        resp = self.app.get("/?status=301%20Redirect&header-location=" + location, status="*")
        self.assertEqual(resp.status_int, 301, resp)
        self.assertEqual(resp.location, location)

        location = "/foo"
        resp = self.app.get("/?status=301%20Redirect&header-location=" + location, status="*")
        self.assertEqual(resp.status_int, 301, resp)
        self.assertEqual(resp.location, self.application_url + location)

        location = self.application_url + "/script_name/form.html"
        self.proxy.strip_script_name = False
        resp = self.app.get(
            "/?status=301%20Redirect&header-Location=" + location,
            status="*",
            extra_environ={"SCRIPT_NAME": "/script_name"},
        )
        self.assertEqual(resp.status_int, 301, resp)
        self.assertEqual(resp.location, location)

    def test_chunked(self):
        resp = self.app.get("/", headers=[("Transfer-Encoding", "chunked")])
        resp.mustcontain(no="chunked")

    def test_quoted_utf8_url(self):
        path = "/targets/NR2F1%C3%82-human/"
        resp = self.app.get(path)
        resp.mustcontain(b"PATH_INFO: /targets/NR2F1\xc3\x82-human/")

    def tearDown(self):
        self.server.shutdown()
Example #18
0
class AppUser:
    """:class:`webtest.TestApp` wrapper for backend functional testing."""
    def __init__(
        self,
        app_router: Router,
        base_path: str = '/',
        header: dict = None,
        user_path: str = '',
        user_login: str = '',
        user_password: str = '',
    ):
        """Initialize self."""
        self.app_router = app_router
        """The adhocracy wsgi application"""
        self.app = TestApp(app_router)
        """:class:`webtest.TestApp`to send requests to the backend server."""
        self.rest_url = rest_url()
        """backend server url to generate request urls."""
        self.base_path = base_path
        """path prefix to generate request urls."""
        self.header = header or {}
        """default header for requests, mostly for authentication.
           If not set, `user_login` and `user_password` is used to login,
           the new authentication header is stored in `header`.
        """
        if user_password and user_login and not header:
            token, user_path = self._get_token_and_user_path(
                user_login, user_password)
            self.header = {UserTokenHeader: token}
        self.user_password = user_password
        """password for authenticated user."""
        self.user_login = user_login
        """login name for authenticated user."""
        self.user_path = user_path
        """path for authenticated user."""
        self._resolver = DottedNameResolver()

    def _get_token_and_user_path(self, login: str, password: str) -> tuple:
        login_page = self.rest_url + '/login_username'
        data = {'name': login, 'password': password}
        resp = self.app.post_json(login_page, data).json
        return resp['user_token'], resp['user_path']

    def post_resource(self, path: str, iresource: IInterface,
                      cstruct: dict) -> TestResponse:
        """Build and post request to create a new resource."""
        url = self._build_url(path)
        props = self._build_post_body(iresource, cstruct)
        resp = self.app.post_json(url,
                                  props,
                                  headers=self.header,
                                  expect_errors=True)
        return resp

    def put(
        self,
        path: str,
        cstruct: dict = {},
        upload_files: [(str, str, bytes)] = None,
        extra_headers: dict = {},
    ) -> TestResponse:
        """Put request to modify a resource."""
        url = self._build_url(path)
        headers = copy(self.header)
        headers.update(extra_headers)
        kwargs = {
            'headers': headers,
            'expect_errors': True,
        }
        if upload_files:
            kwargs['upload_files'] = upload_files
            resp = self.app.put(url, cstruct, **kwargs)
        else:
            resp = self.app.put_json(url, cstruct, **kwargs)
        return resp

    def post(
        self,
        path: str,
        cstruct: dict = {},
        upload_files: [(str, str, bytes)] = None,
        extra_headers: dict = {},
    ) -> TestResponse:
        """Post request to create a new resource."""
        url = self._build_url(path)
        headers = copy(self.header)
        headers.update(extra_headers)
        kwargs = {
            'headers': headers,
            'expect_errors': True,
        }
        if upload_files:
            kwargs['upload_files'] = upload_files
            resp = self.app.post(url, cstruct, **kwargs)
        else:
            resp = self.app.post_json(url, cstruct, **kwargs)
        return resp

    def _build_post_body(self, iresource: IInterface, cstruct: dict) -> dict:
        return {'content_type': iresource.__identifier__, 'data': cstruct}

    def _build_url(self, path: str) -> str:
        if path.startswith('http'):
            return path
        elif path.startswith('/') and (self.base_path == '\\'):
            return self.rest_url + path
        else:
            return self.rest_url + self.base_path + path

    def batch(self, subrequests: list):
        """Build and post batch request to the backend rest server."""
        resp = self.app.post_json(batch_url,
                                  subrequests,
                                  headers=self.header,
                                  expect_errors=True)
        return resp

    def head(self, path: str, extra_headers={}) -> TestResponse:
        """Send head request to the backend rest server."""
        url = self._build_url(path)
        headers = copy(self.header)
        headers.update(extra_headers)
        resp = self.app.head(url, headers=headers, expect_errors=True)
        return resp

    def get(self, path: str, params={}, extra_headers={}) -> TestResponse:
        """Send get request to the backend rest server."""
        url = self._build_url(path)
        headers = copy(self.header)
        headers.update(extra_headers)
        resp = self.app.get(url,
                            headers=headers,
                            params=params,
                            expect_errors=True)
        return resp

    def delete(self, path: str) -> TestResponse:
        """Send delete request to the backend rest server."""
        url = self._build_url(path)
        resp = self.app.delete(url, headers=self.header, expect_errors=True)
        return resp

    def options(self, path: str) -> TestResponse:
        """Send options request to the backend rest server."""
        url = self._build_url(path)
        resp = self.app.options(url, headers=self.header, expect_errors=True)
        return resp

    def get_postable_types(self, path: str) -> []:
        """Send options request and return the postable content types."""
        resp = self.options(path)
        if 'POST' not in resp.json:
            return []
        post_request_body = resp.json['POST']['request_body']
        type_names = sorted([r['content_type'] for r in post_request_body])
        iresources = [self._resolver.resolve(t) for t in type_names]
        return iresources
Example #19
0
class TestUserAPI(unittest.TestCase):
    """Tests API functions associated with actions a regular user can take.
       Note that all tests are in-process, we don't actually start a HTTP server.
       All administrative requirements will be set up with direct calls
       to eos_db.server, and all user calls will be done via self.app.
    """
    def setUp(self):
        """Launch app using webtest with test settings"""
        self.appconf = get_app(test_ini)
        self.app = TestApp(self.appconf)

        #All auth via BasicAuth - never return the session cookie.
        self.app.cookiejar.set_policy(DefaultCookiePolicy(allowed_domains=[]))

        # This sets global var "engine" - in the case of SQLite this is a fresh RAM
        # DB each time.  If we only did this on class instantiation the database would
        # be dirty and one test could influence another.
        # TODO - add a test that tests this.
        server.choose_engine("SQLite")

        # Punch in new user account with direct server call
        # This will implicitly generate the tables.
        user_id = self.create_user("testuser")
        #Here is what the user should look like when inspected
        self.user_json =  { "name"    : "testuser testuser",
                            "handle"  : "*****@*****.**",
                            "id"      : 1,
                            "credits" : 0,
                            "username": "******"}

        #print("user_id is %s" % str(user_id))
        #print("user_from_db_is %s" % server.get_user_id_from_name("testuser"))

        server.touch_to_add_password(user_id, "asdf")

        # And log in as this user for all tests (via BasicAuth)
        # FIXME - switch to token auth to speed up the tests.
        self.app.authorization = ('Basic', ('testuser', 'asdf'))

    """Unauthenticated API functions.

       Should respond the same regardless of authentication.
    """

    def test_home_view(self):
        """ Home view should respond with 200 OK. """
        response = self.app.get('/', status=200)

    # Not sure why Ben implemented options, but it should still work.
    def test_options(self):
        """ Options should respond with 200 OK. """
        response = self.app.options('/', status=200)

    """User API functions.

    The user functions in the API are primarily used by system utilities.
    Creating a user and password, and validating against the database in
    order to receive an access token, are prerequisites for using functions
    in later sections of the API. These can only be called by an
    administrator."""

    def test_whoami(self):
        """ How do I find out who I am? """
        response = self.app.get('/user')

        #We expect to be user 1, as the database is fresh.
        #All other items should be as per create_user("testuser")
        self.assertEqual( response.json, self.user_json )

    def test_retrieve_my_info(self):
        """ Retrieving my own user info by name should give the same result
            as above."""

        response = self.app.get('/users/testuser', status=200)

        #We expect to be user 1, as the database is fresh.
        #All other items should be as per create_user("testuser")
        self.assertEqual( response.json, self.user_json )

    def test_retrieve_other_user_info(self):
        """ Retrieving info for another user should respond 200 OK. """

        self.create_user("anotheruser")

        self.assertEqual(self.app.get('/users/anotheruser').json['name'], "anotheruser anotheruser")

    def test_retrieve_users(self):
        """ Add another couple of users. Three records should be returned, as
            there is already a testuser. """

        self.create_user("foo")
        self.create_user("bar")

        response = self.app.get('/users')

        self.assertEqual(len(response.json), 3)

    #Unimplemented just now.
    @unittest.expectedFailure
    def test_delete_user(self):
        """ Delete a user. Should fail because the account does not have permission,
            but it actually fails because deletion is unimplemented.
        """
        self.create_user("anotheruser")
        response = self.app.delete('/users/anotheruser', status=404)


    def test_change_my_password(self):
        """ Apply a password to our user. Check that we receive a 200 OK.
            Check we can log in with the new password but not the old.
        """
        response = self.app.put('/user/password',
                                {'password': '******'})

        #This should work
        self.app.authorization = ('Basic', ('testuser', 'newpass'))
        self.app.get('/users/testuser')


        #This should fail as the password is now wrong.
        self.app.authorization = ('Basic', ('testuser', 'asdf'))
        self.app.get('/users/testuser', status=401)

    def test_change_other_password(self):
        """ Try to change password for another user, which should fail.
        """
        self.create_user("anotheruser")

        response = self.app.put('/users/anotheruser/password',
                                {'password': '******'},
                                status=401)

    def test_retrieve_user_credit(self):
        """ If administrator adds credit, I should be able to see it.
            See full credit tests in test_credit.py
        """
        self.add_credit(123, 'testuser')

        #And retrieve it back
        response = self.app.get('/user')

        user_json = self.user_json.copy()
        user_json['credits'] = 123

        self.assertEqual( response.json, user_json )


    def test_retrieve_servers(self):
        """ A user can request a list of servers that they own.
        """
        server_id = self.create_server('fooserver', 'testuser')

        my_servers = self.app.get('/servers').json

        self.assertTrue(server_id)

        self.assertEqual(len(my_servers), 1)
        self.assertEqual(my_servers[0]['artifact_name'], 'fooserver')

    def test_retrieve_user_touches(self):
        """ Retrieve a list of touches that the user has made to the database.
        This can only be requested by the user themselves, an agent or an
        administrator. """

    def test_create_server(self):
        """ A regular user cannot create a server or give themselves ownership
            of a server, so this should produce an appropriate error.
        """

    def test_create_server_owner(self):
        """ Add an owner to a server. Ensure that a 200 OK response results.
        """
        #FIXME - move this to administrator tests.

    """ Server State-Change Functions. """

    def test_start_server(self):
        """ Check that a server appears in state 'Starting' after using the
        relevant API call. This also tests the function 'retrieve_servers_in_state'.
        """
        server_id = self.create_server('fooserver', 'testuser')
        self.app.post('/servers/fooserver/Starting')

        #1 - server should appear to be Starting in list of my servers.
        my_servers = self.app.get('/servers').json
        self.assertEqual(len(my_servers), 1)
        self.assertEqual(my_servers[0]['state'], 'Starting')

        #2 - server should appear to be Starting if I look at it directly
        my_server = self.app.get('/servers/fooserver').json
        self.assertEqual(my_server['state'], 'Starting')

        #3 - server should appear as the only server in state Starting
        servers_in_state = self.app.get('/states/Starting').json
        self.assertEqual(len(servers_in_state), 1)
        self.assertEqual(servers_in_state[0]['artifact_name'], 'fooserver')

    def test_error_by_id_server(self):
        """ Check that i can put a server into Error state, refereced by ID
        """
        server_id = self.create_server('fooserver', 'testuser')
        self.app.post('/servers/by_id/%i/Error' % server_id)

        server_info = self.app.get('/servers/fooserver').json
        self.assertEqual(server_info['state'], 'Error')

    def test_boost_deboost_server(self):
        """ Test the boost call, which is done by putting the server into /Preparing.
            After this the server should be in the Preparing state and the user should
            have fewer credits.
            Also test the deboost.
        """
        server_id = self.create_server('boostme', 'testuser')
        self.add_credit(123, 'testuser')

        self.app.post('/servers/boostme/Preparing', params=dict(hours=20, cores=2, ram=40))

        #Check the user
        user_info = self.app.get('/user').json
        self.assertEqual(user_info['credits'], 103)

        #Check the server
        info_expected = dict(boosted="Boosted", boostremaining="19 hrs, 59 min", ram="40 GB", cores="2")
        server_info = self.app.get('/servers/boostme').json
        #Remove items I don't want to compare from server_info
        info_got = {k:str(server_info[k]) for k in server_info if k in info_expected}

        self.assertEqual(info_got, info_expected)

        #Deboost and check again
        self.app.post('/servers/boostme/Pre_Deboosting')

        #Check the user - we should be down 1 credit.
        user_info = self.app.get('/user').json
        self.assertEqual(user_info['credits'], 122)

        #Check the server once more.
        info_expected = dict(boosted="Unboosted", boostremaining="N/A", ram="16 GB", cores="1")
        server_info = self.app.get('/servers/boostme').json
        #Remove items I don't want to compare from server_info
        info_got = {k:str(server_info[k]) for k in server_info if k in info_expected}

        self.assertEqual(info_got, info_expected)

    def test_restart_server(self):
        """ Check that a server appears in state 'Restarted' after using the
        relevant API call. This also tests the function 'retrieve_servers_in_state'.
        """

    def test_stop_server(self):
        """ Check that a server appears in state 'Stopped' after using the
        relevant API call. This also tests the function 'retrieve_servers_in_state'.
        """

    def test_prepare_server(self):
        """ Check that a server appears in state 'Prepared' after using the
        relevant API call. This also tests the function 'retrieve_servers_in_state'.
        """

    def test_pre_deboost_server(self):
        """ Check that a server appears in relevant state after using the
        relevant API call. This also tests the function 'retrieve_servers_in_state'.
        """

    def test_stopped_server(self):
        """ Check that a server appears in relevant state after using the
        relevant API call. This also tests the function 'retrieve_servers_in_state'.
        """

    def test_started_server(self):
        """ Check that a server appears in relevant state after using the
        relevant API call. This also tests the function 'retrieve_servers_in_state'.
        """

    def test_prepared_server(self):
        """ Check that a server appears in relevant state after using the
        relevant API call. This also tests the function 'retrieve_servers_in_state'.
        """

    def test_predeboosted_server(self):
        """ Check that a server appears in relevant state after using the
        relevant API call. This also tests the function 'retrieve_servers_in_state'.
        """

    def test_retrieve_server(self):
        """ Pull back details of our server by name. """

    def test_retrieve_server_by_id(self):
        """ Our server will have ID 1. Check that we can retrieve details of
        it."""

    def test_update_server(self):
        """ Not currently implemented. """

    def test_delete_server(self):
        """ Not currently implemented. """

    def test_set_server_specification(self):
        """ Follows hard-coded rules for machine behaviour.
        Set machine CPUs to 2. Check, should pass.
        Set machine CPUs to 65000. Check, should fail.
        Set machine RAM to 16. Check, should pass.
        Set machine RAM to 65000. Check, should fail."""

    def test_get_server_specification(self):
        """ Check that machine RAM and Cores are 2 and 16 as above. """

    def test_retrieve_job_progress(self):
        """ Not currently implemented. """

    def test_retrieve_server_touches(self):
        """ Not currently implemented. """

###############################################################################
# Support Functions, calling the server code directly                         #
###############################################################################

    def create_user(self, name):
        #Since we are not logged in as the administrator, do this directly
        return server.create_user("users", name + "@example.com", name + " " + name, name)

    # Servers should not normally have uuid set to name, but maybe for testing it doesn't
    # matter?
    def create_server(self, name, owner):
        owner_id = server.get_user_id_from_name(owner)
        server_id = server.create_appliance(name, name)

        server.touch_to_add_ownership(server_id, owner_id)

        return server_id

    def add_credit(self, amount, owner):
        owner_id = server.get_user_id_from_name(owner)

        server.touch_to_add_credit(owner_id, int(amount))
Example #20
0
class TestCORS(TestCase):
    def setUp(self):
        self.config = testing.setUp()
        self.config.include("cornice")
        self.config.scan("cornice.tests.test_cors")
        self.app = TestApp(CatchErrors(self.config.make_wsgi_app()))

        def tearDown(self):
            testing.tearDown()

    def test_preflight_missing_headers(self):
        # we should have an OPTION method defined.
        # If we just try to reach it, without using correct headers:
        # "Access-Control-Request-Method"or without the "Origin" header,
        # we should get a 400.
        resp = self.app.options("/squirel", status=400)
        self.assertEquals(len(resp.json["errors"]), 2)

    def test_preflight_missing_origin(self):

        resp = self.app.options("/squirel", headers={"Access-Control-Request-Method": "GET"}, status=400)
        self.assertEquals(len(resp.json["errors"]), 1)

    def test_preflight_missing_request_method(self):

        resp = self.app.options("/squirel", headers={"Origin": "foobar.org"}, status=400)

        self.assertEquals(len(resp.json["errors"]), 1)

    def test_preflight_incorrect_origin(self):
        # we put "lolnet.org" where only "notmyidea.org" is authorized
        resp = self.app.options(
            "/squirel", headers={"Origin": "lolnet.org", "Access-Control-Request-Method": "GET"}, status=400
        )
        self.assertEquals(len(resp.json["errors"]), 1)

    def test_preflight_correct_origin(self):
        resp = self.app.options("/squirel", headers={"Origin": "notmyidea.org", "Access-Control-Request-Method": "GET"})
        self.assertEquals(resp.headers["Access-Control-Allow-Origin"], "notmyidea.org")

        allowed_methods = resp.headers["Access-Control-Allow-Methods"].split(",")

        self.assertNotIn("POST", allowed_methods)
        self.assertIn("GET", allowed_methods)
        self.assertIn("PUT", allowed_methods)
        self.assertIn("HEAD", allowed_methods)

        allowed_headers = resp.headers["Access-Control-Allow-Headers"].split(",")

        self.assertIn("X-My-Header", allowed_headers)
        self.assertNotIn("X-Another-Header", allowed_headers)

    def test_preflight_deactivated_method(self):
        self.app.options(
            "/squirel", headers={"Origin": "notmyidea.org", "Access-Control-Request-Method": "POST"}, status=400
        )

    def test_preflight_origin_not_allowed_for_method(self):
        self.app.options(
            "/squirel", headers={"Origin": "notmyidea.org", "Access-Control-Request-Method": "PUT"}, status=400
        )

    def test_preflight_credentials_are_supported(self):
        resp = self.app.options("/spam", headers={"Origin": "notmyidea.org", "Access-Control-Request-Method": "GET"})

        self.assertIn("Access-Control-Allow-Credentials", resp.headers)
        self.assertEquals(resp.headers["Access-Control-Allow-Credentials"], "true")

    def test_preflight_credentials_header_not_included_when_not_needed(self):
        resp = self.app.options("/spam", headers={"Origin": "notmyidea.org", "Access-Control-Request-Method": "POST"})

        self.assertNotIn("Access-Control-Allow-Credentials", resp.headers)

    def test_preflight_contains_max_age(self):
        resp = self.app.options("/spam", headers={"Origin": "notmyidea.org", "Access-Control-Request-Method": "GET"})

        self.assertIn("Access-Control-Max-Age", resp.headers)
        self.assertEquals(resp.headers["Access-Control-Max-Age"], "42")

    def test_resp_dont_include_allow_origin(self):
        resp = self.app.get("/squirel")  # omit the Origin header
        self.assertNotIn("Access-Control-Allow-Origin", resp.headers)
        self.assertEquals(resp.json, "squirels")

    def test_responses_include_an_allow_origin_header(self):
        resp = self.app.get("/squirel", headers={"Origin": "notmyidea.org"})
        self.assertIn("Access-Control-Allow-Origin", resp.headers)
        self.assertEquals(resp.headers["Access-Control-Allow-Origin"], "notmyidea.org")

    def test_credentials_are_included(self):
        resp = self.app.get("/spam", headers={"Origin": "notmyidea.org"})
        self.assertIn("Access-Control-Allow-Credentials", resp.headers)
        self.assertEquals(resp.headers["Access-Control-Allow-Credentials"], "true")

    def test_headers_are_exposed(self):
        resp = self.app.get("/squirel", headers={"Origin": "notmyidea.org"})
        self.assertIn("Access-Control-Expose-Headers", resp.headers)

        headers = resp.headers["Access-Control-Expose-Headers"].split(",")
        self.assertIn("X-My-Header", headers)

    def test_preflight_request_headers_are_included(self):
        resp = self.app.options(
            "/squirel",
            headers={
                "Origin": "notmyidea.org",
                "Access-Control-Request-Method": "GET",
                "Access-Control-Request-Headers": "foo,bar,baz",
            },
        )

        # per default, they should be authorized, and returned in the list of
        # authorized headers
        headers = resp.headers["Access-Control-Allow-Headers"].split(",")
        self.assertIn("foo", headers)
        self.assertIn("bar", headers)
        self.assertIn("baz", headers)

    def test_preflight_request_headers_isnt_too_permissive(self):
        self.app.options(
            "/eggs",
            headers={
                "Origin": "notmyidea.org",
                "Access-Control-Request-Method": "GET",
                "Access-Control-Request-Headers": "foo,bar,baz",
            },
            status=400,
        )

    def test_preflight_headers_arent_case_sensitive(self):
        self.app.options(
            "/spam",
            headers={
                "Origin": "notmyidea.org",
                "Access-Control-Request-Method": "GET",
                "Access-Control-Request-Headers": "x-my-header",
            },
        )
Example #21
0
class AppUser:
    """:class:`webtest.TestApp` wrapper for backend functional testing."""

    def __init__(self,
                 app_router: Router,
                 rest_url: str='http://localhost',
                 base_path: str='/',
                 header: dict=None,
                 user_path: str='',
                 user_login: str='',
                 user_password: str='',
                 ):
        """Initialize self."""
        self.app_router = app_router
        """The adhocracy wsgi application"""
        self.app = TestApp(app_router)
        """:class:`webtest.TestApp`to send requests to the backend server."""
        self.rest_url = rest_url
        """backend server url to generate request urls."""
        self.base_path = base_path
        """path prefix to generate request urls."""
        self.header = header or {}
        """default header for requests, mostly for authentication.
           If not set, `user_login` and `user_password` is used to login,
           the new authentication header is stored in `header`.
        """
        if user_password and user_login and not header:
            token, user_path = self._get_token_and_user_path(user_login,
                                                             user_password)
            self.header = {UserTokenHeader: token}
        self.user_password = user_password
        """password for authenticated user."""
        self.user_login = user_login
        """login name for authenticated user."""
        self.user_path = user_path
        """path for authenticated user."""
        self._resolver = DottedNameResolver()

    def _get_token_and_user_path(self, login: str, password: str) -> tuple:
        login_page = self.rest_url + '/login_username'
        data = {'name': login,
                'password': password}
        resp = self.app.post_json(login_page, data).json
        return resp['user_token'], resp['user_path']

    def post_resource(self, path: str,
                      iresource: IInterface,
                      cstruct: dict) -> TestResponse:
        """Build and post request to create a new resource."""
        url = self._build_url(path)
        props = self._build_post_body(iresource, cstruct)
        resp = self.app.post_json(url, props, headers=self.header,
                                  expect_errors=True)
        return resp

    def put(self,
            path: str,
            cstruct: dict={},
            upload_files: [(str, str, bytes)]=None,
            ) -> TestResponse:
        """Put request to modify a resource."""
        url = self._build_url(path)
        kwargs = {'headers': self.header,
                  'expect_errors': True,
                  }
        if upload_files:
            kwargs['upload_files'] = upload_files
            resp = self.app.put(url, cstruct, **kwargs)
        else:
            resp = self.app.put_json(url, cstruct, **kwargs)
        return resp

    def post(self,
             path: str,
             cstruct: dict={},
             upload_files: [(str, str, bytes)]=None,
             ) -> TestResponse:
        """Post request to create a new resource."""
        url = self._build_url(path)
        kwargs = {'headers': self.header,
                  'expect_errors': True,
                  }
        if upload_files:
            kwargs['upload_files'] = upload_files
            resp = self.app.post(url, cstruct, **kwargs)
        else:
            resp = self.app.post_json(url, cstruct, **kwargs)
        return resp

    def _build_post_body(self, iresource: IInterface, cstruct: dict) -> dict:
        return {'content_type': iresource.__identifier__,
                'data': cstruct}

    def _build_url(self, path: str) -> str:
        if path.startswith('http'):
            return path
        return self.rest_url + self.base_path + path

    def batch(self, subrequests: list):
        """Build and post batch request to the backend rest server."""
        resp = self.app.post_json(batch_url, subrequests, headers=self.header,
                                  expect_errors=True)
        return resp

    def get(self, path: str, params={}) -> TestResponse:
        """Send get request to the backend rest server."""
        url = self._build_url(path)
        resp = self.app.get(url,
                            headers=self.header,
                            params=params,
                            expect_errors=True)
        return resp

    def delete(self, path: str) -> TestResponse:
        """Send delete request to the backend rest server."""
        url = self._build_url(path)
        resp = self.app.delete(url,
                               headers=self.header,
                               expect_errors=True)
        return resp

    def options(self, path: str) -> TestResponse:
        """Send options request to the backend rest server."""
        url = self._build_url(path)
        resp = self.app.options(url, headers=self.header, expect_errors=True)
        return resp

    def get_postable_types(self, path: str) -> []:
        """Send options request and return the postable content types."""
        resp = self.options(path)
        if 'POST' not in resp.json:
            return []
        post_request_body = resp.json['POST']['request_body']
        type_names = sorted([r['content_type'] for r in post_request_body])
        iresources = [self._resolver.resolve(t) for t in type_names]
        return iresources
Example #22
0
class TestCORS(TestCase):
    def setUp(self):
        self.config = testing.setUp()
        self.config.include("cornice")
        self.config.add_route('noservice', '/noservice')

        self.config.scan("cornice.tests.test_cors")
        self.app = TestApp(CatchErrors(self.config.make_wsgi_app()))

        def tearDown(self):
            testing.tearDown()

    def test_preflight_missing_headers(self):
        # we should have an OPTION method defined.
        # If we just try to reach it, without using correct headers:
        # "Access-Control-Request-Method"or without the "Origin" header,
        # we should get a 400.
        resp = self.app.options('/squirel', status=400)
        self.assertEquals(len(resp.json['errors']), 2)

    def test_preflight_missing_origin(self):

        resp = self.app.options(
            '/squirel',
            headers={'Access-Control-Request-Method': 'GET'},
            status=400)
        self.assertEquals(len(resp.json['errors']), 1)

    def test_preflight_missing_request_method(self):

        resp = self.app.options('/squirel',
                                headers={'Origin': 'foobar.org'},
                                status=400)

        self.assertEquals(len(resp.json['errors']), 1)

    def test_preflight_incorrect_origin(self):
        # we put "lolnet.org" where only "notmyidea.org" is authorized
        resp = self.app.options('/squirel',
                                headers={
                                    'Origin': 'lolnet.org',
                                    'Access-Control-Request-Method': 'GET'
                                },
                                status=400)
        self.assertEquals(len(resp.json['errors']), 1)

    def test_preflight_correct_origin(self):
        resp = self.app.options('/squirel',
                                headers={
                                    'Origin': 'notmyidea.org',
                                    'Access-Control-Request-Method': 'GET'
                                })
        self.assertEquals(resp.headers['Access-Control-Allow-Origin'],
                          'notmyidea.org')

        allowed_methods = (
            resp.headers['Access-Control-Allow-Methods'].split(','))

        self.assertNotIn('POST', allowed_methods)
        self.assertIn('GET', allowed_methods)
        self.assertIn('PUT', allowed_methods)
        self.assertIn('HEAD', allowed_methods)

        allowed_headers = (
            resp.headers['Access-Control-Allow-Headers'].split(','))

        self.assertIn('X-My-Header', allowed_headers)
        self.assertNotIn('X-Another-Header', allowed_headers)

    def test_preflight_deactivated_method(self):
        self.app.options('/squirel',
                         headers={
                             'Origin': 'notmyidea.org',
                             'Access-Control-Request-Method': 'POST'
                         },
                         status=400)

    def test_preflight_origin_not_allowed_for_method(self):
        self.app.options('/squirel',
                         headers={
                             'Origin': 'notmyidea.org',
                             'Access-Control-Request-Method': 'PUT'
                         },
                         status=400)

    def test_preflight_credentials_are_supported(self):
        resp = self.app.options('/spam',
                                headers={
                                    'Origin': 'notmyidea.org',
                                    'Access-Control-Request-Method': 'GET'
                                })

        self.assertIn('Access-Control-Allow-Credentials', resp.headers)
        self.assertEquals(resp.headers['Access-Control-Allow-Credentials'],
                          'true')

    def test_preflight_credentials_header_not_included_when_not_needed(self):
        resp = self.app.options('/spam',
                                headers={
                                    'Origin': 'notmyidea.org',
                                    'Access-Control-Request-Method': 'POST'
                                })

        self.assertNotIn('Access-Control-Allow-Credentials', resp.headers)

    def test_preflight_contains_max_age(self):
        resp = self.app.options('/spam',
                                headers={
                                    'Origin': 'notmyidea.org',
                                    'Access-Control-Request-Method': 'GET'
                                })

        self.assertIn('Access-Control-Max-Age', resp.headers)
        self.assertEquals(resp.headers['Access-Control-Max-Age'], '42')

    def test_resp_dont_include_allow_origin(self):
        resp = self.app.get('/squirel')  # omit the Origin header
        self.assertNotIn('Access-Control-Allow-Origin', resp.headers)
        self.assertEquals(resp.json, 'squirels')

    def test_responses_include_an_allow_origin_header(self):
        resp = self.app.get('/squirel', headers={'Origin': 'notmyidea.org'})
        self.assertIn('Access-Control-Allow-Origin', resp.headers)
        self.assertEquals(resp.headers['Access-Control-Allow-Origin'],
                          'notmyidea.org')

    def test_credentials_are_included(self):
        resp = self.app.get('/spam', headers={'Origin': 'notmyidea.org'})
        self.assertIn('Access-Control-Allow-Credentials', resp.headers)
        self.assertEquals(resp.headers['Access-Control-Allow-Credentials'],
                          'true')

    def test_headers_are_exposed(self):
        resp = self.app.get('/squirel', headers={'Origin': 'notmyidea.org'})
        self.assertIn('Access-Control-Expose-Headers', resp.headers)

        headers = resp.headers['Access-Control-Expose-Headers'].split(',')
        self.assertIn('X-My-Header', headers)

    def test_preflight_request_headers_are_included(self):
        resp = self.app.options('/squirel',
                                headers={
                                    'Origin': 'notmyidea.org',
                                    'Access-Control-Request-Method': 'GET',
                                    'Access-Control-Request-Headers':
                                    'foo,bar,baz'
                                })

        # per default, they should be authorized, and returned in the list of
        # authorized headers
        headers = resp.headers['Access-Control-Allow-Headers'].split(',')
        self.assertIn('foo', headers)
        self.assertIn('bar', headers)
        self.assertIn('baz', headers)

    def test_preflight_request_headers_isnt_too_permissive(self):
        self.app.options('/eggs',
                         headers={
                             'Origin': 'notmyidea.org',
                             'Access-Control-Request-Method': 'GET',
                             'Access-Control-Request-Headers': 'foo,bar,baz'
                         },
                         status=400)

    def test_preflight_headers_arent_case_sensitive(self):
        self.app.options('/spam',
                         headers={
                             'Origin': 'notmyidea.org',
                             'Access-Control-Request-Method': 'GET',
                             'Access-Control-Request-Headers': 'x-my-header',
                         })

    def test_400_returns_CORS_headers(self):
        resp = self.app.get('/bacon/not',
                            status=400,
                            headers={'Origin': 'notmyidea.org'})
        self.assertIn('Access-Control-Allow-Origin', resp.headers)

    def test_404_returns_CORS_headers(self):
        resp = self.app.get('/bacon/notgood',
                            status=404,
                            headers={'Origin': 'notmyidea.org'})
        self.assertIn('Access-Control-Allow-Origin', resp.headers)

    def test_existing_non_service_route(self):
        resp = self.app.get('/noservice',
                            status=200,
                            headers={'Origin': 'notmyidea.org'})
        self.assertEquals(resp.body, b'No Service here.')
Example #23
0
class ViewIntegrationTest(unittest.TestCase):
    httpd = None
    executor = None

    @classmethod
    def setUpClass(cls) -> None:
        cls.httpd = HTTPServer(('localhost', 8080), SimpleHTTPRequestHandler)
        cls.executor = ThreadPoolExecutor(max_workers=1)
        cls.executor.submit(cls.httpd.serve_forever)

    @classmethod
    def tearDownClass(cls) -> None:
        cls.httpd.shutdown()
        cls.executor.shutdown()

    def setUp(self):
        settings = {
            'ds.host': 'http://localhost:8080',
            'ds.file.prefix': 'ds-preview-',
            'ds.document.meta.path': '/api/index/search/%s/doc/%s',
            'ds.document.src.path': '/api/%s/documents/src/%s',
            'ds.document.max.size': '50000000',
            'ds.document.max.age': '259200',
            'ds.session.cookie.enabled': 'true',
            'ds.session.cookie.name': '_ds_session_id',
            'ds.session.header.enabled': 'true',
            'ds.session.header.name': 'X-Ds-Session-Id',
        }
        app = main({}, **settings)
        self.app = TestApp(app)

    def test_home_page(self):
        response = self.app.get('/')
        self.assertIn(b'Datashare preview', response.body)

    def test_cors_thumbnail_preflight(self):
        self._assert_cors_headers_ok(
            self.app.options('/api/v1/thumbnail/index/id'))

    def test_cors_info_preflight(self):
        self._assert_cors_headers_ok(
            self.app.options('/api/v1/thumbnail/index/id.json'))

    def test_thumbnail_with_neither_cookie_nor_header(self):
        response = self.app.get('/api/v1/thumbnail/index/id',
                                expect_errors=True)
        self.assertEqual(response.status, '401 Unauthorized')

    def test_thumbnail_with_cookie(self):
        create_file_ondisk_from_resource('dummy.jpg',
                                         '/tmp/ds-preview--index-id')
        response = self.app.get('/api/v1/thumbnail/index/id',
                                headers=auth_headers())
        self.assertEqual(response.status, '200 OK')

    def test_thumbnail_with_header(self):
        create_file_ondisk_from_resource('dummy.jpg',
                                         '/tmp/ds-preview--index-id')
        response = self.app.get('/api/v1/thumbnail/index/id',
                                headers=auth_headers())
        self.assertEqual(response.status, '200 OK')

    def test_info_json(self):
        create_file_ondisk_from_resource('dummy.jpg',
                                         '/tmp/ds-preview--index-id')
        response = self.app.get('/api/v1/thumbnail/index/id.json',
                                headers=auth_headers())
        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.headers['Content-Type'], 'application/json')
        self.assertEqual(
            json.loads(response.body.decode()), {
                "previewable": True,
                "pages": 1,
                "content": None,
                "content_type": "image/jpeg"
            })

    def test_ods_json(self):
        create_file_ondisk_from_resource('dummy.ods',
                                         '/tmp/ds-preview--index-id')

        response = self.app.get(
            '/api/v1/thumbnail/index/id.json?include-content=1',
            headers=auth_headers())

        self.assertEqual(response.status, '200 OK')
        self.assertEqual(response.headers['Content-Type'], 'application/json')
        self.assertEqual(
            json.loads(response.body.decode()), {
                'content': {
                    'meals': [['name'], ['couscous'], ['hummus'], ['paella']],
                    'people': [['firstname', 'lastname'], ['foo', 'bar']]
                },
                'content_type':
                'application/vnd.oasis.opendocument.spreadsheet',
                'pages': 2,
                'previewable': True
            })

    def _assert_cors_headers_ok(self, response):
        self.assertEqual('*',
                         response.headers.get('Access-Control-Allow-Origin'))
        self.assertEqual('GET',
                         response.headers.get('Access-Control-Allow-Methods'))
        self.assertEqual('x-ds-session-id',
                         response.headers.get('Access-Control-Allow-Headers'))
Example #24
0
class TestHttplib(unittest.TestCase):

    client = 'httplib'
    client_options = {}

    def setUp(self):
        self.server = StopableWSGIServer.create(debug_app)
        self.application_url = self.server.application_url.rstrip('/')
        self.proxy = proxies.HostProxy(self.application_url,
                                       client=self.client,
                                       **self.client_options)
        self.app = TestApp(self.proxy)

    def test_form(self):
        resp = self.app.get('/form.html')
        resp.mustcontain('</form>')
        form = resp.form
        form['name'] = 'gawel'
        resp = form.submit()
        resp.mustcontain('name=gawel')

    def test_head(self):
        resp = self.app.head('/form.html')
        self.assertEqual(resp.status_int, 200)
        self.assertEqual(len(resp.body), 0)

    def test_webob_error(self):
        req = Request.blank('/')
        req.content_length = '-1'
        resp = req.get_response(self.proxy)
        self.assertEqual(resp.status_int, 500, resp)

    def test_not_allowed_method(self):
        resp = self.app.options('/', status='*')
        self.assertEqual(resp.status_int, 405)

    def test_status(self):
        resp = self.app.get('/?status=404', status='*')
        self.assertEqual(resp.status_int, 404)

    def test_redirect(self):
        location = self.application_url + '/form.html'
        resp = self.app.get('/?status=301%20Redirect&header-location=' +
                            location,
                            status='*')
        self.assertEqual(resp.status_int, 301, resp)
        self.assertEqual(resp.location, location)

        location = 'http://foo.com'
        resp = self.app.get('/?status=301%20Redirect&header-location=' +
                            location,
                            status='*')
        self.assertEqual(resp.status_int, 301, resp)
        self.assertEqual(resp.location, location)

        location = '/foo'
        resp = self.app.get('/?status=301%20Redirect&header-location=' +
                            location,
                            status='*')
        self.assertEqual(resp.status_int, 301, resp)
        self.assertEqual(resp.location, self.application_url + location)

        location = self.application_url + '/script_name/form.html'
        self.proxy.strip_script_name = False
        resp = self.app.get('/?status=301%20Redirect&header-Location=' +
                            location,
                            status='*',
                            extra_environ={'SCRIPT_NAME': '/script_name'})
        self.assertEqual(resp.status_int, 301, resp)
        self.assertEqual(resp.location, location)

    def test_chunked(self):
        resp = self.app.get('/', headers=[('Transfer-Encoding', 'chunked')])
        resp.mustcontain(no='chunked')

    def tearDown(self):
        self.server.shutdown()