diff --git a/backend/src/schema/resolvers/reports.spec.js b/backend/src/schema/resolvers/reports.spec.js index 4022be1b1..21b1b4a7b 100644 --- a/backend/src/schema/resolvers/reports.spec.js +++ b/backend/src/schema/resolvers/reports.spec.js @@ -1,315 +1,16 @@ -import { GraphQLClient } from 'graphql-request' -import Factory from '../../seed/factories' -import { host, login, gql } from '../../jest/helpers' -import { getDriver, neode } from '../../bootstrap/neo4j' import { createTestClient } from 'apollo-server-testing' import createServer from '../.././server' +import Factory from '../../seed/factories' +import { gql } from '../../jest/helpers' +import { getDriver, neode as getNeode } from '../../bootstrap/neo4j' const factory = Factory() -const instance = neode() +const instance = getNeode() const driver = getDriver() -describe('report mutation', () => { - let reportMutation - let headers - let client - let variables - let createPostVariables - let user +describe('report resources', () => { + let authenticatedUser, currentUser, mutate, query, moderator, abusiveUser const categoryIds = ['cat9'] - - const action = () => { - reportMutation = gql` - mutation($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) { - report( - resourceId: $resourceId - reasonCategory: $reasonCategory - reasonDescription: $reasonDescription - ) { - createdAt - reasonCategory - reasonDescription - type - submitter { - email - } - user { - name - } - post { - title - } - comment { - content - } - } - } - ` - client = new GraphQLClient(host, { - headers, - }) - return client.request(reportMutation, variables) - } - - beforeEach(async () => { - variables = { - resourceId: 'whatever', - reasonCategory: 'other', - reasonDescription: 'Violates code of conduct !!!', - } - headers = {} - user = await factory.create('User', { - id: 'u1', - role: 'user', - email: 'test@example.org', - password: '1234', - }) - await factory.create('User', { - id: 'u2', - role: 'user', - name: 'abusive-user', - email: 'abusive-user@example.org', - }) - await instance.create('Category', { - id: 'cat9', - name: 'Democracy & Politics', - icon: 'university', - }) - }) - - afterEach(async () => { - await factory.cleanDatabase() - }) - - describe('unauthenticated', () => { - it('throws authorization error', async () => { - await expect(action()).rejects.toThrow('Not Authorised') - }) - }) - - describe('authenticated', () => { - beforeEach(async () => { - headers = await login({ - email: 'test@example.org', - password: '1234', - }) - }) - - describe('invalid resource id', () => { - it('returns null', async () => { - await expect(action()).resolves.toEqual({ - report: null, - }) - }) - }) - - describe('valid resource id', () => { - describe('reported resource is a user', () => { - beforeEach(async () => { - variables = { - ...variables, - resourceId: 'u2', - } - }) - - it('returns type "User"', async () => { - await expect(action()).resolves.toMatchObject({ - report: { - type: 'User', - }, - }) - }) - - it('returns resource in user attribute', async () => { - await expect(action()).resolves.toMatchObject({ - report: { - user: { - name: 'abusive-user', - }, - }, - }) - }) - - it('returns the submitter', async () => { - await expect(action()).resolves.toMatchObject({ - report: { - submitter: { - email: 'test@example.org', - }, - }, - }) - }) - - it('returns a date', async () => { - await expect(action()).resolves.toMatchObject({ - report: { - createdAt: expect.any(String), - }, - }) - }) - - it('returns the reason category', async () => { - variables = { - ...variables, - reasonCategory: 'criminal_behavior_violation_german_law', - } - await expect(action()).resolves.toMatchObject({ - report: { - reasonCategory: 'criminal_behavior_violation_german_law', - }, - }) - }) - - it('gives an error if the reason category is not in enum "ReasonCategory"', async () => { - variables = { - ...variables, - reasonCategory: 'my_category', - } - await expect(action()).rejects.toThrow( - 'got invalid value "my_category"; Expected type ReasonCategory', - ) - }) - - it('returns the reason description', async () => { - variables = { - ...variables, - reasonDescription: 'My reason!', - } - await expect(action()).resolves.toMatchObject({ - report: { - reasonDescription: 'My reason!', - }, - }) - }) - - it('sanitize the reason description', async () => { - variables = { - ...variables, - reasonDescription: 'My reason !', - } - await expect(action()).resolves.toMatchObject({ - report: { - reasonDescription: 'My reason !', - }, - }) - }) - }) - - describe('reported resource is a post', () => { - beforeEach(async () => { - await factory.create('Post', { - author: user, - id: 'p23', - title: 'Matt and Robert having a pair-programming', - categoryIds, - }) - variables = { - ...variables, - resourceId: 'p23', - } - }) - - it('returns type "Post"', async () => { - await expect(action()).resolves.toMatchObject({ - report: { - type: 'Post', - }, - }) - }) - - it('returns resource in post attribute', async () => { - await expect(action()).resolves.toMatchObject({ - report: { - post: { - title: 'Matt and Robert having a pair-programming', - }, - }, - }) - }) - - it('returns null in user attribute', async () => { - await expect(action()).resolves.toMatchObject({ - report: { - user: null, - }, - }) - }) - }) - - /* An der Stelle würde ich den p23 noch mal prüfen, diesmal muss aber eine error meldung kommen. - At this point I would check the p23 again, but this time there must be an error message. */ - - describe('reported resource is a comment', () => { - beforeEach(async () => { - createPostVariables = { - id: 'p1', - title: 'post to comment on', - content: 'please comment on me', - categoryIds, - } - await factory.create('Post', { ...createPostVariables, author: user }) - await factory.create('Comment', { - author: user, - postId: 'p1', - id: 'c34', - content: 'Robert getting tired.', - }) - variables = { - ...variables, - resourceId: 'c34', - } - }) - - it('returns type "Comment"', async () => { - await expect(action()).resolves.toMatchObject({ - report: { - type: 'Comment', - }, - }) - }) - - it('returns resource in comment attribute', async () => { - await expect(action()).resolves.toMatchObject({ - report: { - comment: { - content: 'Robert getting tired.', - }, - }, - }) - }) - }) - - /* An der Stelle würde ich den c34 noch mal prüfen, diesmal muss aber eine error meldung kommen. - At this point I would check the c34 again, but this time there must be an error message. */ - - describe('reported resource is a tag', () => { - beforeEach(async () => { - await factory.create('Tag', { - id: 't23', - }) - variables = { - ...variables, - resourceId: 't23', - } - }) - - it('returns null', async () => { - await expect(action()).resolves.toMatchObject({ - report: null, - }) - }) - }) - - /* An der Stelle würde ich den t23 noch mal prüfen, diesmal muss aber eine error meldung kommen. - At this point I would check the t23 again, but this time there must be an error message. */ - }) - }) -}) - -describe('reports query', () => { - let query, mutate, authenticatedUser, moderator, user, author - const categoryIds = ['cat9'] - const reportMutation = gql` mutation($resourceId: ID!, $reasonCategory: ReasonCategory!, $reasonDescription: String!) { report( @@ -317,32 +18,30 @@ describe('reports query', () => { reasonCategory: $reasonCategory reasonDescription: $reasonDescription ) { - type - } - } - ` - const reportsQuery = gql` - query { - reports(orderBy: createdAt_desc) { createdAt reasonCategory reasonDescription - submitter { - id - } type + submitter { + email + } user { - id + name } post { - id + title } comment { - id + content } } } ` + const variables = { + resourceId: 'whatever', + reasonCategory: 'other', + reasonDescription: 'Violates code of conduct !!!', + } beforeAll(async () => { await factory.cleanDatabase() @@ -350,172 +49,545 @@ describe('reports query', () => { context: () => { return { driver, + neode: instance, user: authenticatedUser, } }, }) - query = createTestClient(server).query mutate = createTestClient(server).mutate - }) - - beforeEach(async () => { - authenticatedUser = null - - moderator = await factory.create('User', { - id: 'mod1', - role: 'moderator', - email: 'moderator@example.org', - password: '1234', - }) - user = await factory.create('User', { - id: 'user1', - role: 'user', - email: 'test@example.org', - password: '1234', - }) - author = await factory.create('User', { - id: 'auth1', - role: 'user', - name: 'abusive-user', - email: 'abusive-user@example.org', - }) - await instance.create('Category', { - id: 'cat9', - name: 'Democracy & Politics', - icon: 'university', - }) - - await Promise.all([ - factory.create('Post', { - author, - id: 'p1', - categoryIds, - content: 'Interesting Knowledge', - }), - factory.create('Post', { - author: moderator, - id: 'p2', - categoryIds, - content: 'More things to do …', - }), - factory.create('Post', { - author: user, - id: 'p3', - categoryIds, - content: 'I am at school …', - }), - ]) - await Promise.all([ - factory.create('Comment', { - author: user, - id: 'c1', - postId: 'p1', - }), - ]) - - authenticatedUser = await user.toJson() - await Promise.all([ - mutate({ - mutation: reportMutation, - variables: { - resourceId: 'p1', - reasonCategory: 'other', - reasonDescription: 'This comment is bigoted', - }, - }), - mutate({ - mutation: reportMutation, - variables: { - resourceId: 'c1', - reasonCategory: 'discrimination_etc', - reasonDescription: 'This post is bigoted', - }, - }), - mutate({ - mutation: reportMutation, - variables: { - resourceId: 'auth1', - reasonCategory: 'doxing', - reasonDescription: 'This user is harassing me with bigoted remarks', - }, - }), - ]) - authenticatedUser = null + query = createTestClient(server).query }) afterEach(async () => { await factory.cleanDatabase() }) - describe('unauthenticated', () => { - it('throws authorization error', async () => { + describe('report a resource', () => { + describe('unauthenticated', () => { + it('throws authorization error', async () => { + authenticatedUser = null + await expect(mutate({ mutation: reportMutation, variables })).resolves.toMatchObject({ + data: { report: null }, + errors: [{ message: 'Not Authorised!' }], + }) + }) + }) + + describe('authenticated', () => { + beforeEach(async () => { + currentUser = await factory.create('User', { + id: 'current-user-id', + role: 'user', + email: 'test@example.org', + password: '1234', + }) + await factory.create('User', { + id: 'abusive-user-id', + role: 'user', + name: 'abusive-user', + email: 'abusive-user@example.org', + }) + await instance.create('Category', { + id: 'cat9', + name: 'Democracy & Politics', + icon: 'university', + }) + + authenticatedUser = await currentUser.toJson() + }) + + describe('invalid resource id', () => { + it('returns null', async () => { + await expect(mutate({ mutation: reportMutation, variables })).resolves.toMatchObject({ + data: { report: null }, + errors: undefined, + }) + }) + }) + + describe('valid resource', () => { + describe('reported resource is a user', () => { + it('returns type "User"', async () => { + await expect( + mutate({ + mutation: reportMutation, + variables: { ...variables, resourceId: 'abusive-user-id' }, + }), + ).resolves.toMatchObject({ + data: { + report: { + type: 'User', + }, + }, + errors: undefined, + }) + }) + + it('returns resource in user attribute', async () => { + await expect( + mutate({ + mutation: reportMutation, + variables: { ...variables, resourceId: 'abusive-user-id' }, + }), + ).resolves.toMatchObject({ + data: { + report: { + user: { + name: 'abusive-user', + }, + }, + }, + errors: undefined, + }) + }) + + it('returns the submitter', async () => { + await expect( + mutate({ + mutation: reportMutation, + variables: { ...variables, resourceId: 'abusive-user-id' }, + }), + ).resolves.toMatchObject({ + data: { + report: { + submitter: { + email: 'test@example.org', + }, + }, + }, + errors: undefined, + }) + }) + + it('returns a date', async () => { + await expect( + mutate({ + mutation: reportMutation, + variables: { ...variables, resourceId: 'abusive-user-id' }, + }), + ).resolves.toMatchObject({ + data: { + report: { + createdAt: expect.any(String), + }, + }, + errors: undefined, + }) + }) + + it('returns the reason category', async () => { + await expect( + mutate({ + mutation: reportMutation, + variables: { + ...variables, + resourceId: 'abusive-user-id', + reasonCategory: 'criminal_behavior_violation_german_law', + }, + }), + ).resolves.toMatchObject({ + data: { + report: { + reasonCategory: 'criminal_behavior_violation_german_law', + }, + }, + errors: undefined, + }) + }) + + it('gives an error if the reason category is not in enum "ReasonCategory"', async () => { + await expect( + mutate({ + mutation: reportMutation, + variables: { + ...variables, + resourceId: 'abusive-user-id', + reasonCategory: 'category_missing_from_enum_reason_category', + }, + }), + ).resolves.toMatchObject({ + data: undefined, + errors: [ + { + message: + 'Variable "$reasonCategory" got invalid value "category_missing_from_enum_reason_category"; Expected type ReasonCategory.', + }, + ], + }) + }) + + it('returns the reason description', async () => { + await expect( + mutate({ + mutation: reportMutation, + variables: { + ...variables, + resourceId: 'abusive-user-id', + reasonDescription: 'My reason!', + }, + }), + ).resolves.toMatchObject({ + data: { + report: { + reasonDescription: 'My reason!', + }, + }, + errors: undefined, + }) + }) + + it('sanitize the reason description', async () => { + await expect( + mutate({ + mutation: reportMutation, + variables: { + ...variables, + resourceId: 'abusive-user-id', + reasonDescription: 'My reason !', + }, + }), + ).resolves.toMatchObject({ + data: { + report: { + reasonDescription: 'My reason !', + }, + }, + errors: undefined, + }) + }) + }) + + describe('reported resource is a post', () => { + beforeEach(async () => { + await factory.create('Post', { + author: currentUser, + id: 'post-to-report-id', + title: 'This is a post that is going to be reported', + categoryIds, + }) + }) + + it('returns type "Post"', async () => { + await expect( + mutate({ + mutation: reportMutation, + variables: { + ...variables, + resourceId: 'post-to-report-id', + }, + }), + ).resolves.toMatchObject({ + data: { + report: { + type: 'Post', + }, + }, + errors: undefined, + }) + }) + + it('returns resource in post attribute', async () => { + await expect( + mutate({ + mutation: reportMutation, + variables: { + ...variables, + resourceId: 'post-to-report-id', + }, + }), + ).resolves.toMatchObject({ + data: { + report: { + post: { + title: 'This is a post that is going to be reported', + }, + }, + }, + errors: undefined, + }) + }) + + it('returns null in user attribute', async () => { + await expect( + mutate({ + mutation: reportMutation, + variables: { + ...variables, + resourceId: 'post-to-report-id', + }, + }), + ).resolves.toMatchObject({ + data: { + report: { + user: null, + }, + }, + errors: undefined, + }) + }) + }) + + describe('reported resource is a comment', () => { + let createPostVariables + beforeEach(async () => { + createPostVariables = { + id: 'p1', + title: 'post to comment on', + content: 'please comment on me', + categoryIds, + } + await factory.create('Post', { ...createPostVariables, author: currentUser }) + await factory.create('Comment', { + author: currentUser, + postId: 'p1', + id: 'comment-to-report-id', + content: 'Post comment to be reported.', + }) + }) + + it('returns type "Comment"', async () => { + await expect( + mutate({ + mutation: reportMutation, + variables: { + ...variables, + resourceId: 'comment-to-report-id', + }, + }), + ).resolves.toMatchObject({ + data: { + report: { + type: 'Comment', + }, + }, + errors: undefined, + }) + }) + + it('returns resource in comment attribute', async () => { + await expect( + mutate({ + mutation: reportMutation, + variables: { + ...variables, + resourceId: 'comment-to-report-id', + }, + }), + ).resolves.toMatchObject({ + data: { + report: { + comment: { + content: 'Post comment to be reported.', + }, + }, + }, + errors: undefined, + }) + }) + }) + + describe('reported resource is a tag', () => { + beforeEach(async () => { + await factory.create('Tag', { + id: 'tag-to-report-id', + }) + }) + + it('returns null', async () => { + await expect( + mutate({ + mutation: reportMutation, + variables: { + ...variables, + resourceId: 'tag-to-report-id', + }, + }), + ).resolves.toMatchObject({ + data: { report: null }, + errors: undefined, + }) + }) + }) + }) + }) + }) + describe('query for reported resource', () => { + const reportsQuery = gql` + query { + reports(orderBy: createdAt_desc) { + createdAt + reasonCategory + reasonDescription + submitter { + id + } + type + user { + id + } + post { + id + } + comment { + id + } + } + } + ` + + beforeEach(async () => { authenticatedUser = null - expect(query({ query: reportsQuery })).resolves.toMatchObject({ - data: { reports: null }, - errors: [{ message: 'Not Authorised!' }], - }) - }) - it('role "user" gets no reports', async () => { - authenticatedUser = await user.toJson() - expect(query({ query: reportsQuery })).resolves.toMatchObject({ - data: { reports: null }, - errors: [{ message: 'Not Authorised!' }], + moderator = await factory.create('User', { + id: 'moderator-1', + role: 'moderator', + email: 'moderator@example.org', + password: '1234', + }) + currentUser = await factory.create('User', { + id: 'current-user-id', + role: 'user', + email: 'current.user@example.org', + password: '1234', + }) + abusiveUser = await factory.create('User', { + id: 'abusive-user-1', + role: 'user', + name: 'abusive-user', + email: 'abusive-user@example.org', + }) + await instance.create('Category', { + id: 'cat9', + name: 'Democracy & Politics', + icon: 'university', }) - }) - it('role "moderator" gets reports', async () => { - const expected = { - // to check 'orderBy: createdAt_desc' is not possible here, because 'createdAt' does not differ - reports: expect.arrayContaining([ - expect.objectContaining({ - createdAt: expect.any(String), - reasonCategory: 'doxing', - reasonDescription: 'This user is harassing me with bigoted remarks', - submitter: expect.objectContaining({ - id: 'user1', - }), - type: 'User', - user: expect.objectContaining({ - id: 'auth1', - }), - post: null, - comment: null, - }), - expect.objectContaining({ - createdAt: expect.any(String), + await Promise.all([ + factory.create('Post', { + author: abusiveUser, + id: 'abusive-post-1', + categoryIds, + content: 'Interesting Knowledge', + }), + factory.create('Post', { + author: moderator, + id: 'post-2', + categoryIds, + content: 'More things to do …', + }), + factory.create('Post', { + author: currentUser, + id: 'post-3', + categoryIds, + content: 'I am at school …', + }), + ]) + await Promise.all([ + factory.create('Comment', { + author: currentUser, + id: 'abusive-comment-1', + postId: 'post-1', + }), + ]) + authenticatedUser = await currentUser.toJson() + await Promise.all([ + mutate({ + mutation: reportMutation, + variables: { + resourceId: 'abusive-post-1', reasonCategory: 'other', reasonDescription: 'This comment is bigoted', - submitter: expect.objectContaining({ - id: 'user1', - }), - type: 'Post', - user: null, - post: expect.objectContaining({ - id: 'p1', - }), - comment: null, - }), - expect.objectContaining({ - createdAt: expect.any(String), + }, + }), + mutate({ + mutation: reportMutation, + variables: { + resourceId: 'abusive-comment-1', reasonCategory: 'discrimination_etc', reasonDescription: 'This post is bigoted', - submitter: expect.objectContaining({ - id: 'user1', - }), - type: 'Comment', - user: null, - post: null, - comment: expect.objectContaining({ - id: 'c1', - }), - }), - ]), - } + }, + }), + mutate({ + mutation: reportMutation, + variables: { + resourceId: 'abusive-user-1', + reasonCategory: 'doxing', + reasonDescription: 'This user is harassing me with bigoted remarks', + }, + }), + ]) + authenticatedUser = null + }) + describe('unauthenticated', () => { + it('throws authorization error', async () => { + authenticatedUser = null + expect(query({ query: reportsQuery })).resolves.toMatchObject({ + data: { reports: null }, + errors: [{ message: 'Not Authorised!' }], + }) + }) + }) + describe('authenticated', () => { + it('role "user" gets no reports', async () => { + authenticatedUser = await currentUser.toJson() + expect(query({ query: reportsQuery })).resolves.toMatchObject({ + data: { reports: null }, + errors: [{ message: 'Not Authorised!' }], + }) + }) - authenticatedUser = await moderator.toJson() - const { data } = await query({ query: reportsQuery }) - expect(data).toEqual(expected) + it('role "moderator" gets reports', async () => { + const expected = { + // to check 'orderBy: createdAt_desc' is not possible here, because 'createdAt' does not differ + reports: expect.arrayContaining([ + expect.objectContaining({ + createdAt: expect.any(String), + reasonCategory: 'doxing', + reasonDescription: 'This user is harassing me with bigoted remarks', + submitter: expect.objectContaining({ + id: 'current-user-id', + }), + type: 'User', + user: expect.objectContaining({ + id: 'abusive-user-1', + }), + post: null, + comment: null, + }), + expect.objectContaining({ + createdAt: expect.any(String), + reasonCategory: 'other', + reasonDescription: 'This comment is bigoted', + submitter: expect.objectContaining({ + id: 'current-user-id', + }), + type: 'Post', + user: null, + post: expect.objectContaining({ + id: 'abusive-post-1', + }), + comment: null, + }), + expect.objectContaining({ + createdAt: expect.any(String), + reasonCategory: 'discrimination_etc', + reasonDescription: 'This post is bigoted', + submitter: expect.objectContaining({ + id: 'current-user-id', + }), + type: 'Comment', + user: null, + post: null, + comment: expect.objectContaining({ + id: 'abusive-comment-1', + }), + }), + ]), + } + authenticatedUser = await moderator.toJson() + const { data } = await query({ query: reportsQuery }) + expect(data).toEqual(expected) + }) }) }) })