Example #1
0
def test_resource_colander_validation_error():

    error = {'error': {'code': 400, 'message': '400 Bad Request'}}

    app = App()
    c = Client(app)

    response = c.put_json('/', {'password': '******'}, status=400)
    assert response.json == error

    response = c.patch_json('/', {'password': '******'}, status=400)
    assert response.json == error
Example #2
0
def test_resource_marshmallow_validation():

    app = App()
    c = Client(app)

    response = c.put_json('/', {
        'email': '*****@*****.**',
        'password': '******'
    })
    assert response.json == {}

    response = c.patch_json('/', {'email': '*****@*****.**'})
    assert response.json == {}
Example #3
0
def test_resource_marshmallow_validation_error():

    error = {
        'error': {
            'code': 400,
            'message': {
                'email': ['Missing data for required field.']
            }
        }
    }

    app = App()
    c = Client(app)

    response = c.put_json('/', {'password': '******'}, status=400)
    assert response.json == error

    response = c.patch_json('/', {'password': '******'}, status=400)
    assert response.json == error
Example #4
0
class TestIntegration(unittest.TestCase):

    def setUp(self):
        app = Application('tangled.web.tests:test.ini')
        app.mount_resource('user', UserResource, '/users/<id>')
        self.app = TestApp(app)
        self._original_data = copy.deepcopy(Users.data)

    def tearDown(self):
        Users.data = self._original_data

    def test_get(self):
        self.assertEqual(Users.get(1)['name'], 'Alice')
        response = self.app.get('/users/1')
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json['name'], 'Alice')
        self.assertEqual(Users.get(1)['name'], 'Alice')

    def test_put(self):
        self.assertEqual(Users.get(2)['name'], 'Bob')
        response = self.app.put('/users/2', params={'name': 'Bobby'})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json['name'], 'Bobby')
        self.assertEqual(Users.get(2)['name'], 'Bobby')

    def test_patch(self):
        self.assertEqual(Users.get(2)['name'], 'Bob')
        response = self.app.patch('/users/2', params={'name': 'Bobby'})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json['name'], 'Bobby')
        self.assertEqual(Users.get(2)['name'], 'Bobby')

    def test_patch_json(self):
        self.assertEqual(Users.get(2)['name'], 'Bob')
        response = self.app.patch_json('/users/2', params={'name': 'Bobby'})
        self.assertEqual(response.status_code, 200)
        self.assertEqual(response.json['name'], 'Bobby')
        self.assertEqual(Users.get(2)['name'], 'Bobby')
Example #5
0
class Modificacion(TestCase):
    """
    NOTA: Sobre la validación de datos, testar directamente nuestra pequeña clase 
    TODO: Validar cambio de grupo principal
    TODO: Validar cambio de estado de la cuenta
    TODO: Validar cambios de grupos
    TODO: Validar cambio de contraseña
    TODO: Validar que tan pocas claves podemos cambiar
    """

    @classmethod
    def setUpClass(self):

        # Cargamos los datos
        entidad = cargar_datos('usuario')[2]
        self.uid = entidad['uid']
        self.datos = {'corpus': entidad}

        # Trabajamos en obtener un token
        self.token = cargar_credenciales()
        
        # Creamos nuestro objeto para pruebas
        from justine import main
        from webtest import TestApp

        app = main({})
        self.testapp = TestApp(app)

        res = self.testapp.post_json('/usuarios', status=201, params=self.datos, headers=self.token)

    @classmethod
    def tearDownClass(self):
        res = self.testapp.head('/usuarios/' + self.uid, status="*", headers=self.token)
        if res.status_int == 200:
            self.testapp.delete('/usuarios/' + self.uid, status=200, headers=self.token)

    def test_actualizacion(self):
        self.datos['corpus']['title'] = "Titulador"
        
        self.testapp.patch_json('/usuarios/' + self.uid, status=200, params=self.datos, headers=self.token)
        
        res = self.testapp.get('/usuarios/' + self.uid, status=200, headers=self.token)
        respuesta = res.json_body['mensaje'][0]['title']
        datos = self.datos['corpus']['title']
        
        self.assertEqual(respuesta, datos)

    def test_actualizacion_displayName(self):
        sn = "Sotomayor"
        givenName = self.datos['corpus']['givenName']
        displayName = givenName + " " + sn 
        
        self.datos['corpus']['sn'] = sn
        self.testapp.patch_json('/usuarios/' + self.uid, status=200, params=self.datos, headers=self.token)
        
        res = self.testapp.get('/usuarios/' + self.uid, status=200, headers=self.token)
        respuesta = res.json_body['mensaje'][0]['displayName']
        
        self.assertEqual(respuesta, displayName)

    def test_corpus_faltante(self):
        datos = {'cuerpo': self.datos['corpus'].copy()}
        
        self.testapp.patch_json('/usuarios/' + self.uid, status=400, params=datos, headers=self.token)
    
    def test_json_malformateado(self):
        datos = "Mínimo esfuerzo para máximo daño"
        self.testapp.patch_json('/usuarios/' + self.uid, status=400, params=datos, headers=self.token)
    
    def test_noexistente(self):
        uid = 'fitzcarraldo'
        datos = {'corpus': self.datos['corpus'].copy()}
        datos['corpus']['uid'] = uid
        
        self.testapp.patch_json('/usuarios/' + uid, status=404, params=datos, headers=self.token)

    def test_claves_incompletas(self):
        cuerpo = self.datos['corpus'].copy()
        del cuerpo['sn']
        del cuerpo['givenName']
        datos = {'corpus': cuerpo}
        
        self.testapp.patch_json('/usuarios/' + self.uid, status=200, params=datos, headers=self.token)

    def test_actualizacion_noauth(self):
        self.testapp.patch_json('/usuarios/' + self.uid, status=403, params=self.datos)
Example #6
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 #7
0
class ApiTestCase(TestCase):

    def setUp(self):
        super(ApiTestCase, self).setUp()
        repo_store = self.useFixture(TempDir()).path
        self.useFixture(EnvironmentVariable("REPO_STORE", repo_store))
        self.app = TestApp(api.main({}))
        self.repo_path = uuid.uuid1().hex
        self.repo_store = os.path.join(repo_store, self.repo_path)
        self.repo_root = repo_store
        self.commit = {'ref': 'refs/heads/master', 'message': 'test commit.'}
        self.tag = {'ref': 'refs/tags/tag0', 'message': 'tag message'}

    def assertReferencesEqual(self, repo, expected, observed):
        self.assertEqual(
            repo.lookup_reference(expected).peel().oid,
            repo.lookup_reference(observed).peel().oid)

    def get_ref(self, ref):
        resp = self.app.get('/repo/{}/{}'.format(self.repo_path, ref))
        return resp.json

    def test_repo_init(self):
        resp = self.app.post_json('/repo', {'repo_path': self.repo_path})
        self.assertIn(self.repo_path, resp.json['repo_url'])
        self.assertEqual(200, resp.status_code)

    def test_repo_init_with_invalid_repo_path(self):
        resp = self.app.post_json('/repo', {'repo_path': '../1234'},
                                  expect_errors=True)
        self.assertEqual(404, resp.status_code)

    def test_repo_init_with_existing_repo(self):
        """Repo can be not be initialised with existing path."""
        factory = RepoFactory(self.repo_store)
        repo_path = os.path.basename(os.path.normpath(factory.repo_path))
        resp = self.app.post_json('/repo', {'repo_path': repo_path},
                                  expect_errors=True)
        self.assertEqual(409, resp.status_code)

    def test_repo_init_with_clone(self):
        """Repo can be initialised with optional clone."""
        factory = RepoFactory(self.repo_store, num_commits=2)
        factory.build()
        new_repo_path = uuid.uuid1().hex
        resp = self.app.post_json('/repo', {'repo_path': new_repo_path,
                                            'clone_from': self.repo_path,
                                            'clone_refs': True})
        repo1_revlist = get_revlist(factory.repo)
        clone_from = resp.json['repo_url'].split('/')[-1]
        repo2 = open_repo(os.path.join(self.repo_root, clone_from))
        repo2_revlist = get_revlist(repo2)

        self.assertEqual(repo1_revlist, repo2_revlist)
        self.assertEqual(200, resp.status_code)
        self.assertIn(new_repo_path, resp.json['repo_url'])

    def test_repo_get(self):
        """The GET method on a repository returns its properties."""
        factory = RepoFactory(self.repo_store, num_branches=2, num_commits=1)
        factory.build()
        factory.repo.set_head('refs/heads/branch-0')

        resp = self.app.get('/repo/{}'.format(self.repo_path))
        self.assertEqual(200, resp.status_code)
        self.assertEqual({'default_branch': 'refs/heads/branch-0'}, resp.json)

    def test_repo_get_default_branch_missing(self):
        """default_branch is returned even if that branch has been deleted."""
        factory = RepoFactory(self.repo_store, num_branches=2, num_commits=1)
        factory.build()
        factory.repo.set_head('refs/heads/branch-0')
        factory.repo.lookup_reference('refs/heads/branch-0').delete()

        resp = self.app.get('/repo/{}'.format(self.repo_path))
        self.assertEqual(200, resp.status_code)
        self.assertEqual({'default_branch': 'refs/heads/branch-0'}, resp.json)

    def test_repo_patch_default_branch(self):
        """A repository's default branch ("HEAD") can be changed."""
        factory = RepoFactory(self.repo_store, num_branches=2, num_commits=1)
        factory.build()
        factory.repo.set_head('refs/heads/branch-0')
        self.assertReferencesEqual(factory.repo, 'refs/heads/branch-0', 'HEAD')

        resp = self.app.patch_json(
            '/repo/{}'.format(self.repo_path),
            {'default_branch': 'refs/heads/branch-1'})
        self.assertEqual(204, resp.status_code)
        self.assertReferencesEqual(factory.repo, 'refs/heads/branch-1', 'HEAD')

    def test_cross_repo_merge_diff(self):
        """Merge diff can be requested across 2 repositories."""
        factory = RepoFactory(self.repo_store)
        c1 = factory.add_commit('foo', 'foobar.txt')

        repo2_name = uuid.uuid4().hex
        factory2 = RepoFactory(
            os.path.join(self.repo_root, repo2_name), clone_from=factory)
        c2 = factory.add_commit('bar', 'foobar.txt', parents=[c1])
        c3 = factory2.add_commit('baz', 'foobar.txt', parents=[c1])

        resp = self.app.get('/repo/{}:{}/compare-merge/{}:{}'.format(
            self.repo_path, repo2_name, c2, c3))
        self.assertIn('-bar', resp.json['patch'])

    def test_cross_repo_diff(self):
        """Diff can be requested across 2 repositories."""
        factory = RepoFactory(self.repo_store)
        c1 = factory.add_commit('foo', 'foobar.txt')

        repo2_name = uuid.uuid4().hex
        factory2 = RepoFactory(
            os.path.join(self.repo_root, repo2_name), clone_from=factory)
        c2 = factory.add_commit('bar', 'foobar.txt', parents=[c1])
        c3 = factory2.add_commit('baz', 'foobar.txt', parents=[c1])

        resp = self.app.get('/repo/{}:{}/compare/{}..{}'.format(
            self.repo_path, repo2_name, c2, c3))
        self.assertIn('-bar', resp.json['patch'])
        self.assertIn('+baz', resp.json['patch'])

    def test_cross_repo_diff_invalid_repo(self):
        """Cross repo diff with invalid repo returns HTTP 404."""
        resp = self.app.get('/repo/1:2/compare-merge/3:4', expect_errors=True)
        self.assertEqual(404, resp.status_code)

    def test_cross_repo_diff_invalid_commit(self):
        """Cross repo diff with an invalid commit returns HTTP 404."""
        factory = RepoFactory(self.repo_store)
        c1 = factory.add_commit('foo', 'foobar.txt')

        repo2_name = uuid.uuid4().hex
        RepoFactory(
            os.path.join(self.repo_root, repo2_name), clone_from=factory)
        c2 = factory.add_commit('bar', 'foobar.txt', parents=[c1])

        resp = self.app.get('/repo/{}:{}/diff/{}:{}'.format(
            self.repo_path, repo2_name, c2, 'invalid'), expect_errors=True)
        self.assertEqual(404, resp.status_code)

    def test_repo_delete(self):
        self.app.post_json('/repo', {'repo_path': self.repo_path})
        resp = self.app.delete('/repo/{}'.format(self.repo_path))
        self.assertEqual(200, resp.status_code)
        self.assertFalse(os.path.exists(self.repo_store))

    def test_repo_get_refs(self):
        """Ensure expected ref objects are returned and shas match."""
        ref = self.commit.get('ref')
        repo = RepoFactory(self.repo_store, num_commits=1, num_tags=1).build()
        resp = self.app.get('/repo/{}/refs'.format(self.repo_path))
        body = resp.json

        self.assertTrue(ref in body)
        self.assertTrue(self.tag.get('ref') in body)

        oid = repo.head.get_object().oid.hex  # git object sha
        resp_sha = body[ref]['object'].get('sha1')
        self.assertEqual(oid, resp_sha)

    def test_repo_get_refs_nonexistent(self):
        """get_refs on a non-existent repository returns HTTP 404."""
        resp = self.app.get('/repo/1/refs', expect_errors=True)
        self.assertEqual(404, resp.status_code)

    def test_ignore_non_unicode_refs(self):
        """Ensure non-unicode refs are dropped from ref collection."""
        factory = RepoFactory(self.repo_store)
        commit_oid = factory.add_commit('foo', 'foobar.txt')
        tag = '\xe9\xe9\xe9'  # latin-1
        tag_message = 'tag message'
        factory.add_tag(tag, tag_message, commit_oid)

        resp = self.app.get('/repo/{}/refs'.format(self.repo_path))
        refs = resp.json
        self.assertEqual(1, len(refs.keys()))

    def test_allow_unicode_refs(self):
        """Ensure unicode refs are included in ref collection."""
        factory = RepoFactory(self.repo_store)
        commit_oid = factory.add_commit('foo', 'foobar.txt')
        tag = u'おいしいイカ'.encode('utf-8')
        tag_message = u'かわいい タコ'.encode('utf-8')
        factory.add_tag(tag, tag_message, commit_oid)

        resp = self.app.get('/repo/{}/refs'.format(self.repo_path))
        refs = resp.json
        self.assertEqual(2, len(refs.keys()))

    def test_repo_get_ref(self):
        RepoFactory(self.repo_store, num_commits=1).build()
        ref = 'refs/heads/master'
        resp = self.get_ref(ref)
        self.assertTrue(ref in resp)

    def test_repo_get_ref_nonexistent_repository(self):
        """get_ref on a non-existent repository returns HTTP 404."""
        resp = self.app.get('/repo/1/refs/heads/master', expect_errors=True)
        self.assertEqual(404, resp.status_code)

    def test_repo_get_ref_nonexistent_ref(self):
        """get_ref on a non-existent ref in a repository returns HTTP 404."""
        RepoFactory(self.repo_store, num_commits=1).build()
        resp = self.app.get(
            '/repo/{}/refs/heads/master'.format(self.repo_path))
        self.assertEqual(200, resp.status_code)
        resp = self.app.get(
            '/repo/{}/refs/heads/nonexistent'.format(self.repo_path),
            expect_errors=True)
        self.assertEqual(404, resp.status_code)

    def test_repo_get_unicode_ref(self):
        factory = RepoFactory(self.repo_store)
        commit_oid = factory.add_commit('foo', 'foobar.txt')
        tag_name = u'☃'.encode('utf-8')
        tag_message = u'☃'.encode('utf-8')
        factory.add_tag(tag_name, tag_message, commit_oid)

        tag = 'refs/tags/{}'.format(tag_name)
        resp = self.get_ref(tag)
        self.assertTrue(tag.decode('utf-8') in resp)

    def test_repo_get_tag(self):
        RepoFactory(self.repo_store, num_commits=1, num_tags=1).build()
        tag = self.tag.get('ref')
        resp = self.get_ref(tag)
        self.assertTrue(tag in resp)

    def test_repo_compare_commits(self):
        """Ensure expected changes exist in diff patch."""
        repo = RepoFactory(self.repo_store)
        c1_oid = repo.add_commit('foo', 'foobar.txt')
        c2_oid = repo.add_commit('bar', 'foobar.txt', parents=[c1_oid])

        path = '/repo/{}/compare/{}..{}'.format(self.repo_path, c1_oid, c2_oid)
        resp = self.app.get(path)
        self.assertIn('-foo', resp.body)
        self.assertIn('+bar', resp.body)

    def test_repo_diff_commits(self):
        """Ensure expected commits objects are returned in diff."""
        repo = RepoFactory(self.repo_store)
        c1_oid = repo.add_commit('foo', 'foobar.txt')
        c2_oid = repo.add_commit('bar', 'foobar.txt', parents=[c1_oid])

        path = '/repo/{}/compare/{}..{}'.format(self.repo_path, c1_oid, c2_oid)
        resp = self.app.get(path)
        self.assertIn(c1_oid.hex, resp.json['commits'][0]['sha1'])
        self.assertIn(c2_oid.hex, resp.json['commits'][1]['sha1'])

    def test_repo_diff_unicode_commits(self):
        """Ensure expected utf-8 commits objects are returned in diff."""
        factory = RepoFactory(self.repo_store)
        message = u'屋漏偏逢连夜雨'.encode('utf-8')
        message2 = u'说曹操,曹操到'.encode('utf-8')
        oid = factory.add_commit(message, 'foo.py')
        oid2 = factory.add_commit(message2, 'bar.py', [oid])

        resp = self.app.get('/repo/{}/compare/{}..{}'.format(
            self.repo_path, oid, oid2))
        self.assertEqual(resp.json['commits'][0]['message'],
                         message.decode('utf-8'))
        self.assertEqual(resp.json['commits'][1]['message'],
                         message2.decode('utf-8'))

    def test_repo_diff_non_unicode_commits(self):
        """Ensure non utf-8 chars are handled but stripped from diff."""
        factory = RepoFactory(self.repo_store)
        message = 'not particularly sensible latin-1: \xe9\xe9\xe9.'
        oid = factory.add_commit(message, 'foo.py')
        oid2 = factory.add_commit('a sensible commit message', 'foo.py', [oid])

        resp = self.app.get('/repo/{}/compare/{}..{}'.format(
            self.repo_path, oid, oid2))
        self.assertEqual(resp.json['commits'][0]['message'],
                         message.decode('utf-8', 'replace'))

    def test_repo_get_diff_nonexistent_sha1(self):
        """get_diff on a non-existent sha1 returns HTTP 404."""
        RepoFactory(self.repo_store).build()
        resp = self.app.get('/repo/{}/compare/1..2'.format(
            self.repo_path), expect_errors=True)
        self.assertEqual(404, resp.status_code)

    def test_repo_get_diff_invalid_separator(self):
        """get_diff with an invalid separator (not ../...) returns HTTP 404."""
        RepoFactory(self.repo_store).build()
        resp = self.app.get('/repo/{}/compare/1++2'.format(
            self.repo_path), expect_errors=True)
        self.assertEqual(400, resp.status_code)

    def test_repo_common_ancestor_diff(self):
        """Ensure expected changes exist in diff patch."""
        repo = RepoFactory(self.repo_store)
        c1 = repo.add_commit('foo', 'foobar.txt')
        c2_right = repo.add_commit('bar', 'foobar.txt', parents=[c1])
        c3_right = repo.add_commit('baz', 'foobar.txt', parents=[c2_right])
        c2_left = repo.add_commit('qux', 'foobar.txt', parents=[c1])
        c3_left = repo.add_commit('corge', 'foobar.txt', parents=[c2_left])

        resp = self.app.get('/repo/{}/compare/{}...{}'.format(
            self.repo_path, c3_left, c3_right))
        self.assertIn('-foo', resp.json_body['patch'])
        self.assertIn('+baz', resp.json_body['patch'])
        self.assertNotIn('+corge', resp.json_body['patch'])

    def test_repo_diff_empty(self):
        """Ensure that diffing two identical commits returns an empty string
        as the patch, not None."""
        repo = RepoFactory(self.repo_store)
        c1 = repo.add_commit('foo\n', 'blah.txt')

        resp = self.app.get('/repo/{}/compare/{}..{}'.format(
            self.repo_path, c1, c1))
        self.assertEqual('', resp.json_body['patch'])

    def test_repo_diff_merge(self):
        """Ensure expected changes exist in diff patch."""
        repo = RepoFactory(self.repo_store)
        c1 = repo.add_commit('foo\nbar\nbaz\n', 'blah.txt')
        c2_right = repo.add_commit('quux\nbar\nbaz\n', 'blah.txt',
                                   parents=[c1])
        c3_right = repo.add_commit('quux\nbar\nbaz\n', 'blah.txt',
                                   parents=[c2_right])
        c2_left = repo.add_commit('foo\nbar\nbar\n', 'blah.txt', parents=[c1])
        c3_left = repo.add_commit('foo\nbar\nbar\n', 'blah.txt',
                                  parents=[c2_left])

        resp = self.app.get('/repo/{}/compare-merge/{}:{}'.format(
            self.repo_path, c3_right, c3_left))
        self.assertIn(' quux', resp.json_body['patch'])
        self.assertIn('-baz', resp.json_body['patch'])
        self.assertIn('+bar', resp.json_body['patch'])
        self.assertNotIn('foo', resp.json_body['patch'])
        self.assertEqual([], resp.json_body['conflicts'])

    def test_repo_diff_merge_with_conflicts(self):
        """Ensure that compare-merge returns conflicts information."""
        repo = RepoFactory(self.repo_store)
        c1 = repo.add_commit('foo\n', 'blah.txt')
        c2_left = repo.add_commit('foo\nbar\n', 'blah.txt', parents=[c1])
        c2_right = repo.add_commit('foo\nbaz\n', 'blah.txt', parents=[c1])

        resp = self.app.get('/repo/{}/compare-merge/{}:{}'.format(
            self.repo_path, c2_left, c2_right))
        self.assertIn(dedent("""\
            +<<<<<<< blah.txt
             bar
            +=======
            +baz
            +>>>>>>> blah.txt
            """), resp.json_body['patch'])
        self.assertEqual(['blah.txt'], resp.json_body['conflicts'])

    def test_repo_diff_merge_empty(self):
        """Ensure that diffing two identical commits returns an empty string
        as the patch, not None."""
        repo = RepoFactory(self.repo_store)
        c1 = repo.add_commit('foo\n', 'blah.txt')

        resp = self.app.get('/repo/{}/compare-merge/{}:{}'.format(
            self.repo_path, c1, c1))
        self.assertEqual('', resp.json_body['patch'])

    def test_repo_get_commit(self):
        factory = RepoFactory(self.repo_store)
        message = 'Computers make me angry.'
        commit_oid = factory.add_commit(message, 'foobar.txt')

        resp = self.app.get('/repo/{}/commits/{}'.format(
            self.repo_path, commit_oid.hex))
        commit_resp = resp.json
        self.assertEqual(commit_oid.hex, commit_resp['sha1'])
        self.assertEqual(message, commit_resp['message'])

    def test_repo_get_commit_nonexistent(self):
        """Trying to get a non-existent OID returns HTTP 404."""
        factory = RepoFactory(self.repo_store)
        resp = self.app.get('/repo/{}/commits/{}'.format(
            self.repo_path, factory.nonexistent_oid()), expect_errors=True)
        self.assertEqual(404, resp.status_code)

    def test_repo_get_non_commit(self):
        """Trying to get a non-commit returns HTTP 404."""
        factory = RepoFactory(self.repo_store, num_commits=1)
        factory.build()
        tree_oid = factory.repo[factory.commits[0]].tree.hex
        resp = self.app.get('/repo/{}/commits/{}'.format(
            self.repo_path, tree_oid), expect_errors=True)
        self.assertEqual(404, resp.status_code)

    def test_repo_get_commit_collection(self):
        """Ensure commits can be returned in bulk."""
        factory = RepoFactory(self.repo_store, num_commits=10)
        factory.build()
        bulk_commits = {'commits': [c.hex for c in factory.commits[0::2]]}

        resp = self.app.post_json('/repo/{}/commits'.format(
            self.repo_path), bulk_commits)
        self.assertEqual(5, len(resp.json))
        self.assertEqual(bulk_commits['commits'][0], resp.json[0]['sha1'])

    def test_repo_get_commit_collection_ignores_errors(self):
        """Non-existent OIDs and non-commits in a collection are ignored."""
        factory = RepoFactory(self.repo_store, num_commits=10)
        factory.build()
        bulk_commits = {
            'commits': [
                factory.commits[0].hex,
                factory.repo[factory.commits[0]].tree.hex,
                factory.nonexistent_oid(),
                ],
            }

        resp = self.app.post_json(
            '/repo/{}/commits'.format(self.repo_path), bulk_commits)
        self.assertEqual(1, len(resp.json))
        self.assertEqual(bulk_commits['commits'][0], resp.json[0]['sha1'])

    def test_repo_get_log_signatures(self):
        """Ensure signatures are correct."""
        factory = RepoFactory(self.repo_store)
        committer = factory.makeSignature(u'村上 春樹'.encode('utf-8'),
                                          u'tsukuru@猫の町.co.jp'.encode('utf-8'),
                                          encoding='utf-8')
        author = factory.makeSignature(
            u'Владимир Владимирович Набоков'.encode('utf-8'),
            u'Набоко@zembla.ru'.encode('utf-8'), encoding='utf-8')
        oid = factory.add_commit('Obfuscate colophon.', 'path.foo',
                                 author=author, committer=committer)
        resp = self.app.get('/repo/{}/log/{}'.format(self.repo_path, oid))
        self.assertEqual(author.name, resp.json[0]['author']['name'])

    def test_repo_get_log(self):
        factory = RepoFactory(self.repo_store, num_commits=4)
        factory.build()
        commits_from = factory.commits[2].hex
        resp = self.app.get('/repo/{}/log/{}'.format(
            self.repo_path, commits_from))
        self.assertEqual(3, len(resp.json))

    def test_repo_get_unicode_log(self):
        factory = RepoFactory(self.repo_store)
        message = u'나는 김치 사랑'.encode('utf-8')
        message2 = u'(╯°□°)╯︵ ┻━┻'.encode('utf-8')
        oid = factory.add_commit(message, '자장면/짜장면.py')
        oid2 = factory.add_commit(message2, '엄마야!.js', [oid])

        resp = self.app.get('/repo/{}/log/{}'.format(self.repo_path, oid2))
        self.assertEqual(message2.decode('utf-8', 'replace'),
                         resp.json[0]['message'])
        self.assertEqual(message.decode('utf-8', 'replace'),
                         resp.json[1]['message'])

    def test_repo_get_non_unicode_log(self):
        """Ensure that non-unicode data is discarded."""
        factory = RepoFactory(self.repo_store)
        message = '\xe9\xe9\xe9'  # latin-1
        oid = factory.add_commit(message, 'foo.py')
        resp = self.app.get('/repo/{}/log/{}'.format(self.repo_path, oid))
        self.assertEqual(message.decode('utf-8', 'replace'),
                         resp.json[0]['message'])

    def test_repo_get_log_with_limit(self):
        """Ensure the commit log can filtered by limit."""
        factory = RepoFactory(self.repo_store, num_commits=10)
        repo = factory.build()
        head = repo.head.target
        resp = self.app.get('/repo/{}/log/{}?limit=5'.format(
            self.repo_path, head))
        self.assertEqual(5, len(resp.json))

    def test_repo_get_log_with_stop(self):
        """Ensure the commit log can be filtered by a stop commit."""
        factory = RepoFactory(self.repo_store, num_commits=10)
        repo = factory.build()
        stop_commit = factory.commits[4]
        excluded_commit = factory.commits[5]
        head = repo.head.target
        resp = self.app.get('/repo/{}/log/{}?stop={}'.format(
            self.repo_path, head, stop_commit))
        self.assertEqual(5, len(resp.json))
        self.assertNotIn(excluded_commit, resp.json)

    def test_repo_repack_verify_pack(self):
        """Ensure commit exists in pack."""
        factory = RepoFactory(self.repo_store)
        oid = factory.add_commit('foo', 'foobar.txt')
        resp = self.app.post_json('/repo/{}/repack'.format(self.repo_path),
                                  {'prune': True, 'single': True})
        for filename in factory.packs:
            pack = os.path.join(factory.pack_dir, filename)
        out = subprocess.check_output(['git', 'verify-pack', pack, '-v'])
        self.assertEqual(200, resp.status_code)
        self.assertIn(oid.hex, out)

    def test_repo_repack_verify_commits_to_pack(self):
        """Ensure commits in different packs exist in merged pack."""
        factory = RepoFactory(self.repo_store)
        oid = factory.add_commit('foo', 'foobar.txt')
        with chdir(factory.pack_dir):
            subprocess.call(['git', 'gc', '-q'])  # pack first commit
            oid2 = factory.add_commit('bar', 'foobar.txt', [oid])
            p = subprocess.Popen(['git', 'pack-objects', '-q', 'pack2'],
                                 stdin=subprocess.PIPE, stdout=subprocess.PIPE)
            p.communicate(input=oid2.hex)
        self.assertEqual(2, len(factory.packs))  # ensure 2 packs exist
        self.app.post_json('/repo/{}/repack'.format(self.repo_path),
                           {'prune': True, 'single': True})
        self.assertEqual(1, len(factory.packs))
        repacked_pack = os.path.join(factory.pack_dir, factory.packs[0])
        out = subprocess.check_output(['git', 'verify-pack',
                                       repacked_pack, '-v'])
        self.assertIn(oid.hex, out)
        self.assertIn(oid2.hex, out)

    def test_repo_detect_merges_missing_target(self):
        """A non-existent target OID returns HTTP 404."""
        factory = RepoFactory(self.repo_store)
        resp = self.app.post_json('/repo/{}/detect-merges/{}'.format(
            self.repo_path, factory.nonexistent_oid()),
            {'sources': []}, expect_errors=True)
        self.assertEqual(404, resp.status_code)

    def test_repo_detect_merges_missing_source(self):
        """A non-existent source commit is ignored."""
        factory = RepoFactory(self.repo_store)
        # A---B
        a = factory.add_commit('a\n', 'file')
        b = factory.add_commit('b\n', 'file', parents=[a])
        resp = self.app.post_json('/repo/{}/detect-merges/{}'.format(
            self.repo_path, b),
            {'sources': [factory.nonexistent_oid()]})
        self.assertEqual(200, resp.status_code)
        self.assertEqual({}, resp.json)

    def test_repo_detect_merges_unmerged(self):
        """An unmerged commit is not returned."""
        factory = RepoFactory(self.repo_store)
        # A---B
        #  \
        #   C
        a = factory.add_commit('a\n', 'file')
        b = factory.add_commit('b\n', 'file', parents=[a])
        c = factory.add_commit('c\n', 'file', parents=[a])
        resp = self.app.post_json('/repo/{}/detect-merges/{}'.format(
            self.repo_path, b),
            {'sources': [c.hex]})
        self.assertEqual(200, resp.status_code)
        self.assertEqual({}, resp.json)

    def test_repo_detect_merges_pulled(self):
        """Commits that were pulled (fast-forward) are their own merge
        points."""
        factory = RepoFactory(self.repo_store)
        # A---B---C
        a = factory.add_commit('a\n', 'file')
        b = factory.add_commit('b\n', 'file', parents=[a])
        c = factory.add_commit('c\n', 'file', parents=[b])
        # The start commit would never be the source of a merge proposal,
        # but include it anyway to test boundary conditions.
        resp = self.app.post_json('/repo/{}/detect-merges/{}'.format(
            self.repo_path, c),
            {'sources': [a.hex, b.hex, c.hex]})
        self.assertEqual(200, resp.status_code)
        self.assertEqual({a.hex: a.hex, b.hex: b.hex, c.hex: c.hex}, resp.json)

    def test_repo_detect_merges_merged(self):
        """Commits that were merged have sensible merge points."""
        factory = RepoFactory(self.repo_store)
        # A---C---D---G---H
        #  \ /       /
        #   B---E---F---I
        a = factory.add_commit('a\n', 'file')
        b = factory.add_commit('b\n', 'file', parents=[a])
        c = factory.add_commit('c\n', 'file', parents=[a, b])
        d = factory.add_commit('d\n', 'file', parents=[c])
        e = factory.add_commit('e\n', 'file', parents=[b])
        f = factory.add_commit('f\n', 'file', parents=[e])
        g = factory.add_commit('g\n', 'file', parents=[d, f])
        h = factory.add_commit('h\n', 'file', parents=[g])
        i = factory.add_commit('i\n', 'file', parents=[f])
        resp = self.app.post_json('/repo/{}/detect-merges/{}'.format(
            self.repo_path, h),
            {'sources': [b.hex, e.hex, i.hex]})
        self.assertEqual(200, resp.status_code)
        self.assertEqual({b.hex: c.hex, e.hex: g.hex}, resp.json)
Example #8
0
class FlaskAdapterTests(unittest.TestCase):
    def setUp(self):
        from tests.example_app.flask_app import create_pale_flask_app
        self.flask_app = create_pale_flask_app()
        self.app = TestApp(self.flask_app)

    def assertExpectedFields(self, returned_dict, expected_fields):
        d = returned_dict.copy()  # don't clobber the input

        for f in expected_fields:
            self.assertIn(f, d)
            val = d.pop(f)
            # don't check the val for now

        # make sure there's nothing extraneous left in the dict
        self.assertEqual(len(d.keys()), 0)
        return

    def test_successful_get_with_route_args(self):
        now = datetime.datetime.utcnow()
        resp = self.app.get('/api/arg_test/arg-a/arg-b')
        self.assertEqual(resp.status_code, 200)

        self.assertEqual(resp.json, {
            "arg_a": "arg-a",
            "arg_b": "arg-b",
        })

    def test_successful_get_without_params(self):
        now = datetime.datetime.utcnow()
        resp = self.app.get('/api/time/current')
        self.assertEqual(resp.status_code, 200)
        # Test _after_response_handlers
        self.assertIn("After-Response", resp.headers)
        self.assertEqual(resp.headers["After-Response"], 'OK')
        # Test CORS
        self.assertIn("Access-Control-Allow-Origin", resp.headers)
        self.assertEqual(resp.headers["Access-Control-Allow-Origin"], '*')

        # the 'time' value was set in the endpoint handler
        self.assertIn('time', resp.json_body)

        # the returned time value should match the resource defined
        # in tests.example_app.api.resources.py
        returned_time = resp.json_body['time']

        # the endpoint specifies `fields=DateTimeResource._all_fields()`
        # so, we should expect to find all of them
        expected_fields = DateTimeResource._all_fields()

        self.assertExpectedFields(returned_time, expected_fields)
        self.assertEqual(returned_time['eurodate'], now.strftime("%d.%m.%Y"))

    def test_successful_post_with_required_params(self):
        # month is required in the endpoint definition, so we must pass
        # it in here
        resp = self.app.post('/api/time/parse', {'month': 2})

        self.assertEqual(resp.status_code, 200)
        self.assertIn('time', resp.json_body)

        returned_time = resp.json_body['time']

        # we didn't specify any other fields in the endpoint definition,
        # so this one should only get the defaults
        expected_fields = DateTimeResource._default_fields

        self.assertExpectedFields(returned_time, expected_fields)

        self.assertIn('Cache-Control', resp.headers)
        self.assertEqual('max-age=3', resp.headers['Cache-Control'])

    def test_successful_json_post_with_required_params(self):
        # this is the same as the above post, but passes json in the
        # request body, instead of x-www-form-urlencoded
        resp = self.app.post_json('/api/time/parse', {'month': 2})

        self.assertEqual(resp.status_code, 200)
        self.assertIn('time', resp.json_body)

        returned_time = resp.json_body['time']

        # we didn't specify any other fields in the endpoint definition,
        # so this one should only get the defaults
        expected_fields = DateTimeResource._default_fields

        self.assertExpectedFields(returned_time, expected_fields)

    def test_unsuccessful_post_missing_required_params(self):
        resp = self.app.post('/api/time/parse', status=422)

        self.assertIn('error', resp.json_body)

    def test_getting_with_nested_resources(self):
        test_duration = 60 * 1000  # one minute in milliseconds
        resp = self.app.get('/api/time/range', {'duration': test_duration})

        self.assertEqual(resp.status_code, 200)
        self.assertIn('range', resp.json_body)

        returned_range = resp.json_body['range']
        self.assertEqual(returned_range['duration_microseconds'],
                         test_duration * 1000)

        # start has default fields
        start = returned_range['start']
        expected_fields = DateTimeResource._default_fields
        self.assertExpectedFields(start, expected_fields)

        # end has all of them
        end = returned_range['end']
        expected_fields = DateTimeResource._all_fields()
        self.assertExpectedFields(end, expected_fields)

    def test_resource(self):

        # Start by resetting the resource.
        # (multiple test runs from the same process will fail otherwise)
        resp = self.app.post('/api/resource/reset')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        self.assertEqual(resp.json, {'key': 'value'})

        # Test creating a new resource
        resp = self.app.put_json('/api/resource', {'key': 'boop'},
                                 headers={'Content-Type': 'application/json'})
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        self.assertEqual(resp.json, {'key': 'boop'})

        # Test retrieving the resource.
        resp = self.app.get('/api/resource')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        self.assertEqual(resp.json, {'key': 'boop'})

        # Test patching the resource.
        # Without the correct Content-Type, we expect a 415 error.
        self.assertRaises(AppError, self.app.patch_json, '/api/resource',
                          {'key': 'value2'})

        resp = self.app.patch_json(
            '/api/resource', {'key': 'value2'},
            headers={'Content-Type': 'application/merge-patch+json'})
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        self.assertEqual(resp.json, {'key': 'value2'})

        # Test get to ensure the resource persists.
        resp = self.app.get('/api/resource')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        self.assertEqual(resp.json, {'key': 'value2'})

        # A NoContentResource shouldn't have a Content-Type header (no content!)
        resp = self.app.post('/api/blank')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, None)
Example #9
0
class Client(object):
    def __init__(self):
        self.core = Core(db_type="memory").execute_wsgi()
        self.test_core = TestApp(self.core)

    def migrate_in_memory(self,
                          migrations_path,
                          alembic_ini_path=None,
                          connection=None,
                          revision="head"):
        config = Config(alembic_ini_path)
        config.set_main_option("script_location", migrations_path)
        if connection is not None:
            config.attributes["connection"] = connection
        command.upgrade(config, revision)

    def get_api(self, api_url, *auth):
        response = APIResponse()
        __api_url = str(api_url)
        if auth:
            self.test_core.set_authorization(auth)
        test_core_response = self.test_core.get(__api_url)
        response.json = test_core_response.json
        response.status = test_core_response.status
        response.status_code = test_core_response.status_code
        response.body = test_core_response.body
        response.content_type = test_core_response.content_type

        return response

    def post_api(self, api_url, data, *auth):
        response = APIResponse()
        __api_url = str(api_url)
        if auth:
            self.test_core.set_authorization(auth)
        test_core_response = self.test_core.post_json(__api_url, params=data)
        response.json = json.dumps(test_core_response.json)
        response.status = test_core_response.status
        response.status_code = test_core_response.status_code
        response.body = test_core_response.body
        return response

    def patch_api(self, api_url, data, *auth):
        response = APIResponse()
        __api_url = str(api_url)
        if auth:
            self.test_core.set_authorization(auth)
        test_core_response = self.test_core.patch_json(__api_url, params=data)
        response.json = json.dumps(test_core_response.json)
        response.status = test_core_response.status
        response.status_code = test_core_response.status_code
        response.body = test_core_response.body
        return response

    def put_api(self, api_url, data, *auth):
        response = APIResponse()
        __api_url = str(api_url)
        if auth:
            self.test_core.set_authorization(auth)
        test_core_response = self.test_core.put_json(__api_url, params=data)
        response.json = json.dumps(test_core_response.json)
        response.status = test_core_response.status
        response.status_code = test_core_response.status_code
        response.body = test_core_response.body
        return response

    def delete_api(self, api_url, data, *auth):
        response = APIResponse()
        __api_url = str(api_url)
        if auth:
            self.test_core.set_authorization(auth)
        test_core_response = self.test_core.delete_json(__api_url, params=data)
        response.json = json.dumps(test_core_response.json)
        response.status = test_core_response.status
        response.status_code = test_core_response.status_code
        response.body = test_core_response.body
        return response
Example #10
0
class FunctionalTests(unittest.TestCase):
    def setUp(self):
        import tempfile
        import os.path
        from . import main

        self.tmpdir = tempfile.mkdtemp()

        dbpath = os.path.join(self.tmpdir, "test.db")
        uri = "file://" + dbpath
        settings = {
            "zodbconn.uri": uri,
            "pyramid.includes": ["pyramid_zodbconn", "pyramid_tm"],
        }

        app = main({}, **settings)
        self.db = app.registry._zodb_databases[""]
        from webtest import TestApp

        self.testapp = TestApp(app)

    def tearDown(self):
        import shutil

        self.db.close()
        shutil.rmtree(self.tmpdir)

    def test_root(self):
        res = self.testapp.get("/", status=200)
        self.assertTrue(b"" in res.body)

    def test_add_container(self):
        res = self.testapp.post_json("/", {"app_id": "my_app"})
        res = self.testapp.get("/", status=200)
        self.assertEqual(["my_app"], res.json_body)
        with pytest.raises(AppError):
            self.testapp.post_json("/", {"app_idd": "my_app"})
        res = self.testapp.get("/my_app", status=200)
        res = self.testapp.post_json("/my_app", {"container_id": "iasmartweb"})
        res = self.testapp.get("/my_app", status=200)
        self.assertEqual(["iasmartweb"], res.json_body)

    def test_add_content(self):
        res = self.testapp.post_json("/", {"app_id": "my_app"})
        res = self.testapp.get("/", status=200)
        self.assertEqual(["my_app"], res.json_body)
        with pytest.raises(AppError):
            self.testapp.post_json("/", {"app_idd": "my_app"})
        res = self.testapp.get("/my_app", status=200)
        res = self.testapp.post_json("/my_app", {"container_id": "iasmartweb"})
        res = self.testapp.get("/my_app", status=200)
        self.assertEqual(["iasmartweb"], res.json_body)
        res = self.testapp.post_json("/my_app/iasmartweb", {"content_id": "bsuttor"})
        res = self.testapp.get("/my_app/iasmartweb", status=200)
        self.assertEqual(["bsuttor"], res.json_body)

    def test_remove_content(self):
        res = self.testapp.post_json("/", {"app_id": "my_app"})
        res = self.testapp.post_json("/my_app", {"container_id": "imio"})
        res = self.testapp.post_json("/my_app/imio", {"container_id": "iasmartweb"})
        res = self.testapp.post_json(
            "/my_app/imio/iasmartweb",
            {
                "content_id": "bsuttor",
                "username": "******",
                "email": "*****@*****.**",
                "fullname": "Benoît Suttor",
            },
        )
        res = self.testapp.get("/my_app/imio/iasmartweb/bsuttor", status=200)
        self.assertEqual(res.json.get("email"), "*****@*****.**")
        res = self.testapp.get("/my_app/imio/iasmartweb", status=200)
        self.assertEqual(len(res.json), 1)
        self.testapp.delete_json("/my_app/imio/iasmartweb/bsuttor")
        res = self.testapp.get("/my_app/imio/iasmartweb", status=200)
        self.assertEqual(len(res.json), 0)

    def test_update_content(self):
        res = self.testapp.post_json("/", {"app_id": "my_app"})
        res = self.testapp.post_json("/my_app", {"container_id": "imio"})
        res = self.testapp.post_json("/my_app/imio", {"container_id": "iasmartweb"})
        res = self.testapp.post_json(
            "/my_app/imio/iasmartweb",
            {
                "content_id": "bsuttor",
                "username": "******",
                "email": "*****@*****.**",
                "fullname": "Benoît Suttor",
            },
        )
        res = self.testapp.get("/my_app/imio/iasmartweb", status=200)
        self.assertEqual(len(res.json), 1)
        res = self.testapp.patch_json(
            "/my_app/imio/iasmartweb/bsuttor", {"email": "*****@*****.**"}
        )
        res = self.testapp.get("/my_app/imio/iasmartweb", status=200)
        self.assertEqual(len(res.json), 1)
        res = self.testapp.get("/my_app/imio/iasmartweb/bsuttor", status=200)
        self.assertEqual(res.json.get("email"), "*****@*****.**")

    def test_replace_content(self):
        res = self.testapp.post_json("/", {"app_id": "my_app"})
        res = self.testapp.post_json("/my_app", {"container_id": "imio"})
        res = self.testapp.post_json("/my_app/imio", {"container_id": "iasmartweb"})
        res = self.testapp.post_json(
            "/my_app/imio/iasmartweb",
            {
                "content_id": "bsuttor",
                "username": "******",
                "email": "*****@*****.**",
                "fullname": "Benoît Suttor",
            },
        )
        res = self.testapp.get("/my_app/imio/iasmartweb", status=200)
        self.assertEqual(len(res.json), 1)
        res = self.testapp.put_json(
            "/my_app/imio/iasmartweb/bsuttor",
            {
                "content_id": "benoit",
                "username": "******",
                "email": "*****@*****.**",
                "fullname": "Ben Suttor",
            },
        )
        res = self.testapp.get("/my_app/imio/iasmartweb", status=200)
        self.assertEqual(len(res.json), 1)
        res = self.testapp.get("/my_app/imio/iasmartweb/bsuttor", status=404)
        self.assertEqual(
            res.body, b"The path my_app/imio/iasmartweb/bsuttor is not found"
        )  # noqa
        res = self.testapp.get("/my_app/imio/iasmartweb/benoit", status=200)
        self.assertEqual(res.json.get("email"), "*****@*****.**")
        res = self.testapp.get("/my_app/imio/iasmartweb", status=200)
        self.assertEqual(len(res.json), 1)

    def test_export_csv(self):
        res = self.testapp.post_json("/", {"app_id": "my_app"})
        res = self.testapp.post_json("/my_app", {"container_id": "imio"})
        res = self.testapp.post_json("/my_app/imio", {"container_id": "iasmartweb"})
        res = self.testapp.post_json("/my_app/imio", {"container_id": "iaurban"})
        res = self.testapp.post_json("/my_app/imio", {"container_id": "iateleservice"})
        res = self.testapp.post_json(
            "/my_app/imio/iasmartweb",
            {
                "content_id": "bsuttor",
                "username": "******",
                "email": "*****@*****.**",
                "fullname": "Benoît Suttor",
                "password": "******",
            },
        )
        res = self.testapp.post_json(
            "/my_app/imio/iasmartweb",
            {
                "content_id": "jbond",
                "username": "******",
                "email": "*****@*****.**",
                "fullname": "James Bond",
                "password": "******",
            },
        )
        res = self.testapp.post_json(
            "/my_app/imio/iaurban",
            {
                "content_id": "bsuttor",
                "username": "******",
                "email": "*****@*****.**",
                "fullname": "",
                "password": "******",
            },
        )
        res = self.testapp.post_json(
            "/my_app/imio/iateleservice",
            {
                "content_id": "suttorb",
                "username": "******",
                "email": "*****@*****.**",
                "fullname": "benoît suttor",
                "password": "******",
            },
        )
        res = self.testapp.get("/my_app/imio/csv", status=200)
        self.assertEqual(res.content_type, "text/csv")
        self.assertTrue("bsuttor" in str(res.body))
        self.assertEqual(len(res.body.decode("utf8").split("\r\n")), 4)

    def test_export_json(self):
        res = self.testapp.post_json("/", {"app_id": "my_app"})
        res = self.testapp.post_json("/my_app", {"container_id": "imio"})
        res = self.testapp.post_json("/my_app/imio", {"container_id": "iasmartweb"})
        res = self.testapp.post_json("/my_app/imio", {"container_id": "iaurban"})
        res = self.testapp.post_json(
            "/my_app/imio/iasmartweb",
            {
                "content_id": "bsuttor",
                "username": "******",
                "email": "*****@*****.**",
                "fullname": "Benoît Suttor",
                "password": "******",
            },
        )
        res = self.testapp.post_json(
            "/my_app/imio/iasmartweb",
            {
                "content_id": "jbond",
                "username": "******",
                "email": "*****@*****.**",
                "fullname": "James Bond",
                "password": "******",
            },
        )
        res = self.testapp.post_json(
            "/my_app/imio/iaurban",
            {
                "content_id": "bsuttor",
                "username": "******",
                "email": "*****@*****.**",
                "fullname": "",
                "password": "******",
            },
        )
        res = self.testapp.get("/my_app/imio/json", status=200)
        self.assertEqual(res.content_type, "application/json")
        self.assertEqual(len(res.json_body["users"]), 3)
Example #11
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 #12
0
class FlaskAdapterTests(unittest.TestCase):

    def setUp(self):
        from tests.example_app.flask_app import create_pale_flask_app
        self.flask_app = create_pale_flask_app()
        self.app = TestApp(self.flask_app)


    def assertExpectedFields(self, returned_dict, expected_fields):
        d = returned_dict.copy() # don't clobber the input

        for f in expected_fields:
            self.assertIn(f, d)
            val = d.pop(f)
            # don't check the val for now

        # make sure there's nothing extraneous left in the dict
        self.assertEqual(len(d.keys()), 0)
        return

    def test_successful_get_with_route_args(self):
        now = datetime.datetime.utcnow()
        resp = self.app.get('/api/arg_test/arg-a/arg-b')
        self.assertEqual(resp.status_code, 200)

        self.assertEqual(resp.json, {
            "arg_a": "arg-a",
            "arg_b": "arg-b",
            })


    def test_successful_get_without_params(self):
        now = datetime.datetime.utcnow()
        resp = self.app.get('/api/time/current')
        self.assertEqual(resp.status_code, 200)
        # Test _after_response_handlers
        self.assertIn("After-Response", resp.headers)
        self.assertEqual(resp.headers["After-Response"], 'OK')
        # Test CORS
        self.assertIn("Access-Control-Allow-Origin", resp.headers)
        self.assertEqual(resp.headers["Access-Control-Allow-Origin"], '*')

        # the 'time' value was set in the endpoint handler
        self.assertIn('time', resp.json_body)

        # the returned time value should match the resource defined
        # in tests.example_app.api.resources.py
        returned_time = resp.json_body['time']

        # the endpoint specifies `fields=DateTimeResource._all_fields()`
        # so, we should expect to find all of them
        expected_fields = DateTimeResource._all_fields()

        self.assertExpectedFields(returned_time, expected_fields)
        self.assertEqual(returned_time['eurodate'],
                now.strftime("%d.%m.%Y"))


    def test_successful_post_with_required_params(self):
        # month is required in the endpoint definition, so we must pass
        # it in here
        resp = self.app.post('/api/time/parse', {'month': 2})

        self.assertEqual(resp.status_code, 200)
        self.assertIn('time', resp.json_body)

        returned_time = resp.json_body['time']

        # we didn't specify any other fields in the endpoint definition,
        # so this one should only get the defaults
        expected_fields = DateTimeResource._default_fields

        self.assertExpectedFields(returned_time, expected_fields)

        self.assertIn('Cache-Control', resp.headers)
        self.assertEqual('max-age=3', resp.headers['Cache-Control'])


    def test_successful_json_post_with_required_params(self):
        # this is the same as the above post, but passes json in the
        # request body, instead of x-www-form-urlencoded
        resp = self.app.post_json('/api/time/parse', {'month': 2})

        self.assertEqual(resp.status_code, 200)
        self.assertIn('time', resp.json_body)

        returned_time = resp.json_body['time']

        # we didn't specify any other fields in the endpoint definition,
        # so this one should only get the defaults
        expected_fields = DateTimeResource._default_fields

        self.assertExpectedFields(returned_time, expected_fields)


    def test_unsuccessful_post_missing_required_params(self):
        resp = self.app.post('/api/time/parse', status=422)

        self.assertIn('error', resp.json_body)


    def test_getting_with_nested_resources(self):
        test_duration = 60 * 1000 # one minute in milliseconds
        resp = self.app.get('/api/time/range', {'duration': test_duration})

        self.assertEqual(resp.status_code, 200)
        self.assertIn('range', resp.json_body)

        returned_range = resp.json_body['range']
        self.assertEqual(returned_range['duration_microseconds'],
                test_duration * 1000)

        # start has default fields
        start = returned_range['start']
        expected_fields = DateTimeResource._default_fields
        self.assertExpectedFields(start, expected_fields)

        # end has all of them
        end = returned_range['end']
        expected_fields = DateTimeResource._all_fields()
        self.assertExpectedFields(end, expected_fields)

    def test_resource(self):

        # Start by resetting the resource.
        # (multiple test runs from the same process will fail otherwise)
        resp = self.app.post('/api/resource/reset')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        self.assertEqual(resp.json, {'key': 'value'})

        # Test creating a new resource
        resp = self.app.put_json('/api/resource', {'key': 'boop'},
            headers={'Content-Type': 'application/json'})
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        self.assertEqual(resp.json, {'key': 'boop'})

        # Test retrieving the resource.
        resp = self.app.get('/api/resource')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        self.assertEqual(resp.json, {'key': 'boop'})

        # Test patching the resource.
        # Without the correct Content-Type, we expect a 415 error.
        self.assertRaises(AppError, self.app.patch_json,
            '/api/resource', {'key': 'value2'})

        resp = self.app.patch_json('/api/resource', {'key': 'value2'},
            headers={'Content-Type': 'application/merge-patch+json'})
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        self.assertEqual(resp.json, {'key': 'value2'})

        # Test get to ensure the resource persists.
        resp = self.app.get('/api/resource')
        self.assertEqual(resp.status_code, 200)
        self.assertEqual(resp.content_type, 'application/json')
        self.assertEqual(resp.json, {'key': 'value2'})

        # A NoContentResource shouldn't have a Content-Type header (no content!)
        resp = self.app.post('/api/blank')
        self.assertEqual(resp.status_code, 204)
        self.assertEqual(resp.content_type, None)
class PyramidPeeweeMarshmallowTestCase(unittest.TestCase):
    def setUp(self):
        from webtest import TestApp  # Imported here in order to avoid dummy warning from pytest

        db.drop_tables([User])
        db.create_tables([User])

        self.app = create_app()
        self.client = TestApp(self.app)

    def test_list_users(self):
        _create_user(first_name='Filipe', last_name='Waitman')

        response = self.client.get('/api/users/')
        assert response.status_code == 200
        assert response.json_body['count'] == 1
        assert response.json_body['next_page'] is None
        assert response.json_body['prev_page'] is None
        assert 'id' in response.json_body['results'][0]
        assert 'created' in response.json_body['results'][0]
        assert response.json_body['results'][0]['first_name'] == 'Filipe'
        assert response.json_body['results'][0]['last_name'] == 'Waitman'

    def test_create(self):
        data = {
            'first_name': 'Filipe',
            'last_name': 'Waitman',
        }

        response = self.client.post_json('/api/users/', data)
        assert response.status_code == 201
        assert response.json_body['first_name'] == 'Filipe'
        assert response.json_body['last_name'] == 'Waitman'
        assert response.json_body.get('id')
        assert response.json_body.get('created')
        assert User.select().filter(id=response.json_body['id']).count() == 1

        instance = User.select().filter(id=response.json_body['id']).get()
        assert instance.first_name == 'Filipe'
        assert instance.last_name == 'Waitman'

    def test_create_errors(self):
        data = {
            'first_name': 'Filipe',
            'last_name': 'Waitman',
        }

        response = self.client.post('/api/users/', data, status=400)
        assert response.status_code == 400
        assert response.json_body == {'first_name': ['Missing data for required field.'], 'last_name': ['Missing data for required field.'], 'status_code': 400}  # noqa

        del data['first_name']
        response = self.client.post_json('/api/users/', data, status=400)
        assert response.status_code == 400
        assert response.json_body == {'first_name': ['Missing data for required field.'], 'status_code': 400}

    def test_retrieve(self):
        user = _create_user(first_name='Filipe', last_name='Waitman')

        response = self.client.get('/api/users/{}/'.format(user.id))
        assert response.status_code == 200
        assert 'id' in response.json_body
        assert 'created' in response.json_body
        assert response.json_body['first_name'] == 'Filipe'
        assert response.json_body['last_name'] == 'Waitman'

    def test_retrieve_errors(self):
        response = self.client.get('/api/users/999/', status=404)
        assert response.status_code == 404
        assert response.json_body == {'status_code': 404}

    def test_update(self):
        user = _create_user(first_name='Filipe', last_name='Waitman')
        data = {
            'last_name': 'New',
        }

        response = self.client.patch_json('/api/users/{}/'.format(user.id), data)
        assert response.status_code == 200
        assert 'id' in response.json_body
        assert 'created' in response.json_body
        assert response.json_body['first_name'] == 'Filipe'
        assert response.json_body['last_name'] == 'New'

        instance = User.select().filter(id=response.json_body['id']).get()
        assert instance.last_name == 'New'

    def test_update_errors(self):
        user = _create_user(first_name='Filipe', last_name='Waitman')
        data = {
            'last_name': '',
        }

        response = self.client.patch_json('/api/users/{}/'.format(user.id), data, status=400)
        assert response.status_code == 400
        assert response.json_body == {'last_name': ['Shorter than minimum length 1.'], 'status_code': 400}

        instance = User.select().filter(id=user.id).get()
        assert instance.last_name == 'Waitman'

    def test_delete(self):
        user = _create_user(first_name='Filipe', last_name='Waitman')

        response = self.client.delete('/api/users/{}/'.format(user.id))
        assert response.status_code == 204
        assert User.select().filter(id=user.id).count() == 0

    def test_doublename(self):
        user = _create_user(first_name='Filipe', last_name='Waitman')

        response = self.client.get('/api/users/{}/doublename/'.format(user.id))
        assert response.status_code == 200
        assert response.json_body == {'doubled': 'FilipeFilipe'}
        assert response.headers['header-passed-in'] == '1'

    def test_pagination(self):
        _create_user(first_name='Filipe', last_name='Waitman')
        _create_user(first_name='John', last_name='Doe')

        response = self.client.get('/api/users/')
        assert response.status_code == 200
        assert response.json_body['count'] == 2
        assert response.json_body['next_page'] is None
        assert response.json_body['prev_page'] is None
        assert len(response.json_body['results']) == 2

        response = self.client.get('/api/users/?per_page=1&x=y')
        assert response.status_code == 200
        assert response.json_body['count'] == 2
        assert response.json_body['next_page'] is not None
        assert response.json_body['prev_page'] is None
        assert len(response.json_body['results']) == 1
        assert 'http' in response.json_body['next_page']
        assert 'localhost' in response.json_body['next_page']
        assert '/api/users/' in response.json_body['next_page']
        assert 'x=y' in response.json_body['next_page']
        assert 'per_page=1' in response.json_body['next_page']
        assert 'page=2' in response.json_body['next_page']

        response = self.client.get('/api/users/?per_page=1&page=2')
        assert response.status_code == 200
        assert response.json_body['count'] == 2
        assert response.json_body['next_page'] is None
        assert response.json_body['prev_page'] is not None
        assert len(response.json_body['results']) == 1

    @mock.patch.object(MyBaseAPI, 'get_current_user', return_value=None)
    def test_permissions(self, *mocks):
        user = _create_user(first_name='Filipe', last_name='Waitman')

        # User API
        response = self.client.get('/api/users/', status=401)
        assert response.status_code == 401

        response = self.client.post('/api/users/', status=401)
        assert response.status_code == 401

        response = self.client.get('/api/users/{}/'.format(user.id), status=401)
        assert response.status_code == 401

        response = self.client.patch('/api/users/{}/'.format(user.id), status=401)
        assert response.status_code == 401

        response = self.client.delete('/api/users/{}/'.format(user.id), status=401)
        assert response.status_code == 401

        response = self.client.get('/api/users/{}/doublename/'.format(user.id), status=401)
        assert response.status_code == 401

        # UserOpen API
        response = self.client.get('/api/users/{}/doublename_open/'.format(user.id))
        assert response.status_code == 200

        # UserReadOnly API
        response = self.client.get('/api/users/read_only/')
        assert response.status_code == 200

        response = self.client.post('/api/users/read_only/', status=403)
        assert response.status_code == 403

    def test_form_data_create(self):
        data = {
            'first_name': 'Filipe',
            'last_name': 'Waitman',
        }

        response = self.client.post_json('/api/users/form_data/', data, status=400)
        assert response.status_code == 400
        assert response.json_body == {'first_name': ['Missing data for required field.'], 'last_name': ['Missing data for required field.'], 'status_code': 400}  # noqa

        response = self.client.post('/api/users/form_data/', data)
        assert response.status_code == 201

    def test_no_pagination_list(self):
        _create_user(first_name='Filipe', last_name='Waitman')

        response = self.client.get('/api/users/no_pagination/')
        assert response.status_code == 200
        assert len(response.json_body) == 1
        assert 'next_page' not in response.json_body
        assert 'prev_page' not in response.json_body
        assert 'id' in response.json_body[0]
        assert 'created' in response.json_body[0]
        assert response.json_body[0]['first_name'] == 'Filipe'
        assert response.json_body[0]['last_name'] == 'Waitman'

    def test_no_pagination_list_via_query_params(self):
        _create_user(first_name='Filipe', last_name='Waitman')

        response = self.client.get('/api/users/?paginate=f')
        assert response.status_code == 200
        assert len(response.json_body) == 1
        assert 'next_page' not in response.json_body
        assert 'prev_page' not in response.json_body
        assert 'id' in response.json_body[0]
        assert 'created' in response.json_body[0]
        assert response.json_body[0]['first_name'] == 'Filipe'
        assert response.json_body[0]['last_name'] == 'Waitman'

    def test_exception_behavior(self):
        response = self.client.get('/api/users/exception/handled/', status=499)
        assert response.status_code == 499
        assert response.json_body == {'detail': 'Now this is a weird HTTP code', 'status_code': 499}

        with pytest.raises(ZeroDivisionError):
            self.client.get('/api/users/exception/unhandled/')

        response = self.client.get('/api/users', status=404)  # Missing trailing slash (out of wrf scope)
        assert response.status_code == 404
Example #14
0
class Client(object):
    def __init__(self):
        self.test_core = TestApp(Core().execute_wsgi())

    def get_api(self, api_url, *auth):
        response = APIResponse()
        __api_url = str(api_url)
        if auth:
            self.test_core.set_authorization(auth)
        test_core_response = self.test_core.get(__api_url)
        response.json = test_core_response.json
        response.status = test_core_response.status
        response.status_code = test_core_response.status_code
        response.body = test_core_response.body
        response.content_type = test_core_response.content_type

        return response

    def post_api(self, api_url, data, *auth):
        response = APIResponse()
        __api_url = str(api_url)
        if auth:
            self.test_core.set_authorization(auth)
        test_core_response = self.test_core.post_json(__api_url, params=data)
        response.json = json.dumps(test_core_response.json)
        response.status = test_core_response.status
        response.status_code = test_core_response.status_code
        response.body = test_core_response.body
        return response

    def patch_api(self, api_url, data, *auth):
        response = APIResponse()
        __api_url = str(api_url)
        if auth:
            self.test_core.set_authorization(auth)
        test_core_response = self.test_core.patch_json(__api_url, params=data)
        response.json = json.dumps(test_core_response.json)
        response.status = test_core_response.status
        response.status_code = test_core_response.status_code
        response.body = test_core_response.body
        return response

    def put_api(self, api_url, data, *auth):
        response = APIResponse()
        __api_url = str(api_url)
        if auth:
            self.test_core.set_authorization(auth)
        test_core_response = self.test_core.put_json(__api_url, params=data)
        response.json = json.dumps(test_core_response.json)
        response.status = test_core_response.status
        response.status_code = test_core_response.status_code
        response.body = test_core_response.body
        return response

    def delete_api(self, api_url, data, *auth):
        response = APIResponse()
        __api_url = str(api_url)
        if auth:
            self.test_core.set_authorization(auth)
        test_core_response = self.test_core.delete_json(__api_url, params=data)
        response.json = json.dumps(test_core_response.json)
        response.status = test_core_response.status
        response.status_code = test_core_response.status_code
        response.body = test_core_response.body
        return response