From 7ec608691582a0fd4326f520971beb2ea9aa3758 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wolfgang=20Hu=C3=9F?= Date: Fri, 2 Sep 2022 08:10:46 +0200 Subject: [PATCH] Test 'UpdateGroup' in general resolver and 'Group' resolver with 'id' --- backend/src/db/graphql/groups.js | 35 +-- backend/src/schema/resolvers/groups.js | 27 ++- backend/src/schema/resolvers/groups.spec.js | 239 +++++++++++++++----- backend/src/schema/types/type/Group.gql | 16 +- 4 files changed, 231 insertions(+), 86 deletions(-) diff --git a/backend/src/db/graphql/groups.js b/backend/src/db/graphql/groups.js index 277ae356f..fe3153fa2 100644 --- a/backend/src/db/graphql/groups.js +++ b/backend/src/db/graphql/groups.js @@ -38,14 +38,15 @@ export const createGroupMutation = gql` description groupType actionRadius - # categories { - # id - # slug - # name - # icon - # } - # avatar - # locationName + categories { + # test this as result + id + slug + name + icon + } + # avatar # test this as result + # locationName # test this as result myRole } } @@ -85,14 +86,15 @@ export const updateGroupMutation = gql` description groupType actionRadius - # categories { - # id - # slug - # name - # icon - # } - # avatar - # locationName + categories { + # test this as result + id + slug + name + icon + } + # avatar # test this as result + # locationName # test this as result myRole } } @@ -166,6 +168,7 @@ export const groupQuery = gql` actionRadius myRole categories { + # test this as result id slug name diff --git a/backend/src/schema/resolvers/groups.js b/backend/src/schema/resolvers/groups.js index c8a59f0cb..2a3f2fc11 100644 --- a/backend/src/schema/resolvers/groups.js +++ b/backend/src/schema/resolvers/groups.js @@ -9,25 +9,26 @@ import Resolver from './helpers/Resolver' export default { Query: { Group: async (_object, params, context, _resolveInfo) => { - const { isMember } = params + const { id: groupId, isMember } = params const session = context.driver.session() const readTxResultPromise = session.readTransaction(async (txc) => { + const groupIdCypher = groupId ? ` {id: "${groupId}"}` : '' let groupCypher if (isMember === true) { groupCypher = ` - MATCH (:User {id: $userId})-[membership:MEMBER_OF]->(group:Group) + MATCH (:User {id: $userId})-[membership:MEMBER_OF]->(group:Group${groupIdCypher}) RETURN group {.*, myRole: membership.role} ` } else { if (isMember === false) { groupCypher = ` - MATCH (group:Group) + MATCH (group:Group${groupIdCypher}) WHERE NOT (:User {id: $userId})-[:MEMBER_OF]->(group) RETURN group {.*, myRole: NULL} ` } else { groupCypher = ` - MATCH (group:Group) + MATCH (group:Group${groupIdCypher}) OPTIONAL MATCH (:User {id: $userId})-[membership:MEMBER_OF]->(group) RETURN group {.*, myRole: membership.role} ` @@ -153,6 +154,16 @@ export default { throw new UserInputError('Description too short!') } const session = context.driver.session() + if (CONFIG.CATEGORIES_ACTIVE && categoryIds && categoryIds.length) { + const cypherDeletePreviousRelations = ` + MATCH (group:Group {id: $groupId})-[previousRelations:CATEGORIZED]->(category:Category) + DELETE previousRelations + RETURN group, category + ` + await session.writeTransaction((transaction) => { + return transaction.run(cypherDeletePreviousRelations, { groupId }) + }) + } const writeTxResultPromise = session.writeTransaction(async (transaction) => { let updateGroupCypher = ` MATCH (group:Group {id: $groupId}) @@ -161,14 +172,6 @@ export default { WITH group ` if (CONFIG.CATEGORIES_ACTIVE && categoryIds && categoryIds.length) { - const cypherDeletePreviousRelations = ` - MATCH (group:Group {id: $groupId})-[previousRelations:CATEGORIZED]->(category:Category) - DELETE previousRelations - RETURN group, category - ` - await session.writeTransaction((transaction) => { - return transaction.run(cypherDeletePreviousRelations, { groupId }) - }) updateGroupCypher += ` UNWIND $categoryIds AS categoryId MATCH (category:Category {id: categoryId}) diff --git a/backend/src/schema/resolvers/groups.spec.js b/backend/src/schema/resolvers/groups.spec.js index ddf003659..2616cd606 100644 --- a/backend/src/schema/resolvers/groups.spec.js +++ b/backend/src/schema/resolvers/groups.spec.js @@ -107,6 +107,8 @@ describe('in mode', () => { groupType: 'public', actionRadius: 'regional', categoryIds, + // avatar, // test this as result + // locationName, // test this as result } }) @@ -183,13 +185,50 @@ describe('in mode', () => { CONFIG.CATEGORIES_ACTIVE = true }) - describe('not even one', () => { - it('throws error: "Too view categories!"', async () => { - const { errors } = await mutate({ - mutation: createGroupMutation, - variables: { ...variables, categoryIds: null }, + describe('with matching amount of categories', () => { + it('has new categories', async () => { + await expect( + mutate({ + mutation: createGroupMutation, + variables: { + ...variables, + categoryIds: ['cat4', 'cat27'], + }, + }), + ).resolves.toMatchObject({ + data: { + CreateGroup: { + categories: expect.arrayContaining([ + expect.objectContaining({ id: 'cat4' }), + expect.objectContaining({ id: 'cat27' }), + ]), + myRole: 'owner', + }, + }, + errors: undefined, + }) + }) + }) + + describe('not even one', () => { + describe('by "categoryIds: null"', () => { + it('throws error: "Too view categories!"', async () => { + const { errors } = await mutate({ + mutation: createGroupMutation, + variables: { ...variables, categoryIds: null }, + }) + expect(errors[0]).toHaveProperty('message', 'Too view categories!') + }) + }) + + describe('by "categoryIds: []"', () => { + it('throws error: "Too view categories!"', async () => { + const { errors } = await mutate({ + mutation: createGroupMutation, + variables: { ...variables, categoryIds: [] }, + }) + expect(errors[0]).toHaveProperty('message', 'Too view categories!') }) - expect(errors[0]).toHaveProperty('message', 'Too view categories!') }) }) @@ -290,6 +329,25 @@ describe('in mode', () => { }) }) + describe("id = 'my-group'", () => { + it('finds only the group with this id', async () => { + await expect( + query({ query: groupQuery, variables: { id: 'my-group' } }), + ).resolves.toMatchObject({ + data: { + Group: [ + expect.objectContaining({ + id: 'my-group', + slug: 'the-best-group', + myRole: 'owner', + }), + ], + }, + errors: undefined, + }) + }) + }) + describe('isMember = true', () => { it('finds only groups where user is member', async () => { await expect( @@ -2055,7 +2113,7 @@ describe('in mode', () => { }) describe('unauthenticated', () => { - it.only('throws authorization error', async () => { + it('throws authorization error', async () => { const { errors } = await mutate({ query: updateGroupMutation, variables: { @@ -2110,64 +2168,141 @@ describe('in mode', () => { }) }) - describe('query groups', () => { - describe('without any filters', () => { - it('finds all groups', async () => { - await expect(query({ query: groupQuery, variables: {} })).resolves.toMatchObject({ + describe('change group settings', () => { + describe('as owner', () => { + beforeEach(async () => { + authenticatedUser = await user.toJson() + }) + + it('has set the settings', async () => { + await expect( + mutate({ + mutation: updateGroupMutation, + variables: { + id: 'my-group', + name: 'The New Group For Our Coutry', + about: 'We will change the land!', + description: 'Some country relevant description' + descriptionAdditional100, + actionRadius: 'national', + // avatar, // test this as result + // locationName, // test this as result + }, + }), + ).resolves.toMatchObject({ data: { - Group: expect.arrayContaining([ - expect.objectContaining({ - id: 'my-group', - slug: 'the-best-group', - myRole: 'owner', - }), - expect.objectContaining({ - id: 'others-group', - slug: 'uninteresting-group', - myRole: null, - }), - ]), + UpdateGroup: { + id: 'my-group', + name: 'The New Group For Our Coutry', + slug: 'the-new-group-for-our-coutry', // changing the slug is tested in the slugifyMiddleware + about: 'We will change the land!', + description: 'Some country relevant description' + descriptionAdditional100, + actionRadius: 'national', + // avatar, // test this as result + // locationName, // test this as result + myRole: 'owner', + }, }, errors: undefined, }) }) - }) - describe('isMember = true', () => { - it('finds only groups where user is member', async () => { - await expect( - query({ query: groupQuery, variables: { isMember: true } }), - ).resolves.toMatchObject({ - data: { - Group: [ - { - id: 'my-group', - slug: 'the-best-group', - myRole: 'owner', + describe('description', () => { + describe('length without HTML', () => { + describe('less then 100 chars', () => { + it('throws error: "Too view categories!"', async () => { + const { errors } = await mutate({ + mutation: updateGroupMutation, + variables: { + id: 'my-group', + description: + '0123456789' + + '0123456789', + }, + }) + expect(errors[0]).toHaveProperty('message', 'Description too short!') + }) + }) + }) + }) + + describe('categories', () => { + beforeEach(async () => { + CONFIG.CATEGORIES_ACTIVE = true + }) + + describe('with matching amount of categories', () => { + it('has new categories', async () => { + await expect( + mutate({ + mutation: updateGroupMutation, + variables: { + id: 'my-group', + categoryIds: ['cat4', 'cat27'], + }, + }), + ).resolves.toMatchObject({ + data: { + UpdateGroup: { + id: 'my-group', + categories: expect.arrayContaining([ + expect.objectContaining({ id: 'cat4' }), + expect.objectContaining({ id: 'cat27' }), + ]), + myRole: 'owner', + }, }, - ], - }, - errors: undefined, + errors: undefined, + }) + }) + }) + + describe('not even one', () => { + describe('by "categoryIds: []"', () => { + it('throws error: "Too view categories!"', async () => { + const { errors } = await mutate({ + mutation: updateGroupMutation, + variables: { + id: 'my-group', + categoryIds: [], + }, + }) + expect(errors[0]).toHaveProperty('message', 'Too view categories!') + }) + }) + }) + + describe('four', () => { + it('throws error: "Too many categories!"', async () => { + const { errors } = await mutate({ + mutation: updateGroupMutation, + variables: { + id: 'my-group', + categoryIds: ['cat9', 'cat4', 'cat15', 'cat27'], + }, + }) + expect(errors[0]).toHaveProperty('message', 'Too many categories!') + }) }) }) }) - describe('isMember = false', () => { - it('finds only groups where user is not(!) member', async () => { - await expect( - query({ query: groupQuery, variables: { isMember: false } }), - ).resolves.toMatchObject({ - data: { - Group: expect.arrayContaining([ - expect.objectContaining({ - id: 'others-group', - slug: 'uninteresting-group', - myRole: null, - }), - ]), + describe('as no(!) owner', () => { + it('throws authorization error', async () => { + authenticatedUser = await otherUser.toJson() + const { errors } = await mutate({ + mutation: updateGroupMutation, + variables: { + id: 'my-group', + name: 'The New Group For Our Coutry', + about: 'We will change the land!', + description: 'Some country relevant description' + descriptionAdditional100, + actionRadius: 'national', + categoryIds: ['cat4', 'cat27'], // test this as result + // avatar, // test this as result + // locationName, // test this as result }, - errors: undefined, }) + expect(errors[0]).toHaveProperty('message', 'Not Authorized!') }) }) }) diff --git a/backend/src/schema/types/type/Group.gql b/backend/src/schema/types/type/Group.gql index 70f9ce16f..4dc466e53 100644 --- a/backend/src/schema/types/type/Group.gql +++ b/backend/src/schema/types/type/Group.gql @@ -67,6 +67,9 @@ type Query { updatedAt: String about: String description: String + # groupType: GroupType # test this + # actionRadius: GroupActionRadius # test this + # avatar: ImageInput # test this locationName: String first: Int offset: Int @@ -93,13 +96,13 @@ type Mutation { id: ID name: String! slug: String - avatar: ImageInput about: String description: String! groupType: GroupType! actionRadius: GroupActionRadius! - categoryIds: [ID] - locationName: String + categoryIds: [ID] # test this as result + avatar: ImageInput # test this as result + locationName: String # test this as result ): Group UpdateGroup( @@ -108,10 +111,11 @@ type Mutation { slug: String about: String description: String + # groupType: GroupType # is not possible at the moment and has to be discussed. may be in the stronger direction: public → closed → hidden actionRadius: GroupActionRadius - categoryIds: [ID] - avatar: ImageInput - locationName: String + categoryIds: [ID] # test this as result + avatar: ImageInput # test this as result + locationName: String # test this as result ): Group # DeleteGroup(id: ID!): Group