From 3d8fe4d85039d22a3a80c5f0f6f7b5065840ff7c Mon Sep 17 00:00:00 2001 From: ogerly Date: Thu, 13 Feb 2020 13:40:20 +0100 Subject: [PATCH 1/7] feature: Delete_user_as_admin_through_API_only --- backend/src/middleware/permissionsMiddleware.js | 2 +- backend/src/schema/resolvers/users.js | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/backend/src/middleware/permissionsMiddleware.js b/backend/src/middleware/permissionsMiddleware.js index 10dc98845..fa88d9348 100644 --- a/backend/src/middleware/permissionsMiddleware.js +++ b/backend/src/middleware/permissionsMiddleware.js @@ -133,7 +133,7 @@ export default shield( CreateComment: isAuthenticated, UpdateComment: isAuthor, DeleteComment: isAuthor, - DeleteUser: isDeletingOwnAccount, + DeleteUser: or(isDeletingOwnAccount, isAdmin), requestPasswordReset: allow, resetPassword: allow, AddPostEmotions: isAuthenticated, diff --git a/backend/src/schema/resolvers/users.js b/backend/src/schema/resolvers/users.js index a1b68e20d..c29b021be 100644 --- a/backend/src/schema/resolvers/users.js +++ b/backend/src/schema/resolvers/users.js @@ -175,6 +175,7 @@ export default { DeleteUser: async (object, params, context, resolveInfo) => { const { resource } = params const session = context.driver.session() + const { id: userId } = params try { if (resource && resource.length) { await session.writeTransaction(transaction => { @@ -190,7 +191,7 @@ export default { RETURN author `, { - userId: context.user.id, + userId, }, ) }) @@ -212,7 +213,7 @@ export default { DETACH DELETE socialMedia RETURN user `, - { userId: context.user.id }, + { userId }, ) log(deleteUserTransactionResponse) return deleteUserTransactionResponse.records.map(record => record.get('user').properties) From 84c154798efac0cec4c13dfefae18a6a9542058a Mon Sep 17 00:00:00 2001 From: ogerly Date: Thu, 13 Feb 2020 14:30:54 +0100 Subject: [PATCH 2/7] feature: test delete user as admin --- backend/src/schema/resolvers/users.spec.js | 327 +++++++++++++++++++++ 1 file changed, 327 insertions(+) diff --git a/backend/src/schema/resolvers/users.spec.js b/backend/src/schema/resolvers/users.spec.js index 9e24b8082..34b2db0ba 100644 --- a/backend/src/schema/resolvers/users.spec.js +++ b/backend/src/schema/resolvers/users.spec.js @@ -568,3 +568,330 @@ describe('DeleteUser', () => { }) }) }) + + + +describe('DeleteUser as Admin', () => { + const deleteUserMutation = gql` + mutation($id: ID!, $resource: [Deletable]) { + DeleteUser(id: $id, resource: $resource) { + id + name + about + deleted + contributions { + id + content + contentExcerpt + deleted + comments { + id + content + contentExcerpt + deleted + } + } + comments { + id + content + contentExcerpt + deleted + } + } + } + ` + beforeEach(async () => { + variables = { id: ' u343', resource: [] } + + user = await Factory.build('user', { + name: 'My name should be deleted', + about: 'along with my about', + id: 'u343', + }) + await Factory.build( + 'user', + { + id: 'not-my-account', + }, + { + email: 'friends-account@example.org', + }, + ) + }) + + describe('unauthenticated', () => { + it('throws authorization error', async () => { + const { errors } = await mutate({ mutation: deleteUserMutation, variables }) + expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + }) + }) + + describe('authenticated as Admin', () => { + beforeEach(async () => { + const admin = await Factory.build( + 'user', + { + role: 'admin', + }, + { + email: 'admin@example.org', + password: '1234', + }, + ) + authenticatedUser = await admin.toJson() + }) + + + describe('attempting to delete a foreign account by an Admin', () => { + beforeEach(() => { + variables = { ...variables, id: 'u343' } + }) + + describe('given posts and comments', () => { + beforeEach(async () => { + await Factory.build('category', { + id: 'cat9', + name: 'Democracy & Politics', + icon: 'university', + }) + await Factory.build( + 'post', + { + id: 'p139', + content: 'Post by user u343', + }, + { + author: user, + categoryIds, + }, + ) + await Factory.build( + 'comment', + { + id: 'c155', + content: 'Comment by user u343', + }, + { + author: user, + }, + ) + await Factory.build( + 'comment', + { + id: 'c156', + content: "A comment by someone else on user u343's post", + }, + { + postId: 'p139', + }, + ) + }) + + it("deletes my account, but doesn't delete posts or comments by default", async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'Post by user u343', + contentExcerpt: 'Post by user u343', + deleted: false, + comments: [ + { + id: 'c156', + content: "A comment by someone else on user u343's post", + contentExcerpt: "A comment by someone else on user u343's post", + deleted: false, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'Comment by user u343', + contentExcerpt: 'Comment by user u343', + deleted: false, + }, + ], + }, + }, + errors: undefined, + } + await expect(mutate({ mutation: deleteUserMutation, variables })).resolves.toMatchObject( + expectedResponse, + ) + }) + + describe('deletion of all post requested', () => { + beforeEach(() => { + variables = { ...variables, resource: ['Post'] } + }) + + describe("marks user's posts as deleted", () => { + it('posts on request', async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + comments: [ + { + id: 'c156', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'Comment by user u343', + contentExcerpt: 'Comment by user u343', + deleted: false, + }, + ], + }, + }, + errors: undefined, + } + await expect( + mutate({ mutation: deleteUserMutation, variables }), + ).resolves.toMatchObject(expectedResponse) + }) + }) + }) + + describe('deletion of all comments requested', () => { + beforeEach(() => { + variables = { ...variables, resource: ['Comment'] } + }) + + it('marks comments as deleted', async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'Post by user u343', + contentExcerpt: 'Post by user u343', + deleted: false, + comments: [ + { + id: 'c156', + content: "A comment by someone else on user u343's post", + contentExcerpt: "A comment by someone else on user u343's post", + deleted: false, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + }, + errors: undefined, + } + await expect( + mutate({ mutation: deleteUserMutation, variables }), + ).resolves.toMatchObject(expectedResponse) + }) + }) + + describe('deletion of all post and comments requested', () => { + beforeEach(() => { + variables = { ...variables, resource: ['Post', 'Comment'] } + }) + + it('marks posts and comments as deleted', async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + comments: [ + { + id: 'c156', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + }, + errors: undefined, + } + await expect( + mutate({ mutation: deleteUserMutation, variables }), + ).resolves.toMatchObject(expectedResponse) + }) + }) + }) + + describe('connected `EmailAddress` nodes', () => { + it('will be removed completely', async () => { + await expect(neode.all('EmailAddress')).resolves.toHaveLength(3) + await mutate({ mutation: deleteUserMutation, variables }) + await expect(neode.all('EmailAddress')).resolves.toHaveLength(2) + }) + }) + + describe('connected `SocialMedia` nodes', () => { + beforeEach(async () => { + const socialMedia = await Factory.build('socialMedia') + await socialMedia.relateTo(user, 'ownedBy') + }) + + it('will be removed completely', async () => { + await expect(neode.all('SocialMedia')).resolves.toHaveLength(1) + await mutate({ mutation: deleteUserMutation, variables }) + await expect(neode.all('SocialMedia')).resolves.toHaveLength(0) + }) + }) + }) + }) +}) From 5f8d58f46a3fc652d9b1dd2a1f15f5c37d33f7b5 Mon Sep 17 00:00:00 2001 From: ogerly Date: Fri, 14 Feb 2020 10:21:13 +0100 Subject: [PATCH 3/7] test deleteuser as admin, moderator, another user and as I myself --- backend/src/schema/resolvers/users.spec.js | 164 ++++++++++++++++++--- 1 file changed, 145 insertions(+), 19 deletions(-) diff --git a/backend/src/schema/resolvers/users.spec.js b/backend/src/schema/resolvers/users.spec.js index 34b2db0ba..1a2093f49 100644 --- a/backend/src/schema/resolvers/users.spec.js +++ b/backend/src/schema/resolvers/users.spec.js @@ -571,6 +571,144 @@ describe('DeleteUser', () => { +describe('DeleteUser as another user', () => { + const deleteUserMutation = gql` + mutation($id: ID!, $resource: [Deletable]) { + DeleteUser(id: $id, resource: $resource) { + id + name + about + deleted + contributions { + id + content + contentExcerpt + deleted + comments { + id + content + contentExcerpt + deleted + } + } + comments { + id + content + contentExcerpt + deleted + } + } + } +` +beforeEach(async () => { + variables = { id: ' u343', resource: [] } + + user = await Factory.build('user', { + name: 'My name should be deleted', + about: 'along with my about', + id: 'u343', + }) + +}) + + beforeEach(async () => { + + + const anotherUser = await Factory.build( + 'user', + { + role: 'user', + }, + { + email: 'user@example.org', + password: '1234', + }, + ) + + authenticatedUser = await anotherUser.toJson() + }) + + it('a user has no authorization to delete another user accounts', async () => { + const { errors } = await mutate({ mutation: deleteUserMutation, variables }) + expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + }) +}) + + + + +describe('DeleteUser as moderator', () => { + const deleteUserMutation = gql` + mutation($id: ID!, $resource: [Deletable]) { + DeleteUser(id: $id, resource: $resource) { + id + name + about + deleted + contributions { + id + content + contentExcerpt + deleted + comments { + id + content + contentExcerpt + deleted + } + } + comments { + id + content + contentExcerpt + deleted + } + } + } +` +beforeEach(async () => { + variables = { id: ' u343', resource: [] } + + user = await Factory.build('user', { + name: 'My name should be deleted', + about: 'along with my about', + id: 'u343', + }) + +}) + +beforeEach(async () => { + + + const moderator = await Factory.build( + 'user', + { + role: 'moderator', + }, + { + email: 'moderator@example.org', + password: '1234', + }, + ) + + + authenticatedUser = await moderator.toJson() +}) + +it('moderator is not allowed to delete other user accounts', async () => { + const { errors } = await mutate({ mutation: deleteUserMutation, variables }) + expect(errors[0]).toHaveProperty('message', 'Not Authorised!') +}) + + + +}) + + + + + + describe('DeleteUser as Admin', () => { const deleteUserMutation = gql` mutation($id: ID!, $resource: [Deletable]) { @@ -608,23 +746,11 @@ describe('DeleteUser as Admin', () => { about: 'along with my about', id: 'u343', }) - await Factory.build( - 'user', - { - id: 'not-my-account', - }, - { - email: 'friends-account@example.org', - }, - ) + }) - describe('unauthenticated', () => { - it('throws authorization error', async () => { - const { errors } = await mutate({ mutation: deleteUserMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') - }) - }) + + describe('authenticated as Admin', () => { beforeEach(async () => { @@ -642,7 +768,7 @@ describe('DeleteUser as Admin', () => { }) - describe('attempting to delete a foreign account by an Admin', () => { + describe('deleting a user account', () => { beforeEach(() => { variables = { ...variables, id: 'u343' } }) @@ -687,7 +813,7 @@ describe('DeleteUser as Admin', () => { ) }) - it("deletes my account, but doesn't delete posts or comments by default", async () => { + it("deletes account, but doesn't delete posts or comments by default", async () => { const expectedResponse = { data: { DeleteUser: { @@ -874,9 +1000,9 @@ describe('DeleteUser as Admin', () => { describe('connected `EmailAddress` nodes', () => { it('will be removed completely', async () => { - await expect(neode.all('EmailAddress')).resolves.toHaveLength(3) - await mutate({ mutation: deleteUserMutation, variables }) await expect(neode.all('EmailAddress')).resolves.toHaveLength(2) + await mutate({ mutation: deleteUserMutation, variables }) + await expect(neode.all('EmailAddress')).resolves.toHaveLength(1) }) }) From 3983612c56ac92473a192a318959e4c691a3e7b8 Mon Sep 17 00:00:00 2001 From: ogerly Date: Fri, 14 Feb 2020 10:25:05 +0100 Subject: [PATCH 4/7] test deleteuser as admin, moderator, another user and as I myself, fix lint --- backend/src/schema/resolvers/users.spec.js | 883 ++++++++++----------- 1 file changed, 429 insertions(+), 454 deletions(-) diff --git a/backend/src/schema/resolvers/users.spec.js b/backend/src/schema/resolvers/users.spec.js index 1a2093f49..ebcd2e3cb 100644 --- a/backend/src/schema/resolvers/users.spec.js +++ b/backend/src/schema/resolvers/users.spec.js @@ -244,7 +244,435 @@ describe('UpdateUser', () => { }) }) -describe('DeleteUser', () => { +describe('DeleteUser as another user', () => { + const deleteUserMutation = gql` + mutation($id: ID!, $resource: [Deletable]) { + DeleteUser(id: $id, resource: $resource) { + id + name + about + deleted + contributions { + id + content + contentExcerpt + deleted + comments { + id + content + contentExcerpt + deleted + } + } + comments { + id + content + contentExcerpt + deleted + } + } + } + ` + beforeEach(async () => { + variables = { id: ' u343', resource: [] } + + user = await Factory.build('user', { + name: 'My name should be deleted', + about: 'along with my about', + id: 'u343', + }) + }) + + beforeEach(async () => { + const anotherUser = await Factory.build( + 'user', + { + role: 'user', + }, + { + email: 'user@example.org', + password: '1234', + }, + ) + + authenticatedUser = await anotherUser.toJson() + }) + + it('a user has no authorization to delete another user accounts', async () => { + const { errors } = await mutate({ mutation: deleteUserMutation, variables }) + expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + }) +}) + +describe('DeleteUser as moderator', () => { + const deleteUserMutation = gql` + mutation($id: ID!, $resource: [Deletable]) { + DeleteUser(id: $id, resource: $resource) { + id + name + about + deleted + contributions { + id + content + contentExcerpt + deleted + comments { + id + content + contentExcerpt + deleted + } + } + comments { + id + content + contentExcerpt + deleted + } + } + } + ` + beforeEach(async () => { + variables = { id: ' u343', resource: [] } + + user = await Factory.build('user', { + name: 'My name should be deleted', + about: 'along with my about', + id: 'u343', + }) + }) + + beforeEach(async () => { + const moderator = await Factory.build( + 'user', + { + role: 'moderator', + }, + { + email: 'moderator@example.org', + password: '1234', + }, + ) + + authenticatedUser = await moderator.toJson() + }) + + it('moderator is not allowed to delete other user accounts', async () => { + const { errors } = await mutate({ mutation: deleteUserMutation, variables }) + expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + }) +}) + +describe('DeleteUser as Admin', () => { + const deleteUserMutation = gql` + mutation($id: ID!, $resource: [Deletable]) { + DeleteUser(id: $id, resource: $resource) { + id + name + about + deleted + contributions { + id + content + contentExcerpt + deleted + comments { + id + content + contentExcerpt + deleted + } + } + comments { + id + content + contentExcerpt + deleted + } + } + } + ` + beforeEach(async () => { + variables = { id: ' u343', resource: [] } + + user = await Factory.build('user', { + name: 'My name should be deleted', + about: 'along with my about', + id: 'u343', + }) + }) + + describe('authenticated as Admin', () => { + beforeEach(async () => { + const admin = await Factory.build( + 'user', + { + role: 'admin', + }, + { + email: 'admin@example.org', + password: '1234', + }, + ) + authenticatedUser = await admin.toJson() + }) + + describe('deleting a user account', () => { + beforeEach(() => { + variables = { ...variables, id: 'u343' } + }) + + describe('given posts and comments', () => { + beforeEach(async () => { + await Factory.build('category', { + id: 'cat9', + name: 'Democracy & Politics', + icon: 'university', + }) + await Factory.build( + 'post', + { + id: 'p139', + content: 'Post by user u343', + }, + { + author: user, + categoryIds, + }, + ) + await Factory.build( + 'comment', + { + id: 'c155', + content: 'Comment by user u343', + }, + { + author: user, + }, + ) + await Factory.build( + 'comment', + { + id: 'c156', + content: "A comment by someone else on user u343's post", + }, + { + postId: 'p139', + }, + ) + }) + + it("deletes account, but doesn't delete posts or comments by default", async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'Post by user u343', + contentExcerpt: 'Post by user u343', + deleted: false, + comments: [ + { + id: 'c156', + content: "A comment by someone else on user u343's post", + contentExcerpt: "A comment by someone else on user u343's post", + deleted: false, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'Comment by user u343', + contentExcerpt: 'Comment by user u343', + deleted: false, + }, + ], + }, + }, + errors: undefined, + } + await expect(mutate({ mutation: deleteUserMutation, variables })).resolves.toMatchObject( + expectedResponse, + ) + }) + + describe('deletion of all post requested', () => { + beforeEach(() => { + variables = { ...variables, resource: ['Post'] } + }) + + describe("marks user's posts as deleted", () => { + it('on request', async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + comments: [ + { + id: 'c156', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'Comment by user u343', + contentExcerpt: 'Comment by user u343', + deleted: false, + }, + ], + }, + }, + errors: undefined, + } + await expect( + mutate({ mutation: deleteUserMutation, variables }), + ).resolves.toMatchObject(expectedResponse) + }) + }) + }) + + describe('deletion of all comments requested', () => { + beforeEach(() => { + variables = { ...variables, resource: ['Comment'] } + }) + + it('marks comments as deleted', async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'Post by user u343', + contentExcerpt: 'Post by user u343', + deleted: false, + comments: [ + { + id: 'c156', + content: "A comment by someone else on user u343's post", + contentExcerpt: "A comment by someone else on user u343's post", + deleted: false, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + }, + errors: undefined, + } + await expect( + mutate({ mutation: deleteUserMutation, variables }), + ).resolves.toMatchObject(expectedResponse) + }) + }) + + describe('deletion of all posts and comments requested', () => { + beforeEach(() => { + variables = { ...variables, resource: ['Post', 'Comment'] } + }) + + it('marks posts and comments as deleted', async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + comments: [ + { + id: 'c156', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + }, + errors: undefined, + } + await expect( + mutate({ mutation: deleteUserMutation, variables }), + ).resolves.toMatchObject(expectedResponse) + }) + }) + }) + + describe('connected `EmailAddress` nodes', () => { + it('will be removed completely', async () => { + await expect(neode.all('EmailAddress')).resolves.toHaveLength(2) + await mutate({ mutation: deleteUserMutation, variables }) + await expect(neode.all('EmailAddress')).resolves.toHaveLength(1) + }) + }) + + describe('connected `SocialMedia` nodes', () => { + beforeEach(async () => { + const socialMedia = await Factory.build('socialMedia') + await socialMedia.relateTo(user, 'ownedBy') + }) + + it('will be removed completely', async () => { + await expect(neode.all('SocialMedia')).resolves.toHaveLength(1) + await mutate({ mutation: deleteUserMutation, variables }) + await expect(neode.all('SocialMedia')).resolves.toHaveLength(0) + }) + }) + }) + }) +}) + +describe('DeleteUser I myself', () => { const deleteUserMutation = gql` mutation($id: ID!, $resource: [Deletable]) { DeleteUser(id: $id, resource: $resource) { @@ -568,456 +996,3 @@ describe('DeleteUser', () => { }) }) }) - - - -describe('DeleteUser as another user', () => { - const deleteUserMutation = gql` - mutation($id: ID!, $resource: [Deletable]) { - DeleteUser(id: $id, resource: $resource) { - id - name - about - deleted - contributions { - id - content - contentExcerpt - deleted - comments { - id - content - contentExcerpt - deleted - } - } - comments { - id - content - contentExcerpt - deleted - } - } - } -` -beforeEach(async () => { - variables = { id: ' u343', resource: [] } - - user = await Factory.build('user', { - name: 'My name should be deleted', - about: 'along with my about', - id: 'u343', - }) - -}) - - beforeEach(async () => { - - - const anotherUser = await Factory.build( - 'user', - { - role: 'user', - }, - { - email: 'user@example.org', - password: '1234', - }, - ) - - authenticatedUser = await anotherUser.toJson() - }) - - it('a user has no authorization to delete another user accounts', async () => { - const { errors } = await mutate({ mutation: deleteUserMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') - }) -}) - - - - -describe('DeleteUser as moderator', () => { - const deleteUserMutation = gql` - mutation($id: ID!, $resource: [Deletable]) { - DeleteUser(id: $id, resource: $resource) { - id - name - about - deleted - contributions { - id - content - contentExcerpt - deleted - comments { - id - content - contentExcerpt - deleted - } - } - comments { - id - content - contentExcerpt - deleted - } - } - } -` -beforeEach(async () => { - variables = { id: ' u343', resource: [] } - - user = await Factory.build('user', { - name: 'My name should be deleted', - about: 'along with my about', - id: 'u343', - }) - -}) - -beforeEach(async () => { - - - const moderator = await Factory.build( - 'user', - { - role: 'moderator', - }, - { - email: 'moderator@example.org', - password: '1234', - }, - ) - - - authenticatedUser = await moderator.toJson() -}) - -it('moderator is not allowed to delete other user accounts', async () => { - const { errors } = await mutate({ mutation: deleteUserMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') -}) - - - -}) - - - - - - -describe('DeleteUser as Admin', () => { - const deleteUserMutation = gql` - mutation($id: ID!, $resource: [Deletable]) { - DeleteUser(id: $id, resource: $resource) { - id - name - about - deleted - contributions { - id - content - contentExcerpt - deleted - comments { - id - content - contentExcerpt - deleted - } - } - comments { - id - content - contentExcerpt - deleted - } - } - } - ` - beforeEach(async () => { - variables = { id: ' u343', resource: [] } - - user = await Factory.build('user', { - name: 'My name should be deleted', - about: 'along with my about', - id: 'u343', - }) - - }) - - - - - describe('authenticated as Admin', () => { - beforeEach(async () => { - const admin = await Factory.build( - 'user', - { - role: 'admin', - }, - { - email: 'admin@example.org', - password: '1234', - }, - ) - authenticatedUser = await admin.toJson() - }) - - - describe('deleting a user account', () => { - beforeEach(() => { - variables = { ...variables, id: 'u343' } - }) - - describe('given posts and comments', () => { - beforeEach(async () => { - await Factory.build('category', { - id: 'cat9', - name: 'Democracy & Politics', - icon: 'university', - }) - await Factory.build( - 'post', - { - id: 'p139', - content: 'Post by user u343', - }, - { - author: user, - categoryIds, - }, - ) - await Factory.build( - 'comment', - { - id: 'c155', - content: 'Comment by user u343', - }, - { - author: user, - }, - ) - await Factory.build( - 'comment', - { - id: 'c156', - content: "A comment by someone else on user u343's post", - }, - { - postId: 'p139', - }, - ) - }) - - it("deletes account, but doesn't delete posts or comments by default", async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'Post by user u343', - contentExcerpt: 'Post by user u343', - deleted: false, - comments: [ - { - id: 'c156', - content: "A comment by someone else on user u343's post", - contentExcerpt: "A comment by someone else on user u343's post", - deleted: false, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'Comment by user u343', - contentExcerpt: 'Comment by user u343', - deleted: false, - }, - ], - }, - }, - errors: undefined, - } - await expect(mutate({ mutation: deleteUserMutation, variables })).resolves.toMatchObject( - expectedResponse, - ) - }) - - describe('deletion of all post requested', () => { - beforeEach(() => { - variables = { ...variables, resource: ['Post'] } - }) - - describe("marks user's posts as deleted", () => { - it('posts on request', async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - comments: [ - { - id: 'c156', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'Comment by user u343', - contentExcerpt: 'Comment by user u343', - deleted: false, - }, - ], - }, - }, - errors: undefined, - } - await expect( - mutate({ mutation: deleteUserMutation, variables }), - ).resolves.toMatchObject(expectedResponse) - }) - }) - }) - - describe('deletion of all comments requested', () => { - beforeEach(() => { - variables = { ...variables, resource: ['Comment'] } - }) - - it('marks comments as deleted', async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'Post by user u343', - contentExcerpt: 'Post by user u343', - deleted: false, - comments: [ - { - id: 'c156', - content: "A comment by someone else on user u343's post", - contentExcerpt: "A comment by someone else on user u343's post", - deleted: false, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - }, - ], - }, - }, - errors: undefined, - } - await expect( - mutate({ mutation: deleteUserMutation, variables }), - ).resolves.toMatchObject(expectedResponse) - }) - }) - - describe('deletion of all post and comments requested', () => { - beforeEach(() => { - variables = { ...variables, resource: ['Post', 'Comment'] } - }) - - it('marks posts and comments as deleted', async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - comments: [ - { - id: 'c156', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - }, - ], - }, - }, - errors: undefined, - } - await expect( - mutate({ mutation: deleteUserMutation, variables }), - ).resolves.toMatchObject(expectedResponse) - }) - }) - }) - - describe('connected `EmailAddress` nodes', () => { - it('will be removed completely', async () => { - await expect(neode.all('EmailAddress')).resolves.toHaveLength(2) - await mutate({ mutation: deleteUserMutation, variables }) - await expect(neode.all('EmailAddress')).resolves.toHaveLength(1) - }) - }) - - describe('connected `SocialMedia` nodes', () => { - beforeEach(async () => { - const socialMedia = await Factory.build('socialMedia') - await socialMedia.relateTo(user, 'ownedBy') - }) - - it('will be removed completely', async () => { - await expect(neode.all('SocialMedia')).resolves.toHaveLength(1) - await mutate({ mutation: deleteUserMutation, variables }) - await expect(neode.all('SocialMedia')).resolves.toHaveLength(0) - }) - }) - }) - }) -}) From b23328a2111e141e66498af7fbcbaf003be70fae Mon Sep 17 00:00:00 2001 From: ogerly Date: Fri, 21 Feb 2020 05:20:21 +0100 Subject: [PATCH 5/7] docs(setup): changes undone and set in own branch --- backend/src/middleware/slugifyMiddleware.spec.js | 2 +- backend/src/models/User.spec.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/backend/src/middleware/slugifyMiddleware.spec.js b/backend/src/middleware/slugifyMiddleware.spec.js index df011b0a5..e522136d6 100644 --- a/backend/src/middleware/slugifyMiddleware.spec.js +++ b/backend/src/middleware/slugifyMiddleware.spec.js @@ -153,7 +153,7 @@ describe('slugifyMiddleware', () => { \`\`\` Learn how to setup the database here: - https://docs.human-connection.org/human-connection/backend#database-indices-and-constraints + https://docs.human-connection.org/human-connection/neo4j `) } }) diff --git a/backend/src/models/User.spec.js b/backend/src/models/User.spec.js index a45a629e5..f448cbf08 100644 --- a/backend/src/models/User.spec.js +++ b/backend/src/models/User.spec.js @@ -46,7 +46,7 @@ describe('slug', () => { \`\`\` Learn how to setup the database here: - https://docs.human-connection.org/human-connection/backend#database-indices-and-constraints + https://docs.human-connection.org/human-connection/neo4j `) } }) From 07ded7419f20d6e294af7e7ab0f71c3a7aea978e Mon Sep 17 00:00:00 2001 From: ogerly Date: Fri, 21 Feb 2020 08:50:25 +0100 Subject: [PATCH 6/7] test: deleting a user is tested with a foreign user, a moderator, an admin --- backend/src/schema/resolvers/users.spec.js | 45 ++++++++++++++++++++-- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/backend/src/schema/resolvers/users.spec.js b/backend/src/schema/resolvers/users.spec.js index ebcd2e3cb..f1e549390 100644 --- a/backend/src/schema/resolvers/users.spec.js +++ b/backend/src/schema/resolvers/users.spec.js @@ -672,7 +672,7 @@ describe('DeleteUser as Admin', () => { }) }) -describe('DeleteUser I myself', () => { +describe('User deletes his account himself', () => { const deleteUserMutation = gql` mutation($id: ID!, $resource: [Deletable]) { DeleteUser(id: $id, resource: $resource) { @@ -701,8 +701,9 @@ describe('DeleteUser I myself', () => { } } ` + beforeEach(async () => { - variables = { id: ' u343', resource: [] } + variables = { id: 'u343', resource: [] } user = await Factory.build('user', { name: 'My name should be deleted', @@ -720,8 +721,44 @@ describe('DeleteUser I myself', () => { ) }) - describe('unauthenticated', () => { - it('throws authorization error', async () => { + describe('unauthenticated anotherUser', () => { + beforeEach(async () => { + const anotherUser = await Factory.build( + 'user', + { + role: 'user', + }, + { + email: 'user@example.org', + password: '1234', + }, + ) + + authenticatedUser = await anotherUser.toJson() + }) + + it('a another User has no authorization to delete this user accounts', async () => { + const { errors } = await mutate({ mutation: deleteUserMutation, variables }) + expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + }) + }) + describe('unauthenticated anotherModerator', () => { + beforeEach(async () => { + const anotherModerator = await Factory.build( + 'user', + { + role: 'moderator', + }, + { + email: 'moderator@example.org', + password: '1234', + }, + ) + + authenticatedUser = await anotherModerator.toJson() + }) + + it('a Moderator has no authorization to delete this user accounts', async () => { const { errors } = await mutate({ mutation: deleteUserMutation, variables }) expect(errors[0]).toHaveProperty('message', 'Not Authorised!') }) From da165906e2ed12baddd902b43064103ab3adfa06 Mon Sep 17 00:00:00 2001 From: roschaefer Date: Tue, 10 Mar 2020 18:16:09 +0100 Subject: [PATCH 7/7] DRY user.spec.js --- .../src/middleware/slugifyMiddleware.spec.js | 2 +- backend/src/models/User.spec.js | 2 +- backend/src/schema/resolvers/users.spec.js | 1132 ++++++++--------- 3 files changed, 505 insertions(+), 631 deletions(-) diff --git a/backend/src/middleware/slugifyMiddleware.spec.js b/backend/src/middleware/slugifyMiddleware.spec.js index e522136d6..df011b0a5 100644 --- a/backend/src/middleware/slugifyMiddleware.spec.js +++ b/backend/src/middleware/slugifyMiddleware.spec.js @@ -153,7 +153,7 @@ describe('slugifyMiddleware', () => { \`\`\` Learn how to setup the database here: - https://docs.human-connection.org/human-connection/neo4j + https://docs.human-connection.org/human-connection/backend#database-indices-and-constraints `) } }) diff --git a/backend/src/models/User.spec.js b/backend/src/models/User.spec.js index f448cbf08..a45a629e5 100644 --- a/backend/src/models/User.spec.js +++ b/backend/src/models/User.spec.js @@ -46,7 +46,7 @@ describe('slug', () => { \`\`\` Learn how to setup the database here: - https://docs.human-connection.org/human-connection/neo4j + https://docs.human-connection.org/human-connection/backend#database-indices-and-constraints `) } }) diff --git a/backend/src/schema/resolvers/users.spec.js b/backend/src/schema/resolvers/users.spec.js index f1e549390..892d2b4b4 100644 --- a/backend/src/schema/resolvers/users.spec.js +++ b/backend/src/schema/resolvers/users.spec.js @@ -244,7 +244,7 @@ describe('UpdateUser', () => { }) }) -describe('DeleteUser as another user', () => { +describe('DeleteUser', () => { const deleteUserMutation = gql` mutation($id: ID!, $resource: [Deletable]) { DeleteUser(id: $id, resource: $resource) { @@ -273,455 +273,17 @@ describe('DeleteUser as another user', () => { } } ` - beforeEach(async () => { - variables = { id: ' u343', resource: [] } - - user = await Factory.build('user', { - name: 'My name should be deleted', - about: 'along with my about', - id: 'u343', - }) - }) - - beforeEach(async () => { - const anotherUser = await Factory.build( - 'user', - { - role: 'user', - }, - { - email: 'user@example.org', - password: '1234', - }, - ) - - authenticatedUser = await anotherUser.toJson() - }) - - it('a user has no authorization to delete another user accounts', async () => { - const { errors } = await mutate({ mutation: deleteUserMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') - }) -}) - -describe('DeleteUser as moderator', () => { - const deleteUserMutation = gql` - mutation($id: ID!, $resource: [Deletable]) { - DeleteUser(id: $id, resource: $resource) { - id - name - about - deleted - contributions { - id - content - contentExcerpt - deleted - comments { - id - content - contentExcerpt - deleted - } - } - comments { - id - content - contentExcerpt - deleted - } - } - } - ` - beforeEach(async () => { - variables = { id: ' u343', resource: [] } - - user = await Factory.build('user', { - name: 'My name should be deleted', - about: 'along with my about', - id: 'u343', - }) - }) - - beforeEach(async () => { - const moderator = await Factory.build( - 'user', - { - role: 'moderator', - }, - { - email: 'moderator@example.org', - password: '1234', - }, - ) - - authenticatedUser = await moderator.toJson() - }) - - it('moderator is not allowed to delete other user accounts', async () => { - const { errors } = await mutate({ mutation: deleteUserMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') - }) -}) - -describe('DeleteUser as Admin', () => { - const deleteUserMutation = gql` - mutation($id: ID!, $resource: [Deletable]) { - DeleteUser(id: $id, resource: $resource) { - id - name - about - deleted - contributions { - id - content - contentExcerpt - deleted - comments { - id - content - contentExcerpt - deleted - } - } - comments { - id - content - contentExcerpt - deleted - } - } - } - ` - beforeEach(async () => { - variables = { id: ' u343', resource: [] } - - user = await Factory.build('user', { - name: 'My name should be deleted', - about: 'along with my about', - id: 'u343', - }) - }) - - describe('authenticated as Admin', () => { + describe('as another user', () => { beforeEach(async () => { - const admin = await Factory.build( - 'user', - { - role: 'admin', - }, - { - email: 'admin@example.org', - password: '1234', - }, - ) - authenticatedUser = await admin.toJson() - }) + variables = { id: ' u343', resource: [] } - describe('deleting a user account', () => { - beforeEach(() => { - variables = { ...variables, id: 'u343' } - }) - - describe('given posts and comments', () => { - beforeEach(async () => { - await Factory.build('category', { - id: 'cat9', - name: 'Democracy & Politics', - icon: 'university', - }) - await Factory.build( - 'post', - { - id: 'p139', - content: 'Post by user u343', - }, - { - author: user, - categoryIds, - }, - ) - await Factory.build( - 'comment', - { - id: 'c155', - content: 'Comment by user u343', - }, - { - author: user, - }, - ) - await Factory.build( - 'comment', - { - id: 'c156', - content: "A comment by someone else on user u343's post", - }, - { - postId: 'p139', - }, - ) - }) - - it("deletes account, but doesn't delete posts or comments by default", async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'Post by user u343', - contentExcerpt: 'Post by user u343', - deleted: false, - comments: [ - { - id: 'c156', - content: "A comment by someone else on user u343's post", - contentExcerpt: "A comment by someone else on user u343's post", - deleted: false, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'Comment by user u343', - contentExcerpt: 'Comment by user u343', - deleted: false, - }, - ], - }, - }, - errors: undefined, - } - await expect(mutate({ mutation: deleteUserMutation, variables })).resolves.toMatchObject( - expectedResponse, - ) - }) - - describe('deletion of all post requested', () => { - beforeEach(() => { - variables = { ...variables, resource: ['Post'] } - }) - - describe("marks user's posts as deleted", () => { - it('on request', async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - comments: [ - { - id: 'c156', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'Comment by user u343', - contentExcerpt: 'Comment by user u343', - deleted: false, - }, - ], - }, - }, - errors: undefined, - } - await expect( - mutate({ mutation: deleteUserMutation, variables }), - ).resolves.toMatchObject(expectedResponse) - }) - }) - }) - - describe('deletion of all comments requested', () => { - beforeEach(() => { - variables = { ...variables, resource: ['Comment'] } - }) - - it('marks comments as deleted', async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'Post by user u343', - contentExcerpt: 'Post by user u343', - deleted: false, - comments: [ - { - id: 'c156', - content: "A comment by someone else on user u343's post", - contentExcerpt: "A comment by someone else on user u343's post", - deleted: false, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - }, - ], - }, - }, - errors: undefined, - } - await expect( - mutate({ mutation: deleteUserMutation, variables }), - ).resolves.toMatchObject(expectedResponse) - }) - }) - - describe('deletion of all posts and comments requested', () => { - beforeEach(() => { - variables = { ...variables, resource: ['Post', 'Comment'] } - }) - - it('marks posts and comments as deleted', async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - comments: [ - { - id: 'c156', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - }, - ], - }, - }, - errors: undefined, - } - await expect( - mutate({ mutation: deleteUserMutation, variables }), - ).resolves.toMatchObject(expectedResponse) - }) - }) - }) - - describe('connected `EmailAddress` nodes', () => { - it('will be removed completely', async () => { - await expect(neode.all('EmailAddress')).resolves.toHaveLength(2) - await mutate({ mutation: deleteUserMutation, variables }) - await expect(neode.all('EmailAddress')).resolves.toHaveLength(1) - }) - }) - - describe('connected `SocialMedia` nodes', () => { - beforeEach(async () => { - const socialMedia = await Factory.build('socialMedia') - await socialMedia.relateTo(user, 'ownedBy') - }) - - it('will be removed completely', async () => { - await expect(neode.all('SocialMedia')).resolves.toHaveLength(1) - await mutate({ mutation: deleteUserMutation, variables }) - await expect(neode.all('SocialMedia')).resolves.toHaveLength(0) - }) + user = await Factory.build('user', { + name: 'My name should be deleted', + about: 'along with my about', + id: 'u343', }) }) - }) -}) -describe('User deletes his account himself', () => { - const deleteUserMutation = gql` - mutation($id: ID!, $resource: [Deletable]) { - DeleteUser(id: $id, resource: $resource) { - id - name - about - deleted - contributions { - id - content - contentExcerpt - deleted - comments { - id - content - contentExcerpt - deleted - } - } - comments { - id - content - contentExcerpt - deleted - } - } - } - ` - - beforeEach(async () => { - variables = { id: 'u343', resource: [] } - - user = await Factory.build('user', { - name: 'My name should be deleted', - about: 'along with my about', - id: 'u343', - }) - await Factory.build( - 'user', - { - id: 'not-my-account', - }, - { - email: 'friends-account@example.org', - }, - ) - }) - - describe('unauthenticated anotherUser', () => { beforeEach(async () => { const anotherUser = await Factory.build( 'user', @@ -737,14 +299,25 @@ describe('User deletes his account himself', () => { authenticatedUser = await anotherUser.toJson() }) - it('a another User has no authorization to delete this user accounts', async () => { + it("an ordinary user has no authorization to delete another user's account", async () => { const { errors } = await mutate({ mutation: deleteUserMutation, variables }) expect(errors[0]).toHaveProperty('message', 'Not Authorised!') }) }) - describe('unauthenticated anotherModerator', () => { + + describe('as moderator', () => { beforeEach(async () => { - const anotherModerator = await Factory.build( + variables = { id: ' u343', resource: [] } + + user = await Factory.build('user', { + name: 'My name should be deleted', + about: 'along with my about', + id: 'u343', + }) + }) + + beforeEach(async () => { + const moderator = await Factory.build( 'user', { role: 'moderator', @@ -755,172 +328,87 @@ describe('User deletes his account himself', () => { }, ) - authenticatedUser = await anotherModerator.toJson() + authenticatedUser = await moderator.toJson() }) - it('a Moderator has no authorization to delete this user accounts', async () => { + it('moderator is not allowed to delete other user accounts', async () => { const { errors } = await mutate({ mutation: deleteUserMutation, variables }) expect(errors[0]).toHaveProperty('message', 'Not Authorised!') }) }) - describe('authenticated', () => { + describe('as admin', () => { beforeEach(async () => { - authenticatedUser = await user.toJson() - }) + variables = { id: ' u343', resource: [] } - describe("attempting to delete another user's account", () => { - beforeEach(() => { - variables = { ...variables, id: 'not-my-account' } - }) - - it('throws an authorization error', async () => { - const { errors } = await mutate({ mutation: deleteUserMutation, variables }) - expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + user = await Factory.build('user', { + name: 'My name should be deleted', + about: 'along with my about', + id: 'u343', }) }) - describe('attempting to delete my own account', () => { - beforeEach(() => { - variables = { ...variables, id: 'u343' } + describe('authenticated as Admin', () => { + beforeEach(async () => { + const admin = await Factory.build( + 'user', + { + role: 'admin', + }, + { + email: 'admin@example.org', + password: '1234', + }, + ) + authenticatedUser = await admin.toJson() }) - describe('given posts and comments', () => { - beforeEach(async () => { - await Factory.build('category', { - id: 'cat9', - name: 'Democracy & Politics', - icon: 'university', - }) - await Factory.build( - 'post', - { - id: 'p139', - content: 'Post by user u343', - }, - { - author: user, - categoryIds, - }, - ) - await Factory.build( - 'comment', - { - id: 'c155', - content: 'Comment by user u343', - }, - { - author: user, - }, - ) - await Factory.build( - 'comment', - { - id: 'c156', - content: "A comment by someone else on user u343's post", - }, - { - postId: 'p139', - }, - ) + describe('deleting a user account', () => { + beforeEach(() => { + variables = { ...variables, id: 'u343' } }) - it("deletes my account, but doesn't delete posts or comments by default", async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'Post by user u343', - contentExcerpt: 'Post by user u343', - deleted: false, - comments: [ - { - id: 'c156', - content: "A comment by someone else on user u343's post", - contentExcerpt: "A comment by someone else on user u343's post", - deleted: false, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'Comment by user u343', - contentExcerpt: 'Comment by user u343', - deleted: false, - }, - ], - }, - }, - errors: undefined, - } - await expect(mutate({ mutation: deleteUserMutation, variables })).resolves.toMatchObject( - expectedResponse, - ) - }) - - describe('deletion of all post requested', () => { - beforeEach(() => { - variables = { ...variables, resource: ['Post'] } - }) - - describe("marks user's posts as deleted", () => { - it('posts on request', async () => { - const expectedResponse = { - data: { - DeleteUser: { - id: 'u343', - name: 'UNAVAILABLE', - about: 'UNAVAILABLE', - deleted: true, - contributions: [ - { - id: 'p139', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - comments: [ - { - id: 'c156', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, - }, - ], - }, - ], - comments: [ - { - id: 'c155', - content: 'Comment by user u343', - contentExcerpt: 'Comment by user u343', - deleted: false, - }, - ], - }, - }, - errors: undefined, - } - await expect( - mutate({ mutation: deleteUserMutation, variables }), - ).resolves.toMatchObject(expectedResponse) + describe('given posts and comments', () => { + beforeEach(async () => { + await Factory.build('category', { + id: 'cat9', + name: 'Democracy & Politics', + icon: 'university', }) - }) - }) - - describe('deletion of all comments requested', () => { - beforeEach(() => { - variables = { ...variables, resource: ['Comment'] } + await Factory.build( + 'post', + { + id: 'p139', + content: 'Post by user u343', + }, + { + author: user, + categoryIds, + }, + ) + await Factory.build( + 'comment', + { + id: 'c155', + content: 'Comment by user u343', + }, + { + author: user, + }, + ) + await Factory.build( + 'comment', + { + id: 'c156', + content: "A comment by someone else on user u343's post", + }, + { + postId: 'p139', + }, + ) }) - it('marks comments as deleted', async () => { + it("deletes account, but doesn't delete posts or comments by default", async () => { const expectedResponse = { data: { DeleteUser: { @@ -947,9 +435,9 @@ describe('User deletes his account himself', () => { comments: [ { id: 'c155', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, + content: 'Comment by user u343', + contentExcerpt: 'Comment by user u343', + deleted: false, }, ], }, @@ -960,14 +448,257 @@ describe('User deletes his account himself', () => { mutate({ mutation: deleteUserMutation, variables }), ).resolves.toMatchObject(expectedResponse) }) - }) - describe('deletion of all post and comments requested', () => { - beforeEach(() => { - variables = { ...variables, resource: ['Post', 'Comment'] } + describe('deletion of all post requested', () => { + beforeEach(() => { + variables = { ...variables, resource: ['Post'] } + }) + + describe("marks user's posts as deleted", () => { + it('on request', async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + comments: [ + { + id: 'c156', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'Comment by user u343', + contentExcerpt: 'Comment by user u343', + deleted: false, + }, + ], + }, + }, + errors: undefined, + } + await expect( + mutate({ mutation: deleteUserMutation, variables }), + ).resolves.toMatchObject(expectedResponse) + }) + }) }) - it('marks posts and comments as deleted', async () => { + describe('deletion of all comments requested', () => { + beforeEach(() => { + variables = { ...variables, resource: ['Comment'] } + }) + + it('marks comments as deleted', async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'Post by user u343', + contentExcerpt: 'Post by user u343', + deleted: false, + comments: [ + { + id: 'c156', + content: "A comment by someone else on user u343's post", + contentExcerpt: "A comment by someone else on user u343's post", + deleted: false, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + }, + errors: undefined, + } + await expect( + mutate({ mutation: deleteUserMutation, variables }), + ).resolves.toMatchObject(expectedResponse) + }) + }) + + describe('deletion of all posts and comments requested', () => { + beforeEach(() => { + variables = { ...variables, resource: ['Post', 'Comment'] } + }) + + it('marks posts and comments as deleted', async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + comments: [ + { + id: 'c156', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + }, + errors: undefined, + } + await expect( + mutate({ mutation: deleteUserMutation, variables }), + ).resolves.toMatchObject(expectedResponse) + }) + }) + }) + + describe('connected `EmailAddress` nodes', () => { + it('will be removed completely', async () => { + await expect(neode.all('EmailAddress')).resolves.toHaveLength(2) + await mutate({ mutation: deleteUserMutation, variables }) + await expect(neode.all('EmailAddress')).resolves.toHaveLength(1) + }) + }) + + describe('connected `SocialMedia` nodes', () => { + beforeEach(async () => { + const socialMedia = await Factory.build('socialMedia') + await socialMedia.relateTo(user, 'ownedBy') + }) + + it('will be removed completely', async () => { + await expect(neode.all('SocialMedia')).resolves.toHaveLength(1) + await mutate({ mutation: deleteUserMutation, variables }) + await expect(neode.all('SocialMedia')).resolves.toHaveLength(0) + }) + }) + }) + }) + }) + + describe('user deletes his own account', () => { + beforeEach(async () => { + variables = { id: 'u343', resource: [] } + + user = await Factory.build('user', { + name: 'My name should be deleted', + about: 'along with my about', + id: 'u343', + }) + await Factory.build( + 'user', + { + id: 'not-my-account', + }, + { + email: 'friends-account@example.org', + }, + ) + }) + + describe('authenticated', () => { + beforeEach(async () => { + authenticatedUser = await user.toJson() + }) + + describe("attempting to delete another user's account", () => { + beforeEach(() => { + variables = { ...variables, id: 'not-my-account' } + }) + + it('throws an authorization error', async () => { + const { errors } = await mutate({ mutation: deleteUserMutation, variables }) + expect(errors[0]).toHaveProperty('message', 'Not Authorised!') + }) + }) + + describe('attempting to delete my own account', () => { + beforeEach(() => { + variables = { ...variables, id: 'u343' } + }) + + describe('given posts and comments', () => { + beforeEach(async () => { + await Factory.build('category', { + id: 'cat9', + name: 'Democracy & Politics', + icon: 'university', + }) + await Factory.build( + 'post', + { + id: 'p139', + content: 'Post by user u343', + }, + { + author: user, + categoryIds, + }, + ) + await Factory.build( + 'comment', + { + id: 'c155', + content: 'Comment by user u343', + }, + { + author: user, + }, + ) + await Factory.build( + 'comment', + { + id: 'c156', + content: "A comment by someone else on user u343's post", + }, + { + postId: 'p139', + }, + ) + }) + + it("deletes my account, but doesn't delete posts or comments by default", async () => { const expectedResponse = { data: { DeleteUser: { @@ -978,15 +709,15 @@ describe('User deletes his account himself', () => { contributions: [ { id: 'p139', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, + content: 'Post by user u343', + contentExcerpt: 'Post by user u343', + deleted: false, comments: [ { id: 'c156', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, + content: "A comment by someone else on user u343's post", + contentExcerpt: "A comment by someone else on user u343's post", + deleted: false, }, ], }, @@ -994,9 +725,9 @@ describe('User deletes his account himself', () => { comments: [ { id: 'c155', - content: 'UNAVAILABLE', - contentExcerpt: 'UNAVAILABLE', - deleted: true, + content: 'Comment by user u343', + contentExcerpt: 'Comment by user u343', + deleted: false, }, ], }, @@ -1007,27 +738,170 @@ describe('User deletes his account himself', () => { mutate({ mutation: deleteUserMutation, variables }), ).resolves.toMatchObject(expectedResponse) }) - }) - }) - describe('connected `EmailAddress` nodes', () => { - it('will be removed completely', async () => { - await expect(neode.all('EmailAddress')).resolves.toHaveLength(2) - await mutate({ mutation: deleteUserMutation, variables }) - await expect(neode.all('EmailAddress')).resolves.toHaveLength(1) - }) - }) + describe('deletion of all post requested', () => { + beforeEach(() => { + variables = { ...variables, resource: ['Post'] } + }) - describe('connected `SocialMedia` nodes', () => { - beforeEach(async () => { - const socialMedia = await Factory.build('socialMedia') - await socialMedia.relateTo(user, 'ownedBy') + describe("marks user's posts as deleted", () => { + it('posts on request', async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + comments: [ + { + id: 'c156', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'Comment by user u343', + contentExcerpt: 'Comment by user u343', + deleted: false, + }, + ], + }, + }, + errors: undefined, + } + await expect( + mutate({ mutation: deleteUserMutation, variables }), + ).resolves.toMatchObject(expectedResponse) + }) + }) + }) + + describe('deletion of all comments requested', () => { + beforeEach(() => { + variables = { ...variables, resource: ['Comment'] } + }) + + it('marks comments as deleted', async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'Post by user u343', + contentExcerpt: 'Post by user u343', + deleted: false, + comments: [ + { + id: 'c156', + content: "A comment by someone else on user u343's post", + contentExcerpt: "A comment by someone else on user u343's post", + deleted: false, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + }, + errors: undefined, + } + await expect( + mutate({ mutation: deleteUserMutation, variables }), + ).resolves.toMatchObject(expectedResponse) + }) + }) + + describe('deletion of all post and comments requested', () => { + beforeEach(() => { + variables = { ...variables, resource: ['Post', 'Comment'] } + }) + + it('marks posts and comments as deleted', async () => { + const expectedResponse = { + data: { + DeleteUser: { + id: 'u343', + name: 'UNAVAILABLE', + about: 'UNAVAILABLE', + deleted: true, + contributions: [ + { + id: 'p139', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + comments: [ + { + id: 'c156', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + ], + comments: [ + { + id: 'c155', + content: 'UNAVAILABLE', + contentExcerpt: 'UNAVAILABLE', + deleted: true, + }, + ], + }, + }, + errors: undefined, + } + await expect( + mutate({ mutation: deleteUserMutation, variables }), + ).resolves.toMatchObject(expectedResponse) + }) + }) }) - it('will be removed completely', async () => { - await expect(neode.all('SocialMedia')).resolves.toHaveLength(1) - await mutate({ mutation: deleteUserMutation, variables }) - await expect(neode.all('SocialMedia')).resolves.toHaveLength(0) + describe('connected `EmailAddress` nodes', () => { + it('will be removed completely', async () => { + await expect(neode.all('EmailAddress')).resolves.toHaveLength(2) + await mutate({ mutation: deleteUserMutation, variables }) + await expect(neode.all('EmailAddress')).resolves.toHaveLength(1) + }) + }) + + describe('connected `SocialMedia` nodes', () => { + beforeEach(async () => { + const socialMedia = await Factory.build('socialMedia') + await socialMedia.relateTo(user, 'ownedBy') + }) + + it('will be removed completely', async () => { + await expect(neode.all('SocialMedia')).resolves.toHaveLength(1) + await mutate({ mutation: deleteUserMutation, variables }) + await expect(neode.all('SocialMedia')).resolves.toHaveLength(0) + }) }) }) })