diff --git a/src/express/auth/permissions.ts b/src/express/auth/permissions.ts index 2daf1a6..7e27636 100644 --- a/src/express/auth/permissions.ts +++ b/src/express/auth/permissions.ts @@ -26,8 +26,10 @@ export const permissions = shield( Query: { // procedures: isLoggedin, // activityIndex: isLoggedin, + // Procedure notificationSettings: isLoggedin, notifiedProcedures: isLoggedin, + // Vote votes: isLoggedin, votedProcedures: isVerified, }, diff --git a/src/graphql/resolvers/Activity.integ.ts b/src/graphql/resolvers/Activity.integ.ts index ddf9528..9295f28 100644 --- a/src/graphql/resolvers/Activity.integ.ts +++ b/src/graphql/resolvers/Activity.integ.ts @@ -17,6 +17,97 @@ import config from '../../config'; const GRAPHQL_API_URL = process.env.GRAPHQL_API_URL || 'http://localhost:3000'; describe('Activity Resolvers', () => { + describe('Queries', () => { + describe('activityIndex', () => { + let procedure: IProcedure; + let user: User; + + beforeAll(async () => { + await connectDB(config.DB_URL, { debug: false }); + + // create tmp procedure + procedure = await ProcedureModel.create({ + procedureId: '0000010', + title: 'tmp procedure for activityIndex test', + period: 1, + type: 'Antrag', + voteResults: { + yes: 0, + no: 0, + abstination: 0, + }, + }); + + user = await UserModel.create({ + verified: true, + }); + + // create tmp activities + await ActivityModel.create([ + { + procedure, + kind: 'Phone', + actor: user, + }, + ]); + }); + + afterAll(async () => { + await Promise.all([ + ActivityModel.deleteOne({ procedure: procedure }), + procedure.remove(), + user.remove(), + ]); + + await disconnectDB(); + }); + + it('fail to get activity index on non-existing procedure', async () => { + const response = await axios.post(GRAPHQL_API_URL, { + query: ` + query ActivityIndex($procedureId: String!) { + activityIndex(procedureId: $procedureId) { + activityIndex + active + } + } + `, + variables: { + procedureId: 'non-existing-procedure-id', + }, + }); + + const { data } = response.data; + + expect(data).toBeDefined(); + expect(data.activityIndex).toBeNull(); + }); + + it('get activity index', async () => { + const response = await axios.post(GRAPHQL_API_URL, { + query: ` + query ActivityIndex($procedureId: String!) { + activityIndex(procedureId: $procedureId) { + activityIndex + active + } + } + `, + variables: { + procedureId: '0000010', + }, + }); + + const { data } = response.data; + + expect(data).toBeDefined(); + expect(data.activityIndex).toBeDefined(); + expect(data.activityIndex.activityIndex).toBe(1); + expect(data.activityIndex.active).toBeTruthy(); + }); + }); + }); + describe('Mutations', () => { describe('increaseActivity', () => { const DEVICE_HASH = 'SOME_DEVICE_HASH_ACTIVITY_RESOLVER_INCREASE_ACTIVITY'; diff --git a/src/graphql/resolvers/ConferenceWeek.integ.ts b/src/graphql/resolvers/ConferenceWeek.integ.ts index d384524..80ab494 100644 --- a/src/graphql/resolvers/ConferenceWeek.integ.ts +++ b/src/graphql/resolvers/ConferenceWeek.integ.ts @@ -4,10 +4,8 @@ const GRAPHQL_API_URL = process.env.GRAPHQL_API_URL || 'http://localhost:3000'; describe('ConferenceWeek GraphQL API', () => { it('data with device', async () => { - const response = await axios.post( - GRAPHQL_API_URL, - { - query: ` + const response = await axios.post(GRAPHQL_API_URL, { + query: ` query CurrentConferenceWeek { currentConferenceWeek { start @@ -17,8 +15,7 @@ describe('ConferenceWeek GraphQL API', () => { } } `, - }, - ); + }); const { data } = response.data; diff --git a/src/graphql/resolvers/Deputy.integ.ts b/src/graphql/resolvers/Deputy.integ.ts index 7161543..a8b9f60 100644 --- a/src/graphql/resolvers/Deputy.integ.ts +++ b/src/graphql/resolvers/Deputy.integ.ts @@ -1,11 +1,49 @@ import axios from 'axios'; +import { connectDB, disconnectDB } from '../../services/mongoose'; +import config from '../../config'; +import { DeputyModel, IDeputy } from '@democracy-deutschland/democracy-common'; const GRAPHQL_API_URL = process.env.GRAPHQL_API_URL || 'http://localhost:3000'; describe('Deputy GraphQL API', () => { - it('should return data', async () => { - const response = await axios.post(GRAPHQL_API_URL, { - query: ` + describe('Queries', () => { + let deputy1: IDeputy; + let deputy2: IDeputy; + + beforeAll(async () => { + await connectDB(config.DB_URL, { debug: false }); + + // create tmp deputies + deputy1 = await DeputyModel.create({ + name: 'tmp deputy for deputy test', + party: 'CDU', + webId: '0000010', + constituency: '', + period: 19, + imgURL: 'https://example.com', + }); + + deputy2 = await DeputyModel.create({ + name: 'tmp deputy for deputy test', + party: 'CDU', + webId: '0000011', + constituency: '107', + period: 20, + imgURL: 'https://example.com', + }); + }); + + afterAll(async () => { + await deputy1.remove(); + await deputy2.remove(); + + await disconnectDB(); + }); + + describe('deputies', () => { + it('should return data', async () => { + const response = await axios.post(GRAPHQL_API_URL, { + query: ` query Deputies($limit: Int, $offset: Int, $filterTerm: String, $filterConstituency: String, $excludeIds: [String!], $period: Int) { deputies( limit: $limit @@ -27,27 +65,28 @@ describe('Deputy GraphQL API', () => { } } `, - variables: { - limit: 10, - offset: 0, - filterTerm: '', - filterConstituency: '', - excludeIds: [], - period: 19, - }, - }); + variables: { + limit: 10, + offset: 0, + filterTerm: '', + filterConstituency: '', + excludeIds: [], + period: 19, + }, + }); - const { data } = response.data; + const { data } = response.data; - expect(data).toBeDefined(); - expect(data.deputies).toBeDefined(); - expect(data.deputies.hasMore).toBeDefined(); - expect(data.deputies.data).toBeDefined(); - }); + expect(data).toBeDefined(); + expect(data.deputies).toBeDefined(); + expect(data.deputies.hasMore).toBeDefined(); + expect(data.deputies.data).toBeDefined(); + expect(data.deputies.data.length).toBeGreaterThan(0); + }); - it('deputies filtered by constituency', async () => { - const response = await axios.post(GRAPHQL_API_URL, { - query: ` + it('deputies filtered by constituency', async () => { + const response = await axios.post(GRAPHQL_API_URL, { + query: ` query GetDeputiesForNewConstituency($filterConstituency: String, $excludeIds: [String!], $period: Int) { deputies( filterConstituency: $filterConstituency @@ -60,16 +99,80 @@ describe('Deputy GraphQL API', () => { } } `, - variables: { - period: 20, - filterConstituency: '107', - }, + variables: { + period: 20, + filterConstituency: '107', + }, + }); + + const { data } = response.data; + + expect(data).toBeDefined(); + expect(data.deputies).toBeDefined(); + expect(data.deputies.data).toBeDefined(); + expect(data.deputies.data.length).toBeGreaterThan(0); + }); + }); + + describe('deputyOfConstituency', () => { + it('should return data', async () => { + const response = await axios.post(GRAPHQL_API_URL, { + query: ` + query DeputiesOfConstituency($constituency: String!, $period: Int) { + deputiesOfConstituency(constituency: $constituency, period: $period) { + _id + name + party + webId + imgURL + constituency + } + } + `, + variables: { + constituency: '107', + period: 20, + }, + }); + + const { data } = response.data; + + expect(data).toBeDefined(); + expect(data.deputiesOfConstituency).toBeDefined(); + expect(data.deputiesOfConstituency.length).toBeGreaterThan(0); + }); }); - const { data } = response.data; + describe('deputy', () => { + it('should return data', async () => { + const response = await axios.post(GRAPHQL_API_URL, { + query: ` + query Deputy($id: String!) { + deputy(id: $id) { + _id + name + party + webId + imgURL + constituency + } + } + `, + variables: { + id: deputy1.webId, + }, + }); + + const { data } = response.data; - expect(data).toBeDefined(); - expect(data.deputies).toBeDefined(); - expect(data.deputies.data).toBeDefined(); + expect(data).toBeDefined(); + expect(data.deputy).toBeDefined(); + expect(data.deputy.name).toBe(deputy1.name); + expect(data.deputy.party).toBe(deputy1.party); + expect(data.deputy.webId).toBe(deputy1.webId); + expect(data.deputy.imgURL).toBe(deputy1.imgURL); + expect(data.deputy.constituency).toBe(deputy1.constituency); + }); + }); }); }); diff --git a/src/graphql/resolvers/Device.integ.ts b/src/graphql/resolvers/Device.integ.ts index f3d5eec..8b8c0d0 100644 --- a/src/graphql/resolvers/Device.integ.ts +++ b/src/graphql/resolvers/Device.integ.ts @@ -1,5 +1,7 @@ import { + Device, DeviceModel, + Phone, PhoneModel, UserModel, VerificationModel, @@ -13,18 +15,32 @@ const GRAPHQL_API_URL = process.env.GRAPHQL_API_URL || 'http://localhost:3000'; describe('Device GraphQL API', () => { const SOME_DEVICE_HASH = 'SOME_DEVICE_HASH_DEVICE_TESTS'; + let device: Device; const PHONE_NUMBER = `+49123456789`; const xPhoneHash = crypto.createHash('sha256').update(PHONE_NUMBER).digest('hex'); const phoneHash = crypto.createHash('sha256').update(xPhoneHash).digest('hex'); + let phone: Phone; + + beforeAll(async () => { + await connectDB(config.DB_URL, { debug: false }); + + device = await DeviceModel.create({ + deviceHash: SOME_DEVICE_HASH, + }); + + phone = await PhoneModel.create({ + phoneHash, + xPhoneHash, + device, + }); + }); afterAll(async () => { await connectDB(config.DB_URL, { debug: false }); - const device = await DeviceModel.findOne({ deviceHash: SOME_DEVICE_HASH }); await UserModel.deleteMany({ device }); await device.remove(); - const phone = await PhoneModel.findOne({ phoneHash }); await phone.remove(); await VerificationModel.deleteMany({ phoneHash }); @@ -33,123 +49,127 @@ describe('Device GraphQL API', () => { }); describe('notification settings', () => { - it('get notification settings', async () => { - const response = await axios.post( - GRAPHQL_API_URL, - { - query: ` - query NotificationSettings { - notificationSettings { - enabled - conferenceWeekPushs - voteConferenceWeekPushs - voteTOP100Pushs - outcomePushs - } - } - `, - }, - { - headers: { - 'Content-Type': 'application/json', - 'x-device-hash': SOME_DEVICE_HASH, - }, - }, - ); - - const { data } = response.data; - - expect(data).toBeDefined(); - expect(data.notificationSettings).toBeDefined(); - expect(data.notificationSettings.enabled).toBeTruthy(); - expect(data.notificationSettings.conferenceWeekPushs).toBeTruthy(); - expect(data.notificationSettings.voteConferenceWeekPushs).toBeFalsy(); - expect(data.notificationSettings.voteTOP100Pushs).toBeFalsy(); - expect(data.notificationSettings.outcomePushs).toBeFalsy(); - }); + describe('Query', () => { + describe('notificationSettings', () => { + it('get notification settings', async () => { + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` + query NotificationSettings { + notificationSettings { + enabled + conferenceWeekPushs + voteConferenceWeekPushs + voteTOP100Pushs + outcomePushs + } + } + `, + }, + { + headers: { + 'Content-Type': 'application/json', + 'x-device-hash': SOME_DEVICE_HASH, + }, + }, + ); - it('update notification settings', async () => { - const response = await axios.post( - GRAPHQL_API_URL, - { - query: ` - mutation UpdateNotificationSettings($enabled: Boolean!, $conferenceWeekPushs: Boolean!, $voteConferenceWeekPushs: Boolean!, $voteTOP100Pushs: Boolean!, $outcomePushs: Boolean!) { - updateNotificationSettings(enabled: $enabled, conferenceWeekPushs: $conferenceWeekPushs, voteConferenceWeekPushs: $voteConferenceWeekPushs, voteTOP100Pushs: $voteTOP100Pushs, outcomePushs: $outcomePushs) { - enabled - conferenceWeekPushs - voteConferenceWeekPushs - voteTOP100Pushs - outcomePushs - } - } - `, - variables: { - enabled: true, - conferenceWeekPushs: true, - voteConferenceWeekPushs: true, - voteTOP100Pushs: true, - outcomePushs: true, + const { data } = response.data; + + expect(data).toBeDefined(); + expect(data.notificationSettings).toBeDefined(); + expect(data.notificationSettings.enabled).toBeTruthy(); + expect(data.notificationSettings.conferenceWeekPushs).toBeTruthy(); + expect(data.notificationSettings.voteConferenceWeekPushs).toBeFalsy(); + expect(data.notificationSettings.voteTOP100Pushs).toBeFalsy(); + expect(data.notificationSettings.outcomePushs).toBeFalsy(); + }); + }); + }); + describe('Mutation', () => { + it('update notification settings', async () => { + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` + mutation UpdateNotificationSettings($enabled: Boolean!, $conferenceWeekPushs: Boolean!, $voteConferenceWeekPushs: Boolean!, $voteTOP100Pushs: Boolean!, $outcomePushs: Boolean!) { + updateNotificationSettings(enabled: $enabled, conferenceWeekPushs: $conferenceWeekPushs, voteConferenceWeekPushs: $voteConferenceWeekPushs, voteTOP100Pushs: $voteTOP100Pushs, outcomePushs: $outcomePushs) { + enabled + conferenceWeekPushs + voteConferenceWeekPushs + voteTOP100Pushs + outcomePushs + } + } + `, + variables: { + enabled: true, + conferenceWeekPushs: true, + voteConferenceWeekPushs: true, + voteTOP100Pushs: true, + outcomePushs: true, + }, }, - }, - { - headers: { - 'Content-Type': 'application/json', - 'x-device-hash': SOME_DEVICE_HASH, + { + headers: { + 'Content-Type': 'application/json', + 'x-device-hash': SOME_DEVICE_HASH, + }, }, - }, - ); - - const { data } = response.data; - - expect(data).toBeDefined(); - expect(data.updateNotificationSettings).toBeDefined(); - expect(data.updateNotificationSettings.enabled).toBeTruthy(); - expect(data.updateNotificationSettings.conferenceWeekPushs).toBeTruthy(); - expect(data.updateNotificationSettings.voteConferenceWeekPushs).toBeTruthy(); - expect(data.updateNotificationSettings.voteTOP100Pushs).toBeTruthy(); - expect(data.updateNotificationSettings.outcomePushs).toBeTruthy(); - }); + ); - it('get updated notification settings', async () => { - const response = await axios.post( - GRAPHQL_API_URL, - { - query: ` - query NotificationSettings { - notificationSettings { - enabled - conferenceWeekPushs - voteConferenceWeekPushs - voteTOP100Pushs - outcomePushs - } - } - `, - }, - { - headers: { - 'Content-Type': 'application/json', - 'x-device-hash': SOME_DEVICE_HASH, + const { data } = response.data; + + expect(data).toBeDefined(); + expect(data.updateNotificationSettings).toBeDefined(); + expect(data.updateNotificationSettings.enabled).toBeTruthy(); + expect(data.updateNotificationSettings.conferenceWeekPushs).toBeTruthy(); + expect(data.updateNotificationSettings.voteConferenceWeekPushs).toBeTruthy(); + expect(data.updateNotificationSettings.voteTOP100Pushs).toBeTruthy(); + expect(data.updateNotificationSettings.outcomePushs).toBeTruthy(); + }); + + it('get updated notification settings', async () => { + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` + query NotificationSettings { + notificationSettings { + enabled + conferenceWeekPushs + voteConferenceWeekPushs + voteTOP100Pushs + outcomePushs + } + } + `, }, - }, - ); - - const { data } = response.data; - - expect(data).toBeDefined(); - expect(data.notificationSettings).toBeDefined(); - expect(data.notificationSettings.enabled).toBeTruthy(); - expect(data.notificationSettings.conferenceWeekPushs).toBeTruthy(); - expect(data.notificationSettings.voteConferenceWeekPushs).toBeTruthy(); - expect(data.notificationSettings.voteTOP100Pushs).toBeTruthy(); - expect(data.notificationSettings.outcomePushs).toBeTruthy(); - }); + { + headers: { + 'Content-Type': 'application/json', + 'x-device-hash': SOME_DEVICE_HASH, + }, + }, + ); - it('is not allowed to update notification settings without device hash', async () => { - const response = await axios.post( - GRAPHQL_API_URL, - { - query: ` + const { data } = response.data; + + expect(data).toBeDefined(); + expect(data.notificationSettings).toBeDefined(); + expect(data.notificationSettings.enabled).toBeTruthy(); + expect(data.notificationSettings.conferenceWeekPushs).toBeTruthy(); + expect(data.notificationSettings.voteConferenceWeekPushs).toBeTruthy(); + expect(data.notificationSettings.voteTOP100Pushs).toBeTruthy(); + expect(data.notificationSettings.outcomePushs).toBeTruthy(); + }); + + it('is not allowed to update notification settings without device hash', async () => { + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` mutation UpdateNotificationSettings($enabled: Boolean!, $conferenceWeekPushs: Boolean!, $voteConferenceWeekPushs: Boolean!, $voteTOP100Pushs: Boolean!, $outcomePushs: Boolean!) { updateNotificationSettings(enabled: $enabled, conferenceWeekPushs: $conferenceWeekPushs, voteConferenceWeekPushs: $voteConferenceWeekPushs, voteTOP100Pushs: $voteTOP100Pushs, outcomePushs: $outcomePushs) { enabled @@ -160,37 +180,39 @@ describe('Device GraphQL API', () => { } } `, - variables: { - enabled: true, - conferenceWeekPushs: true, - voteConferenceWeekPushs: true, - voteTOP100Pushs: true, - outcomePushs: true, + variables: { + enabled: true, + conferenceWeekPushs: true, + voteConferenceWeekPushs: true, + voteTOP100Pushs: true, + outcomePushs: true, + }, }, - }, - { - headers: { - 'Content-Type': 'application/json', + { + headers: { + 'Content-Type': 'application/json', + }, }, - }, - ); - - const error = response.data.errors.find((e) => e.path.includes('updateNotificationSettings')); - if (error) { - expect(error.message).toBe('Not Authorised!'); - expect(response.data.notificationSettings).toBeUndefined(); - } else { - expect(true).toBeFalsy(); - } + ); + + const error = response.data.errors.find((e) => + e.path.includes('updateNotificationSettings'), + ); + if (error) { + expect(error.message).toBe('Not Authorised!'); + expect(response.data.notificationSettings).toBeUndefined(); + } else { + expect(true).toBeFalsy(); + } + }); }); - }); - describe('verify device', () => { - it('request verification code via sms', async () => { - const response = await axios.post( - GRAPHQL_API_URL, - { - query: ` + describe('verify device', () => { + it('request verification code via sms', async () => { + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` mutation RequestSmsCode($newPhone: String!, $oldPhoneHash: String) { requestCode(newPhone: $newPhone, oldPhoneHash: $oldPhoneHash) { reason @@ -201,31 +223,31 @@ describe('Device GraphQL API', () => { } } `, - variables: { - newPhone: PHONE_NUMBER, - oldPhoneHash: '', + variables: { + newPhone: PHONE_NUMBER, + oldPhoneHash: '', + }, }, - }, - { - headers: { - 'Content-Type': 'application/json', - 'x-device-hash': SOME_DEVICE_HASH, + { + headers: { + 'Content-Type': 'application/json', + 'x-device-hash': SOME_DEVICE_HASH, + }, }, - }, - ); + ); - const { data } = response.data; + const { data } = response.data; - expect(data).toBeDefined(); - expect(data.requestCode).toBeDefined(); - expect(data.requestCode.succeeded).toBeTruthy(); - }); + expect(data).toBeDefined(); + expect(data.requestCode).toBeDefined(); + expect(data.requestCode.succeeded).toBeTruthy(); + }); - it('request fast second verification code via sms', async () => { - const response = await axios.post( - GRAPHQL_API_URL, - { - query: ` + it('request fast second verification code via sms', async () => { + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` mutation RequestSmsCode($newPhone: String!, $oldPhoneHash: String) { requestCode(newPhone: $newPhone, oldPhoneHash: $oldPhoneHash) { reason @@ -236,32 +258,32 @@ describe('Device GraphQL API', () => { } } `, - variables: { - newPhone: PHONE_NUMBER, - oldPhoneHash: '', + variables: { + newPhone: PHONE_NUMBER, + oldPhoneHash: '', + }, }, - }, - { - headers: { - 'Content-Type': 'application/json', - 'x-device-hash': SOME_DEVICE_HASH, + { + headers: { + 'Content-Type': 'application/json', + 'x-device-hash': SOME_DEVICE_HASH, + }, }, - }, - ); + ); - const { data } = response.data; + const { data } = response.data; - expect(data).toBeDefined(); - expect(data.requestCode).toBeDefined(); - expect(data.requestCode.succeeded).toBeFalsy(); - }); + expect(data).toBeDefined(); + expect(data.requestCode).toBeDefined(); + expect(data.requestCode.succeeded).toBeFalsy(); + }); - it('try to verify phone number with wrong code', async () => { - const newPhoneHash = crypto.createHash('sha256').update(PHONE_NUMBER).digest('hex'); - const response = await axios.post( - GRAPHQL_API_URL, - { - query: ` + it('try to verify phone number with wrong code', async () => { + const newPhoneHash = crypto.createHash('sha256').update(PHONE_NUMBER).digest('hex'); + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` mutation RequestVerification($code: String!, $newPhoneHash: String!, $newUser: Boolean) { requestVerification(code: $code, newPhoneHash: $newPhoneHash, newUser: $newUser) { reason @@ -269,32 +291,32 @@ describe('Device GraphQL API', () => { } } `, - variables: { - code: '123456', - newPhoneHash, + variables: { + code: '123456', + newPhoneHash, + }, }, - }, - { - headers: { - 'Content-Type': 'application/json', - 'x-device-hash': SOME_DEVICE_HASH, + { + headers: { + 'Content-Type': 'application/json', + 'x-device-hash': SOME_DEVICE_HASH, + }, }, - }, - ); + ); - const { data } = response.data; + const { data } = response.data; - expect(data).toBeDefined(); - expect(data.requestVerification).toBeDefined(); - expect(data.requestVerification.succeeded).toBeFalsy(); - }); + expect(data).toBeDefined(); + expect(data.requestVerification).toBeDefined(); + expect(data.requestVerification.succeeded).toBeFalsy(); + }); - it('verify phone number', async () => { - const newPhoneHash = crypto.createHash('sha256').update(PHONE_NUMBER).digest('hex'); - const response = await axios.post( - GRAPHQL_API_URL, - { - query: ` + it('verify phone number', async () => { + const newPhoneHash = crypto.createHash('sha256').update(PHONE_NUMBER).digest('hex'); + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` mutation RequestVerification($code: String!, $newPhoneHash: String!, $newUser: Boolean) { requestVerification(code: $code, newPhoneHash: $newPhoneHash, newUser: $newUser) { reason @@ -302,24 +324,25 @@ describe('Device GraphQL API', () => { } } `, - variables: { - code: '000000', - newPhoneHash, + variables: { + code: '000000', + newPhoneHash, + }, }, - }, - { - headers: { - 'Content-Type': 'application/json', - 'x-device-hash': SOME_DEVICE_HASH, + { + headers: { + 'Content-Type': 'application/json', + 'x-device-hash': SOME_DEVICE_HASH, + }, }, - }, - ); + ); - const { data } = response.data; + const { data } = response.data; - expect(data).toBeDefined(); - expect(data.requestVerification).toBeDefined(); - expect(data.requestVerification.succeeded).toBeTruthy(); + expect(data).toBeDefined(); + expect(data.requestVerification).toBeDefined(); + expect(data.requestVerification.succeeded).toBeTruthy(); + }); }); }); }); diff --git a/src/graphql/resolvers/Procedure.integ.ts b/src/graphql/resolvers/Procedure.integ.ts index 627990c..bcbb56c 100644 --- a/src/graphql/resolvers/Procedure.integ.ts +++ b/src/graphql/resolvers/Procedure.integ.ts @@ -16,7 +16,7 @@ import config from '../../config'; const GRAPHQL_API_URL = process.env.GRAPHQL_API_URL || 'http://localhost:3000'; describe('Resolver: Procedure', () => { - describe('votedProcedures', () => { + describe('Query', () => { const DEVICE_HASH = 'SOME_DEVICE_HASH_PROCEDURE_RESOLVER_VOTED_PROCEDURES'; const PHONE_NUMBER = `+49111111112`; const xPhoneHash = crypto.createHash('sha256').update(PHONE_NUMBER).digest('hex'); @@ -25,6 +25,7 @@ describe('Resolver: Procedure', () => { let device: Device; let phone: Phone; let user: User; + let userNotVerified: User; beforeAll(async () => { await connectDB(config.DB_URL, { debug: false }); @@ -56,40 +57,130 @@ describe('Resolver: Procedure', () => { device, phone, }); + + userNotVerified = await UserModel.create({ + verified: false, + device, + }); }); afterAll(async () => { - await Promise.all([procedure.remove(), phone.remove(), device.remove(), user.remove()]); + await Promise.all([ + procedure.remove(), + phone.remove(), + device.remove(), + user.remove(), + userNotVerified.remove(), + ]); await disconnectDB(); }); + describe('votedProcedures', () => { + it('get voted procedures with no votes', async () => { + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` + query VotedProcedures { + votedProcedures { + procedureId + } + } + `, + }, + { + headers: { + 'Content-Type': 'application/json', + 'x-device-hash': device.deviceHash, + 'x-phone-hash': xPhoneHash, + }, + }, + ); + + const { data } = response.data; - it('get voted procedures with no votes', async () => { - const response = await axios.post( - GRAPHQL_API_URL, - { - query: ` + expect(data).toBeDefined(); + expect(data.votedProcedures).toBeDefined(); + expect(data.votedProcedures.length).toStrictEqual(0); + }); + it('try to get voted procedures without phone', async () => { + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` query VotedProcedures { votedProcedures { procedureId } } `, - }, - { - headers: { - 'Content-Type': 'application/json', - 'x-device-hash': device.deviceHash, - 'x-phone-hash': xPhoneHash, }, - }, - ); + { + headers: { + 'Content-Type': 'application/json', + 'x-device-hash': device.deviceHash, + }, + }, + ); + + const { data } = response.data; + + expect(data).toBeDefined(); + expect(data).toBeNull(); + }); + }); + describe('notifiedProcedures', () => { + it('get notified procedures with no votes', async () => { + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` + query NotifiedProcedures { + notifiedProcedures { + procedureId + } + } + `, + }, + { + headers: { + 'Content-Type': 'application/json', + 'x-device-hash': device.deviceHash, + 'x-phone-hash': xPhoneHash, + }, + }, + ); + + const { data } = response.data; - const { data } = response.data; + expect(data).toBeDefined(); + expect(data.notifiedProcedures).toBeDefined(); + expect(data.notifiedProcedures.length).toStrictEqual(0); + }); + it('try to get notified procedures without device', async () => { + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` + query NotifiedProcedures { + notifiedProcedures { + procedureId + } + } + `, + }, + { + headers: { + 'Content-Type': 'application/json', + }, + }, + ); - expect(data).toBeDefined(); - expect(data.votedProcedures).toBeDefined(); - expect(data.votedProcedures.length).toStrictEqual(0); + const { data } = response.data; + + expect(data).toBeDefined(); + expect(data).toBeNull(); + }); }); }); }); diff --git a/src/graphql/resolvers/Vote.integ.ts b/src/graphql/resolvers/Vote.integ.ts new file mode 100644 index 0000000..c47b61f --- /dev/null +++ b/src/graphql/resolvers/Vote.integ.ts @@ -0,0 +1,235 @@ +import { + Device, + DeviceModel, + IProcedure, + Phone, + PhoneModel, + ProcedureModel, + User, + UserModel, + Vote, + VoteModel, +} from '@democracy-deutschland/democracy-common'; +import config from '../../config'; +import crypto from 'crypto'; +import axios from 'axios'; +import { connectDB, disconnectDB } from '../../services/mongoose'; + +const GRAPHQL_API_URL = process.env.GRAPHQL_API_URL || 'http://localhost:3000'; + +describe('Resolver: Vote', () => { + let vote: Vote; + let device: Device; + const DEVICE_HASH = 'SOME_DEVICE_HASH_VOTE'; + let procedure: IProcedure; + const PHONE_NUMBER = `+49111113112`; + const xPhoneHash = crypto.createHash('sha256').update(PHONE_NUMBER).digest('hex'); + const phoneHash = crypto.createHash('sha256').update(xPhoneHash).digest('hex'); + let phone: Phone; + let userVerified: User; + let userNotVerified: User; + + beforeAll(async () => { + await connectDB(config.DB_URL, { debug: false }); + + procedure = await ProcedureModel.create({ + procedureId: '0001101', + title: 'tmp procedure for increaseActivity test', + period: 1, + type: 'Antrag', + voteResults: { + yes: 10, + no: 20, + abstination: 30, + }, + }); + + device = await DeviceModel.create({ + deviceHash: DEVICE_HASH, + }); + + phone = await PhoneModel.create({ + phoneHash, + }); + + vote = await VoteModel.create({ + procedure: procedure, + type: 'Phone', + votes: { + general: { + yes: 1, + no: 2, + abstain: 3, + }, + cache: { + yes: 4, + no: 5, + abstain: 6, + }, + constituencies: [ + { + constituency: '107', + yes: 7, + no: 8, + abstain: 9, + }, + ], + }, + state: 'COMPLETED', + voters: [ + { + voter: phone._id, + }, + ], + }); + + userVerified = await UserModel.create({ + verified: true, + device, + phone, + }); + userNotVerified = await UserModel.create({ + verified: false, + device, + }); + }); + + afterAll(async () => { + await Promise.all([ + vote.remove(), + device.remove(), + procedure.remove(), + userVerified.remove(), + userNotVerified.remove(), + phone.remove(), + ]); + + await disconnectDB(); + }); + describe('Query', () => { + describe('votes', () => { + it('should return all votes', async () => { + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` + query Votes($procedure: ID!) { + votes(procedure: $procedure ) { + _id + voted + voteResults { + yes + no + abstination + total + } + } + } + `, + variables: { + procedure: procedure._id, + }, + }, + { + headers: { + 'x-device-hash': device.deviceHash, + 'x-phone-hash': xPhoneHash, + }, + }, + ); + + const { data } = response.data; + + expect(data).toBeDefined(); + expect(data.votes).toBeDefined(); + expect(data.votes.voted).toBeTruthy(); + expect(data.votes.voteResults).toBeDefined(); + expect(data.votes.voteResults.yes).toEqual(4); + expect(data.votes.voteResults.no).toEqual(5); + expect(data.votes.voteResults.abstination).toEqual(6); + expect(data.votes.voteResults.total).toEqual(15); + }); + + it('try to get votes without device', async () => { + const response = await axios.post(GRAPHQL_API_URL, { + query: ` + query Votes($procedure: ID!) { + votes(procedure: $procedure ) { + _id + voted + voteResults { + yes + no + abstination + total + } + } + } + `, + variables: { + procedure: procedure._id, + }, + }); + + const { data } = response.data; + + expect(data).toBeDefined(); + expect(data.votes).toBeNull(); + }); + }); + + describe('votedProcedures', () => { + it('get voted procedures', async () => { + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` + query VotedProcedures { + votedProcedures { + procedureId + } + } + `, + }, + { + headers: { + 'x-device-hash': device.deviceHash, + 'x-phone-hash': xPhoneHash, + }, + }, + ); + + const { data } = response.data; + + expect(data).toBeDefined(); + expect(data.votedProcedures).toBeDefined(); + expect(data.votedProcedures.length).toStrictEqual(1); + }); + + it('try to get voted procedures without phone', async () => { + const response = await axios.post( + GRAPHQL_API_URL, + { + query: ` + query VotedProcedures { + votedProcedures { + procedureId + } + } + `, + }, + { + headers: { + 'x-device-hash': device.deviceHash, + }, + }, + ); + + const { data } = response.data; + + expect(data).toBeDefined(); + expect(data).toBeNull(); + }); + }); + }); +}); diff --git a/src/graphql/resolvers/Vote.ts b/src/graphql/resolvers/Vote.ts index 592a92e..efa0a95 100644 --- a/src/graphql/resolvers/Vote.ts +++ b/src/graphql/resolvers/Vote.ts @@ -25,6 +25,7 @@ const queryVotes = async ( }, ) => { logger.graphql('Vote.query.votes'); + // Has user voted? const voted = await VoteModel.findOne({ procedure, @@ -34,12 +35,14 @@ const queryVotes = async ( }, }); + const procedureId = Types.ObjectId.isValid(procedure) ? Types.ObjectId(procedure) : null; + // Find global result(cache), not including constituencies const votesGlobal = await VoteModel.aggregate([ // Find Procedure, including type; results in up to two objects for state { $match: { - procedure, + procedure: procedureId, type: CONFIG.SMS_VERIFICATION ? 'Phone' : 'Device', }, },