From 3e43539e23b874fa884d890b79a708e41b86cd5b Mon Sep 17 00:00:00 2001 From: Matt Rider Date: Mon, 29 Jul 2019 17:08:07 +0200 Subject: [PATCH] Set up some backend tests for emotions --- .../src/middleware/permissionsMiddleware.js | 1 + backend/src/schema/resolvers/posts.js | 34 ++++ backend/src/schema/resolvers/posts.spec.js | 151 ++++++++++++++---- backend/src/schema/types/type/Post.gql | 5 +- backend/yarn.lock | 104 ------------ 5 files changed, 154 insertions(+), 141 deletions(-) diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index fe33cd4c4..fe001d676 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -184,6 +184,7 @@ const permissions = shield( requestPasswordReset: allow, resetPassword: allow, AddPostEmotions: isAuthenticated, + RemovePostEmotions: isAuthenticated, }, User: { email: isMyOwn, diff --git a/backend/src/schema/resolvers/posts.js b/backend/src/schema/resolvers/posts.js index 74f88d62e..c4ed494b7 100644 --- a/backend/src/schema/resolvers/posts.js +++ b/backend/src/schema/resolvers/posts.js @@ -71,5 +71,39 @@ export default { return post.properties }, + AddPostEmotions: async (object, params, context, resolveInfo) => { + const session = context.driver.session() + const { from, to, data } = params + const transactionRes = await session.run( + `MATCH (userFrom:User {id: $from.id}), (postTo:Post {id: $to.id}) + MERGE (userFrom)-[emotedRelation:EMOTED {emotion: $data.emotion}]->(postTo) + RETURN userFrom, postTo, emotedRelation`, + { from, to, data }, + ) + session.close() + const [emoted] = transactionRes.records.map(record => { + return { + from: { id: record.get('userFrom').properties.id }, + to: { id: record.get('postTo').properties.id }, + emotion: record.get('emotedRelation').properties.emotion, + } + }) + return emoted + }, + RemovePostEmotions: async (object, params, context, resolveInfo) => { + const session = context.driver.session() + const { from, to, data } = params + const transactionRes = await session.run( + `MATCH (userFrom:User {id: $from.id})-[emotedRelation:EMOTED {emotion: $data.emotion}]->(postTo:Post {id: $to.id}) + DELETE emotedRelation + RETURN emotedRelation`, + { from, to, data }, + ) + session.close() + const [removed] = transactionRes.records.map(record => { + return record.get('emotedRelation') + }) + return !!removed + }, }, } diff --git a/backend/src/schema/resolvers/posts.spec.js b/backend/src/schema/resolvers/posts.spec.js index 49cb2123b..e1c85974b 100644 --- a/backend/src/schema/resolvers/posts.spec.js +++ b/backend/src/schema/resolvers/posts.spec.js @@ -58,6 +58,7 @@ const postQueryFilteredByCategoryVariables = { } beforeEach(async () => { userParams = { + id: 'u198', name: 'TestUser', email: 'test@example.org', password: '1234', @@ -384,43 +385,59 @@ describe('DeletePost', () => { }) }) -describe('AddPostEmotions', () => { +describe('emotions', () => { let addPostEmotionsVariables - const addPostEmotionsMutation = ` - mutation($from: _UserInput!, $to: _PostInput!, $data: _EMOTEDInput!) { - AddPostEmotions(from: $from, to: $to, data: $data) { - from { - id - } - to { - id - } - emotion - } + let postEmotionsVariables + beforeEach(async () => { + const asAuthor = Factory() + authorParams = { + id: 'u25', + email: 'wanna-add-emotions@example.org', + password: '1234', } - ` - describe('emotions', () => { - beforeEach(async () => { - const asAuthor = Factory() - authorParams = { - id: 'u25', - email: 'wanna-add-emotions@example.org', - password: '1234', - } - await asAuthor.create('User', authorParams) - await asAuthor.authenticateAs(authorParams) - await asAuthor.create('Post', { - id: 'p1376', - title: postTitle, - content: postContent, - }) - addPostEmotionsVariables = { - from: { id: authorParams.id }, - to: { id: 'p1376' }, - data: { emotion: 'happy' }, - } + await asAuthor.create('User', authorParams) + await asAuthor.authenticateAs(authorParams) + await asAuthor.create('Post', { + id: 'p1376', + title: postTitle, + content: postContent, }) - // it('supports setting emotions for a post', () => {}) + addPostEmotionsVariables = { + from: { id: authorParams.id }, + to: { id: 'p1376' }, + data: { emotion: 'happy' }, + } + postEmotionsVariables = { + id: 'p1376', + } + }) + + describe('AddPostEmotions', () => { + const addPostEmotionsMutation = ` + mutation($from: _UserInput!, $to: _PostInput!, $data: _EMOTEDInput!) { + AddPostEmotions(from: $from, to: $to, data: $data) { + from { id } + to { id } + emotion + } + } + ` + const postEmotionsCountQuery = ` + query($id: ID!) { + Post(id: $id) { + emotionsCount + } + } + ` + const postEmotionsQuery = ` + query($id: ID!) { + Post(id: $id) { + emotions { + emotion + } + } + } + ` describe('unauthenticated', () => { it('throws authorization error', async () => { @@ -435,6 +452,52 @@ describe('AddPostEmotions', () => { }) }) + describe('authenticated and not the author', () => { + let headers + beforeEach(async () => { + headers = await login(userParams) + client = new GraphQLClient(host, { headers }) + }) + + it('adds an emotion to the post', async () => { + addPostEmotionsVariables.from.id = userParams.id + const expected = { + AddPostEmotions: { + from: addPostEmotionsVariables.from, + to: addPostEmotionsVariables.to, + emotion: 'happy', + }, + } + await expect( + client.request(addPostEmotionsMutation, addPostEmotionsVariables), + ).resolves.toEqual(expected) + }) + it('limits the addition of the same emotion to 1', async () => { + const expected = { + Post: [ + { + emotionsCount: 1, + }, + ], + } + await client.request(addPostEmotionsMutation, addPostEmotionsVariables) + await client.request(addPostEmotionsMutation, addPostEmotionsVariables) + await expect( + client.request(postEmotionsCountQuery, postEmotionsVariables), + ).resolves.toEqual(expected) + }) + + it('allows a user to add more than one emotion', async () => { + const expected = [{ emotion: 'happy' }, { emotion: 'surprised' }] + await client.request(addPostEmotionsMutation, addPostEmotionsVariables) + addPostEmotionsVariables.data.emotion = 'surprised' + await client.request(addPostEmotionsMutation, addPostEmotionsVariables) + await expect(client.request(postEmotionsQuery, postEmotionsVariables)).resolves.toEqual({ + Post: [{ emotions: expect.arrayContaining(expected) }], + }) + }) + }) + describe('authenticated as author', () => { let headers beforeEach(async () => { @@ -456,4 +519,24 @@ describe('AddPostEmotions', () => { }) }) }) + + describe('RemovePostEmotions', () => { + const removePostEmotionsMutation = ` + mutation($from: _UserInput!, $to: _PostInput!, $data: _EMOTEDInput!) { + RemovePostEmotions(from: $from, to: $to, data: $data) + } + ` + describe('unauthenticated', () => { + it('throws authorization error', async () => { + client = new GraphQLClient(host) + await expect( + client.request(removePostEmotionsMutation, { + from: { id: 'u25' }, + to: { id: 'p1376' }, + data: { emotion: 'happy' }, + }), + ).rejects.toThrow('Not Authorised') + }) + }) + }) }) diff --git a/backend/src/schema/types/type/Post.gql b/backend/src/schema/types/type/Post.gql index 30cdd71d4..422ab2867 100644 --- a/backend/src/schema/types/type/Post.gql +++ b/backend/src/schema/types/type/Post.gql @@ -51,9 +51,7 @@ type Post { emotions: [EMOTED] emotionsCount: Int! - @cypher( - statement: "MATCH (this)<-[emoted:EMOTED]-(:User) RETURN COUNT(DISTINCT emoted)" - ) + @cypher(statement: "MATCH (this)<-[emoted:EMOTED]-(:User) RETURN COUNT(DISTINCT emoted)") } type Mutation { @@ -93,4 +91,5 @@ type Mutation { language: String categoryIds: [ID] ): Post + RemovePostEmotions(from: _UserInput!, to: _PostInput!, data: _EMOTEDInput!): Boolean! } diff --git a/backend/yarn.lock b/backend/yarn.lock index beeaa91e1..e799968b3 100644 --- a/backend/yarn.lock +++ b/backend/yarn.lock @@ -2,13 +2,6 @@ # yarn lockfile v1 -"@apollographql/apollo-tools@^0.3.6": - version "0.3.7" - resolved "https://registry.yarnpkg.com/@apollographql/apollo-tools/-/apollo-tools-0.3.7.tgz#3bc9c35b9fff65febd4ddc0c1fc04677693a3d40" - integrity sha512-+ertvzAwzkYmuUtT8zH3Zi6jPdyxZwOgnYaZHY7iLnMVJDhQKWlkyjLMF8wyzlPiEdDImVUMm5lOIBZo7LkGlg== - dependencies: - apollo-env "0.5.1" - "@apollographql/apollo-tools@^0.4.0": version "0.4.0" resolved "https://registry.yarnpkg.com/@apollographql/apollo-tools/-/apollo-tools-0.4.0.tgz#8a1a0ab7a0bb12ccc03b72e4a104cfa5d969fd5f" @@ -1454,14 +1447,6 @@ anymatch@^2.0.0: micromatch "^3.1.4" normalize-path "^2.1.1" -apollo-cache-control@0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.8.0.tgz#08b157e5f8cd86f63608b05d45222de0725ebd5a" - integrity sha512-BBnfUmSWRws5dRSDD+R56RLJCE9v6xQuob+i/1Ju9EX4LZszU5JKVmxEvnkJ1bk/BkihjoQXTnP6fJCnt6fCmA== - dependencies: - apollo-server-env "2.4.0" - graphql-extensions "0.8.0" - apollo-cache-control@0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/apollo-cache-control/-/apollo-cache-control-0.8.1.tgz#707c0b958c02c5b47ddf49a02f60ea88a64783fb" @@ -1503,14 +1488,6 @@ apollo-client@~2.6.3: tslib "^1.9.3" zen-observable "^0.8.0" -apollo-datasource@0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/apollo-datasource/-/apollo-datasource-0.6.0.tgz#823d6be8a3804613b5c56d2972c07db662293fc6" - integrity sha512-DOzzYWEOReYRu2vWPKEulqlTb9Xjg67sjVCzve5MXa7GUXjfr8IKioljvfoBMlqm/PpbJVk2ci4n5NIFqoYsrQ== - dependencies: - apollo-server-caching "0.5.0" - apollo-server-env "2.4.0" - apollo-datasource@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/apollo-datasource/-/apollo-datasource-0.6.1.tgz#697870f564da90bee53fa30d07875cb46c4d6b06" @@ -1526,18 +1503,6 @@ apollo-engine-reporting-protobuf@0.4.0: dependencies: protobufjs "^6.8.6" -apollo-engine-reporting@1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.4.0.tgz#3a9bd011b271593e16d7057044898d0a817b197d" - integrity sha512-NMiO3h1cuEBt6QZNGHxivwuyZQnoU/2MMx0gUA8Gyy1ERBhK6P235qoMnvoi34rLmqJuyGPX6tXcab8MpMIzYQ== - dependencies: - apollo-engine-reporting-protobuf "0.4.0" - apollo-graphql "^0.3.3" - apollo-server-env "2.4.0" - apollo-server-types "0.2.0" - async-retry "^1.2.1" - graphql-extensions "0.8.0" - apollo-engine-reporting@1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/apollo-engine-reporting/-/apollo-engine-reporting-1.4.2.tgz#f6c1e964c3c2c09bdb25c449f6b7ab05952ff459" @@ -1618,34 +1583,6 @@ apollo-server-caching@0.5.0: dependencies: lru-cache "^5.0.0" -apollo-server-core@2.7.0: - version "2.7.0" - resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.7.0.tgz#c444347dea11149b5b453890506e43dc7e711257" - integrity sha512-CXjXAkgcMBCJZpsZgfAY5W7f5thdxUhn75UgzeH28RTUZ2aKi/LjoCixPWRSF1lU4vuEWneAnM8Vg/KCD+29lQ== - dependencies: - "@apollographql/apollo-tools" "^0.3.6" - "@apollographql/graphql-playground-html" "1.6.24" - "@types/ws" "^6.0.0" - apollo-cache-control "0.8.0" - apollo-datasource "0.6.0" - apollo-engine-reporting "1.4.0" - apollo-engine-reporting-protobuf "0.4.0" - apollo-server-caching "0.5.0" - apollo-server-env "2.4.0" - apollo-server-errors "2.3.1" - apollo-server-plugin-base "0.6.0" - apollo-server-types "0.2.0" - apollo-tracing "0.8.0" - fast-json-stable-stringify "^2.0.0" - graphql-extensions "0.8.0" - graphql-subscriptions "^1.0.0" - graphql-tag "^2.9.2" - graphql-tools "^4.0.0" - graphql-upload "^8.0.2" - sha.js "^2.4.11" - subscriptions-transport-ws "^0.9.11" - ws "^6.0.0" - apollo-server-core@2.7.2: version "2.7.2" resolved "https://registry.yarnpkg.com/apollo-server-core/-/apollo-server-core-2.7.2.tgz#4acd9f4d0d235bef0e596e2a821326dfc07ae7b2" @@ -1672,14 +1609,6 @@ apollo-server-core@2.7.2: subscriptions-transport-ws "^0.9.11" ws "^6.0.0" -apollo-server-env@2.4.0: - version "2.4.0" - resolved "https://registry.yarnpkg.com/apollo-server-env/-/apollo-server-env-2.4.0.tgz#6611556c6b627a1636eed31317d4f7ea30705872" - integrity sha512-7ispR68lv92viFeu5zsRUVGP+oxsVI3WeeBNniM22Cx619maBUwcYTIC3+Y3LpXILhLZCzA1FASZwusgSlyN9w== - dependencies: - node-fetch "^2.1.2" - util.promisify "^1.0.0" - apollo-server-env@2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/apollo-server-env/-/apollo-server-env-2.4.1.tgz#58264ecfeb151919e0f480320b4e3769be9f18f3" @@ -1713,13 +1642,6 @@ apollo-server-express@2.7.2, apollo-server-express@^2.7.2: subscriptions-transport-ws "^0.9.16" type-is "^1.6.16" -apollo-server-plugin-base@0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.6.0.tgz#4186296ea5d52cfe613961d252a8a2f9e13e6ba6" - integrity sha512-BjfyWpHyKwHOe819gk3wEFwbnVp9Xvos03lkkYTTcXS/8G7xO78aUcE65mmyAC56/ZQ0aodNFkFrhwNtWBQWUQ== - dependencies: - apollo-server-types "0.2.0" - apollo-server-plugin-base@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/apollo-server-plugin-base/-/apollo-server-plugin-base-0.6.1.tgz#b9c209aa2102a26c6134f51bfa1e4a8307b63b11" @@ -1734,15 +1656,6 @@ apollo-server-testing@~2.7.2: dependencies: apollo-server-core "2.7.2" -apollo-server-types@0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/apollo-server-types/-/apollo-server-types-0.2.0.tgz#270d7298f709fd8237ebfa48753249e5286df5f2" - integrity sha512-5dgiyXsM90vnfmdXO1ixHvsLn0d9NP4tWufmr3ZmjKv00r4JAQNUaUdgOSGbRIKoHELQGwxUuTySTZ/tYfGaNQ== - dependencies: - apollo-engine-reporting-protobuf "0.4.0" - apollo-server-caching "0.5.0" - apollo-server-env "2.4.0" - apollo-server-types@0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/apollo-server-types/-/apollo-server-types-0.2.1.tgz#553da40ea1ad779ef0390c250ddad7eb782fdf64" @@ -1763,14 +1676,6 @@ apollo-server@~2.7.2: graphql-subscriptions "^1.0.0" graphql-tools "^4.0.0" -apollo-tracing@0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.8.0.tgz#28cd9c61a4db12b2c24dad67fdedd309806c1650" - integrity sha512-cNOtOlyZ56iJRsCjnxjM1V0SnQ2ZZttuyoeOejdat6llPfk5bfYTVOKMjdbSfDvU33LS9g9sqNJCT0MwrEPFKQ== - dependencies: - apollo-server-env "2.4.0" - graphql-extensions "0.8.0" - apollo-tracing@0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/apollo-tracing/-/apollo-tracing-0.8.1.tgz#220aeac6ad598c67f9333739155b7a56bd63ccab" @@ -4071,15 +3976,6 @@ graphql-custom-directives@~0.2.14: moment "^2.22.2" numeral "^2.0.6" -graphql-extensions@0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.8.0.tgz#b3fe7915aa84eef5a39135840840cc4d2e700c46" - integrity sha512-zV9RefkusIXqi9ZJtl7IJ5ecjDKdb7PLAb5E3CmxX3OK1GwNCIubp0vE7Fp4fXlCUKgTB1Woubs0zj71JT8o0A== - dependencies: - "@apollographql/apollo-tools" "^0.3.6" - apollo-server-env "2.4.0" - apollo-server-types "0.2.0" - graphql-extensions@0.8.1: version "0.8.1" resolved "https://registry.yarnpkg.com/graphql-extensions/-/graphql-extensions-0.8.1.tgz#f5f1fed5fe49620c4e70c5d08bdbd0039e91c402"