diff --git a/package.json b/package.json index c100f258..62e6384d 100644 --- a/package.json +++ b/package.json @@ -34,7 +34,8 @@ "exclude": [ "dist", "coverage", - "src/seeders" + "src/seeders", + "src/utils" ] }, "repository": { diff --git a/src/test/attendance.spec.ts b/src/test/attendance.spec.ts new file mode 100644 index 00000000..b23efc5d --- /dev/null +++ b/src/test/attendance.spec.ts @@ -0,0 +1,373 @@ +import { ApolloServer } from '@apollo/server' +import gql from 'graphql-tag' +import { expect } from 'chai' +import { resolvers, typeDefs } from '../index' +import { PubSub } from 'graphql-subscriptions' + +// Queries and Mutations +const GET_TEAM_ATTENDANCE_QUERY = gql` + query GetTeamAttendance($team: String!) { + getTeamAttendance(team: $team) { + id + cohort { + id + name + } + phase { + id + name + } + teams { + team { + id + name + } + trainees { + trainee { + id + firstName + lastName + } + status { + day + score + } + } + } + } + } +` + +const GET_TRAINEE_ATTENDANCE_BY_ID_QUERY = gql` + query GetTraineeAttendanceByID($traineeEmail: String!) { + getTraineeAttendanceByID(traineeEmail: $traineeEmail) { + weekNumber + traineeAttendance { + day + score + } + } + } +` + +const GET_ATTENDANCE_STATS_QUERY = gql` + query GetAttendanceStats { + getAttendanceStats { + week + traineesStatistics { + traineeId + attendancePerc + } + } + } +` + +const RECORD_ATTENDANCE_MUTATION = gql` + mutation RecordAttendance( + $week: String! + $team: String! + $date: String + $orgToken: String! + $trainees: [TraineeAttendanceInput!]! + ) { + recordAttendance( + week: $week + team: $team + date: $date + orgToken: $orgToken + trainees: $trainees + ) { + team { + id + name + } + trainees { + trainee { + id + firstName + lastName + } + status { + day + score + } + } + } + } +` + +const UPDATE_ATTENDANCE_MUTATION = gql` + mutation UpdateAttendance( + $week: String! + $team: String! + $orgToken: String! + $trainees: [TraineeAttendanceInput!]! + $phase: String! + ) { + updateAttendance( + week: $week + team: $team + orgToken: $orgToken + trainees: $trainees + phase: $phase + ) { + teams { + team { + id + name + } + trainees { + trainee { + id + firstName + lastName + } + status { + day + score + } + } + } + } + } +` + +const DELETE_ATTENDANCE_MUTATION = gql` + mutation DeleteAttendance($week: String!, $day: String!, $team: String!) { + deleteAttendance(week: $week, day: $day, team: $team) { + teams { + team { + id + name + } + } + } + } +` + +describe('Attendance Resolvers', () => { + let testServer: ApolloServer + let pubsub: PubSub + + beforeEach(() => { + pubsub = new PubSub() + + testServer = new ApolloServer({ + typeDefs, + resolvers, + }) + }) + + it('should fetch team attendance', async () => { + const result = await testServer.executeOperation({ + query: GET_TEAM_ATTENDANCE_QUERY, + variables: { team: 'someTeamId' }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data?.getTeamAttendance).to.exist + }) + + it('should fetch trainee attendance by ID', async () => { + const result = await testServer.executeOperation({ + query: GET_TRAINEE_ATTENDANCE_BY_ID_QUERY, + variables: { traineeEmail: 'trainee@example.com' }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data?.getTraineeAttendanceByID).to.exist + }) + + it('should fetch attendance stats', async () => { + const result = await testServer.executeOperation({ + query: GET_ATTENDANCE_STATS_QUERY, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data?.getAttendanceStats).to.exist + }) + + it('should record attendance', async () => { + const result = await testServer.executeOperation({ + query: RECORD_ATTENDANCE_MUTATION, + variables: { + week: 'Week 1', + team: 'someTeamId', + date: '2024-10-09', + orgToken: 'someOrgToken', + trainees: [ + { + trainee: 'traineeId1', + status: { + day: 'mon', + score: '1', + }, + }, + ], + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data?.recordAttendance).to.exist + }) + + it('should update attendance', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_ATTENDANCE_MUTATION, + variables: { + week: 'Week 1', + team: 'someTeamId', + orgToken: 'someOrgToken', + phase: 'somePhaseId', + trainees: [ + { + trainee: 'traineeId1', + status: { + day: 'mon', + score: '1', + }, + }, + ], + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data?.updateAttendance).to.exist + }) + + it('should delete attendance', async () => { + const result = await testServer.executeOperation({ + query: DELETE_ATTENDANCE_MUTATION, + variables: { + week: 'Week 1', + day: 'mon', + team: 'someTeamId', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data?.deleteAttendance).to.exist + }) +}) + +describe('Attendance Resolvers Edge Cases', () => { + let testServer: ApolloServer + let pubsub: PubSub + + beforeEach(() => { + pubsub = new PubSub() + + testServer = new ApolloServer({ + typeDefs, + resolvers, + }) + }) + + it('should return null or error for invalid team ID in getTeamAttendance', async () => { + const result = await testServer.executeOperation({ + query: GET_TEAM_ATTENDANCE_QUERY, + variables: { team: 'invalidTeamId' }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.errors).to.exist + }) + + it('should return error for missing orgToken in recordAttendance', async () => { + const result = await testServer.executeOperation({ + query: RECORD_ATTENDANCE_MUTATION, + variables: { + week: 'Week 1', + team: 'someTeamId', + date: '2024-10-09', + trainees: [ + { + trainee: 'traineeId1', + status: { + day: 'mon', + score: '1', + }, + }, + ], + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.errors).to.exist // Expect an error due to missing orgToken + }) + + it('should return error for invalid trainee data in recordAttendance', async () => { + const result = await testServer.executeOperation({ + query: RECORD_ATTENDANCE_MUTATION, + variables: { + week: 'Week 1', + team: 'someTeamId', + date: '2024-10-09', + orgToken: 'someOrgToken', + trainees: [ + { + trainee: '', + status: { + day: 'mon', + score: '1', + }, + }, + ], + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.errors).to.exist + }) + + it('should return error for missing week in updateAttendance', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_ATTENDANCE_MUTATION, + variables: { + team: 'someTeamId', + orgToken: 'someOrgToken', + phase: 'somePhaseId', + trainees: [ + { + trainee: 'traineeId1', + status: { + day: 'mon', + score: '1', + }, + }, + ], + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.errors).to.exist // Expect an error due to missing week + }) + + it('should return error for invalid week in deleteAttendance', async () => { + const result = await testServer.executeOperation({ + query: DELETE_ATTENDANCE_MUTATION, + variables: { + week: 'invalidWeek', + day: 'mon', + team: 'someTeamId', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.errors).to.exist + }) + + it('should return error for non-existent team in deleteAttendance', async () => { + const result = await testServer.executeOperation({ + query: DELETE_ATTENDANCE_MUTATION, + variables: { + week: 'Week 1', + day: 'mon', + team: 'nonExistentTeamId', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.errors).to.exist + }) +}) diff --git a/src/test/cohorts.spec.ts b/src/test/cohorts.spec.ts new file mode 100644 index 00000000..3330be40 --- /dev/null +++ b/src/test/cohorts.spec.ts @@ -0,0 +1,161 @@ +import { ApolloServer } from '@apollo/server' +import gql from 'graphql-tag' +import { expect } from 'chai' +import { resolvers, typeDefs } from '../index' +import { PubSub } from 'graphql-subscriptions' + +const GET_ALL_COHORTS_QUERY = gql` + query GetAllCohorts($orgToken: String!) { + getAllCohorts(orgToken: $orgToken) { + id + name + startDate + endDate + coordinator { + id + email + } + program { + id + name + } + phase { + id + name + } + } + } +` + +const ADD_COHORT_MUTATION = gql` + mutation AddCohort( + $name: String! + $phaseName: String! + $coordinatorEmail: String! + $programName: String! + $startDate: Date! + $endDate: Date + $orgToken: String! + ) { + addCohort( + name: $name + phaseName: $phaseName + coordinatorEmail: $coordinatorEmail + programName: $programName + startDate: $startDate + endDate: $endDate + orgToken: $orgToken + ) { + id + name + } + } +` + +const UPDATE_COHORT_MUTATION = gql` + mutation UpdateCohort( + $id: ID! + $name: String + $phaseName: String + $coordinatorEmail: String + $programName: String + $startDate: Date + $endDate: Date + $orgToken: String! + ) { + updateCohort( + id: $id + name: $name + phaseName: $phaseName + coordinatorEmail: $coordinatorEmail + programName: $programName + startDate: $startDate + endDate: $endDate + orgToken: $orgToken + ) { + id + name + } + } +` + +const DELETE_COHORT_MUTATION = gql` + mutation DeleteCohort($id: ID!, $orgToken: String!) { + deleteCohort(id: $id, orgToken: $orgToken) { + id + name + } + } +` + +describe('Cohort Resolvers', () => { + let testServer: ApolloServer + let pubsub: PubSub + + beforeEach(() => { + pubsub = new PubSub() + + testServer = new ApolloServer({ + typeDefs, + resolvers, + }) + }) + + it('should fetch all cohorts', async () => { + const result = await testServer.executeOperation({ + query: GET_ALL_COHORTS_QUERY, + variables: { + orgToken: 'validOrgToken', + }, + }) + + expect(result.body.kind).to.equal('single') + }) + + it('should add a new cohort', async () => { + const result = await testServer.executeOperation({ + query: ADD_COHORT_MUTATION, + variables: { + name: 'Test Cohort', + phaseName: 'Test Phase', + coordinatorEmail: 'test@coordinator.com', + programName: 'Test Program', + startDate: new Date(), + endDate: new Date(), + orgToken: 'validOrgToken', + }, + }) + + expect(result.body.kind).to.equal('single') + }) + + it('should update a cohort', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_COHORT_MUTATION, + variables: { + id: 'someCohortId', + name: 'Updated Test Cohort', + phaseName: 'Updated Test Phase', + coordinatorEmail: 'updated@coordinator.com', + programName: 'Updated Test Program', + startDate: new Date(), + endDate: new Date(), + orgToken: 'validOrgToken', + }, + }) + + expect(result.body.kind).to.equal('single') + }) + + it('should delete a cohort', async () => { + const result = await testServer.executeOperation({ + query: DELETE_COHORT_MUTATION, + variables: { + id: 'someCohortId', + orgToken: 'validOrgToken', + }, + }) + + expect(result.body.kind).to.equal('single') + }) +}) diff --git a/src/test/coordinator.spec.ts b/src/test/coordinator.spec.ts new file mode 100644 index 00000000..c1fefad9 --- /dev/null +++ b/src/test/coordinator.spec.ts @@ -0,0 +1,178 @@ +import { ApolloServer } from '@apollo/server' +import gql from 'graphql-tag' +import { expect } from 'chai' +import { resolvers, typeDefs } from '../index' + +const GET_COORDINATOR_BY_ID_QUERY = gql` + query GetCoordinatorById($id: ID!) { + getCoordinatorById(id: $id) { + id + name + email + } + } +` + +const GET_ALL_COORDINATORS_QUERY = gql` + query GetAllCoordinators { + getAllCoordinators { + id + name + email + } + } +` + +const CREATE_COORDINATOR_MUTATION = gql` + mutation CreateCoordinator($name: String!, $email: String!, $orgId: ID!) { + createCoordinator(name: $name, email: $email, orgId: $orgId) { + id + name + email + } + } +` + +const UPDATE_COORDINATOR_MUTATION = gql` + mutation UpdateCoordinator($id: ID!, $name: String, $email: String) { + updateCoordinator(id: $id, name: $name, email: $email) { + id + name + email + } + } +` + +const DELETE_COORDINATOR_MUTATION = gql` + mutation DeleteCoordinator($id: ID!) { + deleteCoordinator(id: $id) { + id + name + } + } +` + +describe('Coordinator Resolvers', () => { + let testServer: ApolloServer + + beforeEach(() => { + testServer = new ApolloServer({ + typeDefs, + resolvers, + }) + }) + + it('should fetch a coordinator by ID', async () => { + const result = await testServer.executeOperation({ + query: GET_COORDINATOR_BY_ID_QUERY, + variables: { id: 'someCoordinatorId' }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data?.getCoordinatorById).to.have.property('id') + }) + + it('should return error for fetching non-existent coordinator by ID', async () => { + const result = await testServer.executeOperation({ + query: GET_COORDINATOR_BY_ID_QUERY, + variables: { id: 'invalidCoordinatorId' }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.errors).to.exist + }) + + it('should fetch all coordinators', async () => { + const result = await testServer.executeOperation({ + query: GET_ALL_COORDINATORS_QUERY, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data?.getAllCoordinators).to.be.an('array') + }) + + it('should return an empty list if no coordinators exist', async () => { + const result = await testServer.executeOperation({ + query: GET_ALL_COORDINATORS_QUERY, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data?.getAllCoordinators).to.be.an('array').that.is.empty + }) + + it('should create a new coordinator', async () => { + const result = await testServer.executeOperation({ + query: CREATE_COORDINATOR_MUTATION, + variables: { + name: 'Test Coordinator', + email: 'test@example.com', + orgId: 'someOrgId', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data?.createCoordinator).to.have.property('id') + }) + + it('should fail to create coordinator with invalid email', async () => { + const result = await testServer.executeOperation({ + query: CREATE_COORDINATOR_MUTATION, + variables: { + name: 'Test Coordinator', + email: 'invalid-email', + orgId: 'someOrgId', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.errors).to.exist + }) + + it('should update a coordinator', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_COORDINATOR_MUTATION, + variables: { + id: 'someCoordinatorId', + name: 'Updated Coordinator', + email: 'updated@example.com', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data?.updateCoordinator).to.have.property('id') + }) + + it('should return error when updating non-existent coordinator', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_COORDINATOR_MUTATION, + variables: { + id: 'invalidCoordinatorId', + name: 'Updated Coordinator', + email: 'updated@example.com', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.errors).to.exist + }) + + it('should delete a coordinator', async () => { + const result = await testServer.executeOperation({ + query: DELETE_COORDINATOR_MUTATION, + variables: { id: 'someCoordinatorId' }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data?.deleteCoordinator).to.have.property('id') + }) + + it('should return error when deleting non-existent coordinator', async () => { + const result = await testServer.executeOperation({ + query: DELETE_COORDINATOR_MUTATION, + variables: { id: 'invalidCoordinatorId' }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.errors).to.exist + }) +}) diff --git a/src/test/createRating.spec.ts b/src/test/createRating.spec.ts new file mode 100644 index 00000000..b66d008b --- /dev/null +++ b/src/test/createRating.spec.ts @@ -0,0 +1,224 @@ +import { ApolloServer } from '@apollo/server'; +import gql from 'graphql-tag'; +import { expect } from 'chai'; +import { resolvers, typeDefs } from '../index'; +import { PubSub } from 'graphql-subscriptions'; + +const GET_RATING_SYSTEMS_QUERY = gql` + query GetRatingSystems($orgToken: String!) { + getRatingSystems(orgToken: $orgToken) { + id + name + grade + description + percentage + organization + } + } +`; + +const GET_RATING_SYSTEM_QUERY = gql` + query GetRatingSystem($id: ID!) { + getRatingSystem(id: $id) { + id + name + grade + description + percentage + } + } +`; + +const GET_DEFAULT_GRADING_QUERY = gql` + query GetDefaultGrading { + getDefaultGrading { + id + name + grade + description + percentage + defaultGrading + } + } +`; + +const CREATE_RATING_SYSTEM_MUTATION = gql` + mutation CreateRatingSystem( + $name: String! + $grade: String! + $description: String + $percentage: Int! + $orgToken: String! + ) { + createRatingSystem( + name: $name + grade: $grade + description: $description + percentage: $percentage + orgToken: $orgToken + ) { + id + name + grade + description + percentage + } + } +`; + +const MAKE_RATING_DEFAULT_MUTATION = gql` + mutation MakeRatingDefault($id: ID!) { + makeRatingdefault(id: $id) + } +`; + +const DELETE_RATING_SYSTEM_MUTATION = gql` + mutation DeleteRatingSystem($id: ID!) { + deleteRatingSystem(id: $id) + } +`; + +describe('Rating System Resolvers', () => { + let testServer: ApolloServer; + let pubsub: PubSub; + + beforeEach(() => { + pubsub = new PubSub(); + + testServer = new ApolloServer({ + typeDefs, + resolvers, + }); + }); + + it('should fetch all rating systems for a given organization', async () => { + const result = await testServer.executeOperation({ + query: GET_RATING_SYSTEMS_QUERY, + variables: { orgToken: 'someOrgToken' }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should fetch a single rating system by ID', async () => { + const result = await testServer.executeOperation({ + query: GET_RATING_SYSTEM_QUERY, + variables: { id: 'someRatingSystemId' }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should fetch the default grading system', async () => { + const result = await testServer.executeOperation({ + query: GET_DEFAULT_GRADING_QUERY, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should create a new rating system', async () => { + const result = await testServer.executeOperation({ + query: CREATE_RATING_SYSTEM_MUTATION, + variables: { + name: 'Test Rating', + grade: 'A', + description: 'A test rating system', + percentage: 90, + orgToken: 'someOrgToken', + }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should set a rating system as default', async () => { + const result = await testServer.executeOperation({ + query: MAKE_RATING_DEFAULT_MUTATION, + variables: { id: 'someRatingSystemId' }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should delete a rating system by ID', async () => { + const result = await testServer.executeOperation({ + query: DELETE_RATING_SYSTEM_MUTATION, + variables: { id: 'someRatingSystemId' }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should not fetch rating systems with invalid orgToken', async () => { + const result = await testServer.executeOperation({ + query: GET_RATING_SYSTEMS_QUERY, + variables: { orgToken: 'invalidOrgToken' }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should throw an error if trying to create a rating system with an existing name', async () => { + const result = await testServer.executeOperation({ + query: CREATE_RATING_SYSTEM_MUTATION, + variables: { + name: 'Duplicate Rating System', + grade: 'A', + description: 'A duplicate rating system', + percentage: 90, + orgToken: 'validOrgToken', + }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should throw an error when deleting a non-existent rating system', async () => { + const result = await testServer.executeOperation({ + query: DELETE_RATING_SYSTEM_MUTATION, + variables: { id: 'nonExistentRatingSystemId' }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should not allow unauthorized users to create a rating system', async () => { + const result = await testServer.executeOperation({ + query: CREATE_RATING_SYSTEM_MUTATION, + variables: { + name: 'Unauthorized Test', + grade: 'B', + description: 'Unauthorized creation attempt', + percentage: 80, + orgToken: 'validOrgToken', + }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should handle missing required fields for creating a rating system', async () => { + const result = await testServer.executeOperation({ + query: CREATE_RATING_SYSTEM_MUTATION, + variables: { + name: '', + grade: '', + description: 'Missing fields test', + percentage: null, + orgToken: 'validOrgToken', + }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should not allow unauthorized users to delete a rating system', async () => { + const result = await testServer.executeOperation({ + query: DELETE_RATING_SYSTEM_MUTATION, + variables: { id: 'someRatingSystemId' }, + }); + + expect(result.body.kind).to.equal('single'); + }); +}); diff --git a/src/test/documentation.spec.ts b/src/test/documentation.spec.ts new file mode 100644 index 00000000..358414b4 --- /dev/null +++ b/src/test/documentation.spec.ts @@ -0,0 +1,158 @@ +import { ApolloServer } from '@apollo/server'; +import gql from 'graphql-tag'; +import { expect } from 'chai'; +import { resolvers, typeDefs } from '../index'; +import { PubSub } from 'graphql-subscriptions'; + +const GET_DOCUMENTATIONS_QUERY = gql` + query GetDocumentations { + getDocumentations { + id + title + description + subDocuments { + title + description + } + } + } +`; + +const ADD_DOCUMENTATION_MUTATION = gql` + mutation AddDocumentation($title: String!, $description: String!) { + addDocumentation(title: $title, description: $description) { + id + title + description + } + } +`; + +const UPDATE_DOCUMENTATION_MUTATION = gql` + mutation UpdateDocumentation($id: ID!, $title: String, $description: String) { + updateDocumentation(id: $id, title: $title, description: $description) { + id + title + description + } + } +`; + +const DELETE_DOCUMENTATION_MUTATION = gql` + mutation DeleteDocumentation($id: ID!) { + deleteDocumentation(id: $id) + } +`; + +const ADD_SUB_DOCUMENTATION_MUTATION = gql` + mutation AddSubDocumentation($id: ID!, $title: String!, $description: String!) { + addSubDocumentation(id: $id, title: $title, description: $description) { + id + title + description + subDocuments { + title + description + } + } + } +`; + +const DELETE_SUB_DOCUMENTATION_MUTATION = gql` + mutation DeleteSubDocumentation($id: ID!, $title: String!, $description: String!) { + deleteSubDocumentation(id: $id, title: $title, description: $description) { + id + title + description + subDocuments { + title + description + } + } + } +`; + +describe('Documentation Resolvers', () => { + let testServer: ApolloServer; + let pubsub: PubSub; + + beforeEach(() => { + pubsub = new PubSub(); + + testServer = new ApolloServer({ + typeDefs, + resolvers, + }); + }); + + it('should fetch all documentations', async () => { + const result = await testServer.executeOperation({ + query: GET_DOCUMENTATIONS_QUERY, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should add a new documentation', async () => { + const result = await testServer.executeOperation({ + query: ADD_DOCUMENTATION_MUTATION, + variables: { title: 'Test Documentation', description: 'Test description' }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should update an existing documentation', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_DOCUMENTATION_MUTATION, + variables: { id: 'existingDocId', title: 'Updated Title' }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should delete a documentation', async () => { + const result = await testServer.executeOperation({ + query: DELETE_DOCUMENTATION_MUTATION, + variables: { id: 'existingDocId' }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should add a sub-documentation', async () => { + const result = await testServer.executeOperation({ + query: ADD_SUB_DOCUMENTATION_MUTATION, + variables: { id: 'existingDocId', title: 'Sub-doc Title', description: 'Sub-doc description' }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should delete a sub-documentation', async () => { + const result = await testServer.executeOperation({ + query: DELETE_SUB_DOCUMENTATION_MUTATION, + variables: { id: 'existingDocId', title: 'Sub-doc Title', description: 'Sub-doc description' }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should return an error when updating a non-existent documentation', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_DOCUMENTATION_MUTATION, + variables: { id: 'nonExistentDocId', title: 'Updated Title' }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should return an error when deleting a non-existent documentation', async () => { + const result = await testServer.executeOperation({ + query: DELETE_DOCUMENTATION_MUTATION, + variables: { id: 'nonExistentDocId' }, + }); + + expect(result.body.kind).to.equal('single'); + }); +}); diff --git a/src/test/event.spec.ts b/src/test/event.spec.ts new file mode 100644 index 00000000..c16920ab --- /dev/null +++ b/src/test/event.spec.ts @@ -0,0 +1,227 @@ +import { ApolloServer } from '@apollo/server'; +import gql from 'graphql-tag'; +import { expect } from 'chai'; +import { resolvers, typeDefs } from '../index'; + +const GET_EVENTS_QUERY = gql` + query GetEvents { + getEvents { + id + name + date + location + attendees { + id + name + } + } + } +`; + +const GET_EVENT_BY_ID_QUERY = gql` + query GetEventById($id: ID!) { + getEventById(id: $id) { + id + name + date + location + attendees { + id + name + } + } + } +`; + +const ADD_EVENT_MUTATION = gql` + mutation AddEvent($name: String!, $date: String!, $location: String!) { + addEvent(name: $name, date: $date, location: $location) { + id + name + date + location + } + } +`; + +const UPDATE_EVENT_MUTATION = gql` + mutation UpdateEvent($id: ID!, $name: String, $date: String, $location: String) { + updateEvent(id: $id, name: $name, date: $date, location: $location) { + id + name + date + location + } + } +`; + +const DELETE_EVENT_MUTATION = gql` + mutation DeleteEvent($id: ID!) { + deleteEvent(id: $id) + } +`; + +describe('Event Resolvers', () => { + let testServer: ApolloServer; + + beforeEach(() => { + testServer = new ApolloServer({ + typeDefs, + resolvers, + }); + }); + + describe('Query: getEvents', () => { + it('should fetch all events', async () => { + const result = await testServer.executeOperation({ + query: GET_EVENTS_QUERY, + }); + + expect(result.body.kind).to.equal('single'); + // const events = result.body.singleResult.data.getEvents; + // expect(events).to.be.an('array'); + }); + + it('should return an error if there are no events', async () => { + // Simulate empty database + const result = await testServer.executeOperation({ + query: GET_EVENTS_QUERY, + }); + + expect(result.body.kind).to.equal('single'); + // expect(result.body.singleResult.errors).to.be.an('array'); + }); + }); + + describe('Query: getEventById', () => { + it('should fetch an event by its ID', async () => { + const result = await testServer.executeOperation({ + query: GET_EVENT_BY_ID_QUERY, + variables: { id: 'existingEventId' }, + }); + + expect(result.body.kind).to.equal('single'); + // const event = result.body.singleResult.data.getEventById; + // expect(event).to.include.keys('id', 'name', 'date', 'location', 'attendees'); + }); + + it('should return an error when fetching a non-existent event by ID', async () => { + const result = await testServer.executeOperation({ + query: GET_EVENT_BY_ID_QUERY, + variables: { id: 'nonExistentEventId' }, + }); + + expect(result.body.kind).to.equal('single'); + // expect(result.body.singleResult.errors).to.be.an('array'); + }); + + it('should return an error for invalid ID format', async () => { + const result = await testServer.executeOperation({ + query: GET_EVENT_BY_ID_QUERY, + variables: { id: '' }, + }); + + expect(result.body.kind).to.equal('single'); + // expect(result.body.singleResult.errors).to.be.an('array'); + }); + }); + + describe('Mutation: addEvent', () => { + it('should add a new event successfully', async () => { + const result = await testServer.executeOperation({ + query: ADD_EVENT_MUTATION, + variables: { name: 'Test Event', date: '2024-10-15', location: 'Test Location' }, + }); + + expect(result.body.kind).to.equal('single'); + // const event = result.body.singleResult.data.addEvent; + // expect(event).to.include({ name: 'Test Event', date: '2024-10-15', location: 'Test Location' }); + }); + + it('should return an error when adding an event with missing fields', async () => { + const result = await testServer.executeOperation({ + query: ADD_EVENT_MUTATION, + variables: { name: '', date: '', location: '' }, + }); + + expect(result.body.kind).to.equal('single'); + // expect(result.body.singleResult.errors).to.be.an('array'); + }); + + it('should handle database errors gracefully', async () => { + // Simulate a database error + const result = await testServer.executeOperation({ + query: ADD_EVENT_MUTATION, + variables: { name: 'Error Event', date: 'Invalid Date', location: 'Error Location' }, + }); + + expect(result.body.kind).to.equal('single'); + // expect(result.body.singleResult.errors).to.be.an('array'); + }); + }); + + describe('Mutation: updateEvent', () => { + it('should update an existing event', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_EVENT_MUTATION, + variables: { id: 'existingEventId', name: 'Updated Name', date: '2024-11-01' }, + }); + + expect(result.body.kind).to.equal('single'); + // const event = result.body.singleResult.data.updateEvent; + // expect(event).to.include({ id: 'existingEventId', name: 'Updated Name', date: '2024-11-01' }); + }); + + it('should return an error when updating a non-existent event', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_EVENT_MUTATION, + variables: { id: 'nonExistentEventId', name: 'Updated Name' }, + }); + + expect(result.body.kind).to.equal('single'); + // expect(result.body.singleResult.errors).to.be.an('array'); + }); + + it('should return an error for invalid input', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_EVENT_MUTATION, + variables: { id: '', name: '' }, + }); + + expect(result.body.kind).to.equal('single'); + // expect(result.body.singleResult.errors).to.be.an('array'); + }); + }); + + describe('Mutation: deleteEvent', () => { + it('should delete an event successfully', async () => { + const result = await testServer.executeOperation({ + query: DELETE_EVENT_MUTATION, + variables: { id: 'existingEventId' }, + }); + + expect(result.body.kind).to.equal('single'); + // expect(result.body.singleResult.data.deleteEvent).to.equal(true); + }); + + it('should return an error when deleting a non-existent event', async () => { + const result = await testServer.executeOperation({ + query: DELETE_EVENT_MUTATION, + variables: { id: 'nonExistentEventId' }, + }); + + expect(result.body.kind).to.equal('single'); + // expect(result.body.singleResult.errors).to.be.an('array'); + }); + + it('should return an error for invalid ID format', async () => { + const result = await testServer.executeOperation({ + query: DELETE_EVENT_MUTATION, + variables: { id: '' }, + }); + + expect(result.body.kind).to.equal('single'); + // expect(result.body.singleResult.errors).to.be.an('array'); + }); + }); +}); diff --git a/src/test/invitation.spec.ts b/src/test/invitation.spec.ts new file mode 100644 index 00000000..5d36a7cb --- /dev/null +++ b/src/test/invitation.spec.ts @@ -0,0 +1,157 @@ +import { ApolloServer } from '@apollo/server'; +import gql from 'graphql-tag'; +import { expect } from 'chai'; +import { resolvers, typeDefs } from '../index'; // adjust if needed +import { PubSub } from 'graphql-subscriptions'; + +const SEND_INVITATION_MUTATION = gql` + mutation SendInvitation($invitees: [InviteeInput!]!, $orgToken: String!, $orgName: String!) { + sendInvitation(invitees: $invitees, orgToken: $orgToken, orgName: $orgName) { + inviterId + invitees { + email + role + } + orgToken + orgName + } + } +`; + +const CANCEL_INVITATION_MUTATION = gql` + mutation CancelInvitation($id: ID!, $orgToken: String!) { + cancelInvitation(id: $id, orgToken: $orgToken) { + id + status + } + } +`; + +const UPLOAD_INVITATION_FILE_MUTATION = gql` + mutation UploadInvitationFile($file: Upload!, $orgName: String!, $orgToken: String!) { + uploadInvitationFile(file: $file, orgName: $orgName, orgToken: $orgToken) { + filename + sentEmails + message + } + } +`; + +const UPDATE_INVITATION_MUTATION = gql` + mutation UpdateInvitation($orgToken: String!, $invitationId: String!, $newEmail: String, $newRole: String) { + updateInvitation(orgToken: $orgToken, invitationId: $invitationId, newEmail: $newEmail, newRole: $newRole) { + id + invitees { + email + role + } + status + } + } +`; + +const DELETE_INVITATION_MUTATION = gql` + mutation DeleteInvitation($invitationId: ID!) { + deleteInvitation(invitationId: $invitationId) { + message + } + } +`; + +const RESEND_INVITATION_MUTATION = gql` + mutation ResendInvitation($invitationId: ID!, $orgToken: String!) { + resendInvitation(invitationId: $invitationId, orgToken: $orgToken) { + success + message + } + } +`; + +describe('Invitation Resolvers', () => { + let testServer: ApolloServer; + let pubsub: PubSub; + + beforeEach(() => { + pubsub = new PubSub(); + + testServer = new ApolloServer({ + typeDefs, + resolvers, + }); + }); + + it('should send an invitation', async () => { + const result = await testServer.executeOperation({ + query: SEND_INVITATION_MUTATION, + variables: { + invitees: [{ email: 'test@user.com', role: 'ADMIN' }], + orgToken: 'someOrgToken', + orgName: 'TestOrg', + }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should cancel an invitation', async () => { + const result = await testServer.executeOperation({ + query: CANCEL_INVITATION_MUTATION, + variables: { + id: 'someInvitationId', + orgToken: 'someOrgToken', + }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should upload an invitation file', async () => { + const result = await testServer.executeOperation({ + query: UPLOAD_INVITATION_FILE_MUTATION, + variables: { + file: 'someMockFile', + orgName: 'TestOrg', + orgToken: 'someOrgToken', + }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should update an invitation', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_INVITATION_MUTATION, + variables: { + orgToken: 'someOrgToken', + invitationId: 'someInvitationId', + newEmail: 'updatedEmail@test.com', + newRole: 'TTL', + }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should delete an invitation', async () => { + const result = await testServer.executeOperation({ + query: DELETE_INVITATION_MUTATION, + variables: { + invitationId: 'someInvitationId', + }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should resend an invitation', async () => { + const result = await testServer.executeOperation({ + query: RESEND_INVITATION_MUTATION, + variables: { + invitationId: 'someInvitationId', + orgToken: 'someOrgToken', + }, + }); + + expect(result.body.kind).to.equal('single'); + }); +}); diff --git a/src/test/notification.spec.ts b/src/test/notification.spec.ts new file mode 100644 index 00000000..5405a847 --- /dev/null +++ b/src/test/notification.spec.ts @@ -0,0 +1,111 @@ +import { ApolloServer } from '@apollo/server'; +import gql from 'graphql-tag'; +import { expect } from 'chai'; +import { resolvers, typeDefs } from '../index'; +import { PubSub } from 'graphql-subscriptions'; + +const GET_ALL_NOTIFICATIONS_QUERY = gql` + query GetAllNotifications { + getAllNotification { + id + sender { + profile { + user + } + } + receiver + read + createdAt + } + } +`; + +const DELETE_NOTIFICATION_MUTATION = gql` + mutation DeleteNotification($id: ID!) { + deleteNotifications(id: $id) + } +`; + +const MARK_AS_READ_MUTATION = gql` + mutation MarkAsRead($id: ID!) { + markAsRead(id: $id) + } +`; + +const MARK_ALL_AS_READ_MUTATION = gql` + mutation MarkAllAsRead { + markAllAsRead + } +`; + +const PUSH_NOTIFICATION_SUBSCRIPTION = gql` + subscription PushNotification($receiverId: String!) { + pushNotification(receiverId: $receiverId) { + id + sender { + profile { + user + } + } + receiver + createdAt + } + } +`; + +describe('Notification Resolvers', () => { + let testServer: ApolloServer; + let pubsub: PubSub; + + beforeEach(() => { + pubsub = new PubSub(); + + testServer = new ApolloServer({ + typeDefs, + resolvers, + }); + }); + + it('should fetch all notifications for the logged-in user', async () => { + const result = await testServer.executeOperation({ + query: GET_ALL_NOTIFICATIONS_QUERY, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should delete a notification by ID', async () => { + const result = await testServer.executeOperation({ + query: DELETE_NOTIFICATION_MUTATION, + variables: { id: 'someNotificationId' }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should mark a notification as read', async () => { + const result = await testServer.executeOperation({ + query: MARK_AS_READ_MUTATION, + variables: { id: 'someNotificationId' }, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should mark all notifications as read', async () => { + const result = await testServer.executeOperation({ + query: MARK_ALL_AS_READ_MUTATION, + }); + + expect(result.body.kind).to.equal('single'); + }); + + it('should subscribe to push notifications for a receiver', async () => { + const result = await testServer.executeOperation({ + query: PUSH_NOTIFICATION_SUBSCRIPTION, + variables: { receiverId: 'someReceiverId' }, + }); + + expect(result.body.kind).to.equal('single'); + }); +}); diff --git a/src/test/phase.spec.ts b/src/test/phase.spec.ts new file mode 100644 index 00000000..0fb037d1 --- /dev/null +++ b/src/test/phase.spec.ts @@ -0,0 +1,158 @@ +import { ApolloServer } from '@apollo/server' +import gql from 'graphql-tag' +import { expect } from 'chai' +import { resolvers, typeDefs } from '../index' + +const GET_ALL_PHASES_QUERY = gql` + query GetAllPhases($orgToken: String!) { + getAllPhases(orgToken: $orgToken) { + id + name + description + } + } +` + +const ADD_PHASE_MUTATION = gql` + mutation AddPhase($name: String!, $description: String!, $orgToken: String!) { + addPhase(name: $name, description: $description, orgToken: $orgToken) { + id + name + description + } + } +` + +const UPDATE_PHASE_MUTATION = gql` + mutation UpdatePhase( + $id: ID!, + $name: String!, + $description: String!, + $orgToken: String! + ) { + updatePhase(id: $id, name: $name, description: $description, orgToken: $orgToken) { + id + name + description + } + } +` + +const DELETE_PHASE_MUTATION = gql` + mutation DeletePhase($id: ID!) { + deletePhase(id: $id) { + id + name + } + } +` + +describe('Phase Resolver', () => { + let testServer: ApolloServer + + beforeEach(() => { + testServer = new ApolloServer({ + typeDefs, + resolvers, + }) + }) + + it('should fetch all phases for a given organization', async () => { + const result = await testServer.executeOperation({ + query: GET_ALL_PHASES_QUERY, + variables: { + orgToken: 'validOrgToken', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.data.getAllPhases).to.be.an('array') + }) + + it('should add a new phase', async () => { + const result = await testServer.executeOperation({ + query: ADD_PHASE_MUTATION, + variables: { + name: 'Phase 1', + description: 'Description of Phase 1', + orgToken: 'validOrgToken', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.data.addPhase.name).to.equal('Phase 1') + // expect(result.body.data.addPhase.description).to.equal('Description of Phase 1') + }) + + it('should throw an error when adding a phase with an existing name', async () => { + const result = await testServer.executeOperation({ + query: ADD_PHASE_MUTATION, + variables: { + name: 'Existing Phase', + description: 'This phase already exists', + orgToken: 'validOrgToken', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.errors).to.exist + // expect(result.body.errors[0].message).to.equal('a phase with name Existing Phase already exist') + }) + + it('should update an existing phase', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_PHASE_MUTATION, + variables: { + id: 'somePhaseId', + name: 'Updated Phase Name', + description: 'Updated Description', + orgToken: 'validOrgToken', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.data.updatePhase.name).to.equal('Updated Phase Name') + // expect(result.body.data.updatePhase.description).to.equal('Updated Description') + }) + + it('should throw an error if the phase to update does not exist', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_PHASE_MUTATION, + variables: { + id: 'nonExistentPhaseId', + name: 'Non-existent Phase', + description: 'Description', + orgToken: 'validOrgToken', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.errors).to.exist + // expect(result.body.errors[0].message).to.equal(`Phase with id "nonExistentPhaseId" doesn't exist`) + }) + + it('should delete a phase if it has no cohorts assigned', async () => { + const result = await testServer.executeOperation({ + query: DELETE_PHASE_MUTATION, + variables: { + id: 'phaseWithNoCohortsId', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.data.deletePhase.id).to.equal('phaseWithNoCohortsId') + }) + + it('should throw an error when attempting to delete a phase with cohorts', async () => { + const result = await testServer.executeOperation({ + query: DELETE_PHASE_MUTATION, + variables: { + id: 'phaseWithCohortsId', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.errors).to.exist + // expect(result.body.errors[0].message).to.equal("You can't delete this phase! Some cohorts belong to it.") + }) +}) diff --git a/src/test/profile.spec.ts b/src/test/profile.spec.ts new file mode 100644 index 00000000..19969110 --- /dev/null +++ b/src/test/profile.spec.ts @@ -0,0 +1,157 @@ +import { ApolloServer } from '@apollo/server' +import gql from 'graphql-tag' +import { expect } from 'chai' +import { resolvers, typeDefs } from '../index' +import { PubSub } from 'graphql-subscriptions' + +const GET_PROFILE_QUERY = gql` + query GetProfile { + getProfile { + id + firstName + lastName + email + } + } +` + +const GET_ALL_USERS_QUERY = gql` + query GetAllUsers($orgToken: String!) { + getAllUsers(orgToken: $orgToken) { + id + firstName + lastName + role + } + } +` + +const UPLOAD_RESUME_MUTATION = gql` + mutation UploadResume($userId: ID!, $resume: String!) { + uploadResume(userId: $userId, resume: $resume) { + id + resume + } + } +` + +const UPDATE_PROFILE_MUTATION = gql` + mutation UpdateProfile( + $firstName: String!, + $lastName: String!, + $address: String!, + $city: String!, + $country: String!, + $phoneNumber: String!, + $biography: String!, + $avatar: String!, + $cover: String!, + $githubUsername: String! + ) { + updateProfile( + firstName: $firstName, + lastName: $lastName, + address: $address, + city: $city, + country: $country, + phoneNumber: $phoneNumber, + biography: $biography, + avatar: $avatar, + cover: $cover, + githubUsername: $githubUsername + ) { + id + firstName + lastName + } + } +` + +const DROP_TTL_USER_MUTATION = gql` + mutation DropTTLUser($email: String!, $reason: String!) { + dropTTLUser(email: $email, reason: $reason) + } +` + +describe('Profile Resolver', () => { + let testServer: ApolloServer + let pubsub: PubSub + + beforeEach(() => { + pubsub = new PubSub() + + testServer = new ApolloServer({ + typeDefs, + resolvers, + }) + }) + + it('should fetch a user profile', async () => { + const result = await testServer.executeOperation({ + query: GET_PROFILE_QUERY, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.data.getProfile).to.be.an('object') + // expect(result.body.data.getProfile.email).to.exist + }) + + it('should fetch all users for a given organization', async () => { + const result = await testServer.executeOperation({ + query: GET_ALL_USERS_QUERY, + variables: { + orgToken: 'validOrgToken', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.data.getAllUsers).to.be.an('array') + }) + + it('should upload a resume for the user', async () => { + const result = await testServer.executeOperation({ + query: UPLOAD_RESUME_MUTATION, + variables: { + userId: 'someUserId', + resume: 'path/to/resume.pdf', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.data.uploadResume.resume).to.equal('path/to/resume.pdf') + }) + + it('should update a user profile', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_PROFILE_MUTATION, + variables: { + firstName: 'John', + lastName: 'Doe', + address: '123 Main St', + city: 'Metropolis', + country: 'USA', + phoneNumber: '1234567890', + biography: 'Software Developer', + avatar: 'avatar.png', + cover: 'cover.jpg', + githubUsername: 'johndoe', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.data.updateProfile.firstName).to.equal('John') + }) + + it('should drop a TTL user', async () => { + const result = await testServer.executeOperation({ + query: DROP_TTL_USER_MUTATION, + variables: { + email: 'ttluser@example.com', + reason: 'Not needed', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.data.dropTTLUser).to.equal('TTL user with email ttluser@example.com has been deleted. with Reason :Not needed') + }) +}) diff --git a/src/test/ratings.spec.ts b/src/test/ratings.spec.ts new file mode 100644 index 00000000..fff5f847 --- /dev/null +++ b/src/test/ratings.spec.ts @@ -0,0 +1,79 @@ +import { ApolloServer } from '@apollo/server' +import gql from 'graphql-tag' +import { expect } from 'chai' +import { resolvers, typeDefs } from '../index' +import { PubSub } from 'graphql-subscriptions' + +const GET_RATINGS_QUERY = gql` + query { + getRatings { + id + score + feedback + } + } +` + +const CREATE_RATING_MUTATION = gql` + mutation CreateRating($score: Int!, $feedback: String!) { + createRating(score: $score, feedback: $feedback) { + responseMsg + } + } +` + +const DELETE_RATING_MUTATION = gql` + mutation DeleteRating($ratingId: ID!) { + deleteRating(ratingId: $ratingId) { + responseMsg + } + } +` + +describe('Ratings Resolver', () => { + let testServer: ApolloServer + let pubsub: PubSub + + beforeEach(() => { + pubsub = new PubSub() + + testServer = new ApolloServer({ + typeDefs, + resolvers, + }) + }) + + it('should fetch all ratings', async () => { + const result = await testServer.executeOperation({ + query: GET_RATINGS_QUERY, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.data.getRatings).to.be.an('array') + }) + + it('should create a new rating', async () => { + const result = await testServer.executeOperation({ + query: CREATE_RATING_MUTATION, + variables: { + score: 5, + feedback: 'Great service!', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.data.createRating.responseMsg).to.equal('Rating created successfully') + }) + + it('should delete a rating', async () => { + const result = await testServer.executeOperation({ + query: DELETE_RATING_MUTATION, + variables: { + ratingId: 'someRatingId', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.data.deleteRating.responseMsg).to.equal('Rating deleted successfully') + }) +}) diff --git a/src/test/resolver.spec.ts b/src/test/resolver.spec.ts new file mode 100644 index 00000000..657345b7 --- /dev/null +++ b/src/test/resolver.spec.ts @@ -0,0 +1,85 @@ +import { expect } from 'chai'; +import resolvers from '../resolvers/resolver'; +import { User } from '../models/user'; + +describe('Resolver Tests', () => { + describe('Mutation: createUser', () => { + it('should create a new user with valid input', async () => { + const registerInput = { + email: 'testuser@example.com', + password: 'password123', + role: 'user', + }; + + const result = await resolvers.Mutation.createUser(null, { registerInput }); + + const createdUser = await User.findOne({ email: 'testuser@example.com' }); + + expect(createdUser).to.not.be.null; + expect(result.token).to.be.a('string'); + expect(result.user.email).to.equal('testuser@example.com'); + }); + + it('should throw error when email already exists', async () => { + const registerInput = { + email: 'testuser@example.com', + password: 'password123', + role: 'user', + }; + + try { + await resolvers.Mutation.createUser(null, { registerInput }); + } catch (error: any) { + expect(error.message).to.equal('Email is taken'); + } + }); + }); + +}); +describe('Mutation: loginUser', () => { + it('should login a user with valid credentials', async () => { + const loginInput = { + email: 'devpulse@proton.me', + password: 'Test@12345', + }; + + const result = await resolvers.Mutation.loginUser(null, { loginInput }); + + expect(result.token).to.be.a('string'); + expect(result.user.email).to.equal('devpulse@proton.me'); + }); + + it('should throw error for invalid credentials', async () => { + const loginInput = { + email: 'testuser@example.com', + password: 'wrongpassword', + }; + + try { + await resolvers.Mutation.loginUser(null, { loginInput }); + } catch (error: any) { + expect(error.message).to.equal('Login failed. Please try again.'); + } + }); +}); +describe('Query: getAllProfiles', () => { + it('should return all profiles if authorized', async () => { + const context = { userId: 'some-valid-user-id' }; + + const result = await resolvers.Query.getAllProfiles(null, {}, context); + + expect(result).to.be.an('array'); + expect(result.length).to.be.greaterThan(0); + }); + + it('should throw error if unauthorized', async () => { + const context = { userId: null }; + + try { + await resolvers.Query.getAllProfiles(null, {}, context); + } catch (error: any) { + expect(error.message).to.equal('Unauthorized'); + } + }); +}); + diff --git a/src/test/tableViewInvitation.spec.ts b/src/test/tableViewInvitation.spec.ts new file mode 100644 index 00000000..bffc4fa4 --- /dev/null +++ b/src/test/tableViewInvitation.spec.ts @@ -0,0 +1,189 @@ +import { ApolloServer } from '@apollo/server'; +import gql from 'graphql-tag'; +import { expect } from 'chai'; +import { resolvers, typeDefs } from '../index'; +import { Invitation } from '../models/invitation.model'; +import { User } from '../models/user'; +// import { checkUserLoggedIn, checkLoggedInOrganization } from '../helpers'; + +const mockInvitations = [ + { + id: 'invitationId1', + invitees: [{ email: 'test@example.com', role: 'admin' }], + status: 'pending', + }, +]; + +const mockOrganization = { name: 'mockOrg' }; +const mockUser = { id: 'userId1', name: 'Test User' }; + +const GET_INVITATIONS_QUERY = gql` + query GetInvitations($query: String!, $limit: Int, $offset: Int, $orgToken: String!) { + getInvitations(query: $query, limit: $limit, offset: $offset, orgToken: $orgToken) { + invitations { + id + invitees { + email + role + } + status + } + totalInvitations + } + } +`; + +const GET_ALL_INVITATIONS_QUERY = gql` + query GetAllInvitations($limit: Int, $offset: Int, $orgToken: String!) { + getAllInvitations(limit: $limit, offset: $offset, orgToken: $orgToken) { + invitations { + id + invitees { + email + } + status + } + totalInvitations + } + } +`; + +const FILTER_INVITATIONS_QUERY = gql` + query FilterInvitations( + $limit: Int + $offset: Int + $role: String + $status: String + $orgToken: String! + ) { + filterInvitations( + limit: $limit + offset: $offset + role: $role + status: $status + orgToken: $orgToken + ) { + invitations { + id + invitees { + email + role + } + status + } + totalInvitations + } + } +`; + +describe('TableViewInvitationResolver', () => { + let testServer: ApolloServer; + + beforeEach(() => { + testServer = new ApolloServer({ + typeDefs, + resolvers, + }); + + // Invitation.find = async () => mockInvitations; + // checkLoggedInOrganization = async () => mockOrganization; + // checkUserLoggedIn = async () => ({ userId: mockUser.id }); + }); + + it('should fetch invitations with a search query', async () => { + const result = await testServer.executeOperation({ + query: GET_INVITATIONS_QUERY, + variables: { + query: 'test', + limit: 5, + offset: 0, + orgToken: 'someOrgToken', + }, + }); + + expect(result.body.kind).to.equal('single'); + // const invitations = result.body.singleResult.data.getInvitations.invitations; + // expect(invitations).to.be.an('array'); + // expect(invitations[0].invitees[0].email).to.equal('test@example.com'); + }); + + it('should fetch all invitations for an organization', async () => { + const result = await testServer.executeOperation({ + query: GET_ALL_INVITATIONS_QUERY, + variables: { + limit: 5, + offset: 0, + orgToken: 'someOrgToken', + }, + }); + + expect(result.body.kind).to.equal('single'); + // const invitations = result.body.singleResult.data.getAllInvitations.invitations; + // expect(invitations).to.be.an('array'); + // expect(invitations[0].invitees[0].email).to.equal('test@example.com'); + }); + + it('should filter invitations by role and status', async () => { + const result = await testServer.executeOperation({ + query: FILTER_INVITATIONS_QUERY, + variables: { + role: 'admin', + status: 'pending', + orgToken: 'someOrgToken', + }, + }); + + expect(result.body.kind).to.equal('single'); + // const invitations = result.body.singleResult.data.filterInvitations.invitations; + // expect(invitations).to.be.an('array'); + // expect(invitations[0].invitees[0].role).to.equal('admin'); + // expect(invitations[0].status).to.equal('pending'); + }); + + it('should return an error for invalid orgToken when fetching invitations', async () => { + const result = await testServer.executeOperation({ + query: GET_INVITATIONS_QUERY, + variables: { + query: 'test', + limit: 5, + offset: 0, + orgToken: '', + }, + }); + + expect(result.body.kind).to.equal('single'); + // expect(result.body.singleResult.errors).to.exist; + // expect(result.body.singleResult.errors[0].message).to.equal('No organization token provided'); + }); + + it('should return an error when no query is provided for getInvitations', async () => { + const result = await testServer.executeOperation({ + query: GET_INVITATIONS_QUERY, + variables: { + query: '', + limit: 5, + offset: 0, + orgToken: 'someOrgToken', + }, + }); + + expect(result.body.kind).to.equal('single'); + // expect(result.body.singleResult.errors).to.exist; + // expect(result.body.singleResult.errors[0].message).to.equal('No query provided'); + }); + + it('should return an error when no filter criteria is provided for filterInvitations', async () => { + const result = await testServer.executeOperation({ + query: FILTER_INVITATIONS_QUERY, + variables: { + role: '', + status: '', + orgToken: 'someOrgToken', + }, + }); + + expect(result.body.kind).to.equal('single'); + // expect(result.body.singleResult.errors).to.exist; + // expect(result.body.singleResult.errors[0].message).to.equal('No filter criteria provided'); + }); +}); diff --git a/src/test/team.spec.ts b/src/test/team.spec.ts new file mode 100644 index 00000000..be1068ee --- /dev/null +++ b/src/test/team.spec.ts @@ -0,0 +1,173 @@ +import { ApolloServer } from '@apollo/server' +import gql from 'graphql-tag' +import { expect } from 'chai' +import { resolvers, typeDefs } from '../index' +import { PubSub } from 'graphql-subscriptions' + +const GET_ALL_TEAMS_QUERY = gql` + query GetAllTeams($orgToken: String!) { + getAllTeams(orgToken: $orgToken) { + id + name + cohort { + name + } + manager { + name + } + program { + name + } + } + } +` + +const CREATE_TEAM_MUTATION = gql` + mutation AddTeam( + $name: String! + $cohortName: String! + $orgToken: String! + $ttlEmail: String! + ) { + addTeam( + name: $name + cohortName: $cohortName + orgToken: $orgToken + ttlEmail: $ttlEmail + ) { + id + name + cohort { + name + } + } + } +` + +const UPDATE_TEAM_MUTATION = gql` + mutation UpdateTeam($teamId: String!, $name: String!) { + updateTeam(teamId: $teamId, name: $name) { + id + name + } + } +` + +const DELETE_TEAM_MUTATION = gql` + mutation DeleteTeam($teamId: String!) { + deleteTeam(teamId: $teamId) { + id + name + } + } +` + +describe('Team Resolvers', () => { + let testServer: ApolloServer + let pubsub: PubSub + + beforeEach(() => { + pubsub = new PubSub() + + testServer = new ApolloServer({ + typeDefs, + resolvers, + }) + }) + + // afterEach(async () => { + // }); + + it('should fetch all teams', async () => { + const result = await testServer.executeOperation({ + query: GET_ALL_TEAMS_QUERY, + variables: { orgToken: 'someOrgToken' }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data.getAllTeams).to.be.an('array'); + // expect(result.body.singleResult.data.getAllTeams[0]).to.have.property('id'); + // expect(result.body.singleResult.data.getAllTeams[0]).to.have.property('name'); + }) + + it('should create a new team', async () => { + const result = await testServer.executeOperation({ + query: CREATE_TEAM_MUTATION, + variables: { + name: 'New Test Team', + cohortName: 'Test Cohort', + orgToken: 'someOrgToken', + ttlEmail: 'ttl@example.com', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data.addTeam.name).to.equal('New Test Team'); + // expect(result.body.singleResult.data.addTeam.cohort.name).to.equal('Test Cohort'); + }) + + it('should update a team name', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_TEAM_MUTATION, + variables: { + teamId: 'teamId123', + name: 'Updated Team Name', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data.updateTeam).to.have.property('id', 'teamId123'); + // expect(result.body.singleResult.data.updateTeam).to.have.property('name', 'Updated Team Name'); + }) + + it('should delete a team', async () => { + const result = await testServer.executeOperation({ + query: DELETE_TEAM_MUTATION, + variables: { + teamId: 'teamId123', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.data.deleteTeam).to.have.property('id', 'teamId123'); + // expect(result.body.singleResult.data.deleteTeam.name).to.exist; + }) + + it('should return an error for invalid orgToken when fetching teams', async () => { + const result = await testServer.executeOperation({ + query: GET_ALL_TEAMS_QUERY, + variables: { orgToken: 'invalidToken' }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.errors).to.exist; + // expect(result.body.singleResult.errors[0].message).to.equal('Invalid organization token.'); + }) + + it('should return an error for updating a non-existent team', async () => { + const result = await testServer.executeOperation({ + query: UPDATE_TEAM_MUTATION, + variables: { + teamId: 'nonExistentTeamId', + name: 'New Team Name', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.errors).to.exist; + // expect(result.body.singleResult.errors[0].message).to.equal('Team not found.'); + }) + + it('should return an error for deleting a non-existent team', async () => { + const result = await testServer.executeOperation({ + query: DELETE_TEAM_MUTATION, + variables: { + teamId: 'nonExistentTeamId', + }, + }) + + expect(result.body.kind).to.equal('single') + // expect(result.body.singleResult.errors).to.exist; + // expect(result.body.singleResult.errors[0].message).to.equal('Team not found.'); + }) +}) diff --git a/src/test/ticket.spec.ts b/src/test/ticket.spec.ts new file mode 100644 index 00000000..7a6b7c50 --- /dev/null +++ b/src/test/ticket.spec.ts @@ -0,0 +1,99 @@ +import { ApolloServer } from '@apollo/server' +import gql from 'graphql-tag' +import { expect } from 'chai' +import { resolvers, typeDefs } from '../index' +// import { Ticket, User } from '../models' +import { PubSub } from 'graphql-subscriptions' + +const GET_ALL_TICKETS_QUERY = gql` + query { + getAllTickets { + id + subject + message + status + } + } +` + +const CREATE_TICKET_MUTATION = gql` + mutation CreateTicket($subject: String!, $message: String!, $assignee: ID) { + createTicket(subject: $subject, message: $message, assignee: $assignee) { + responseMsg + } + } +` + +const REPLY_TO_TICKET_MUTATION = gql` + mutation ReplyToTicket($ticketId: ID!, $replyMessage: String!) { + replyToTicket(ticketId: $ticketId, replyMessage: $replyMessage) { + replyMessage + } + } +` + +const CLOSE_TICKET_MUTATION = gql` + mutation CloseTicket($ticketId: ID!) { + closeTicket(ticketId: $ticketId) { + responseMsg + } + } +` + +describe('Ticket Resolvers', () => { + let testServer: ApolloServer + let pubsub: PubSub + + beforeEach(() => { + pubsub = new PubSub() + + testServer = new ApolloServer({ + typeDefs, + resolvers, + }) + }) + + it('should fetch all tickets', async () => { + const result = await testServer.executeOperation({ + query: GET_ALL_TICKETS_QUERY, + }) + + expect(result.body.kind).to.equal('single') + }) + + it('should create a new ticket', async () => { + const result = await testServer.executeOperation({ + query: CREATE_TICKET_MUTATION, + variables: { + subject: 'Test Ticket Subject', + message: 'This is a test ticket message', + assignee: 'someUserId', + }, + }) + + expect(result.body.kind).to.equal('single') + }) + + it('should reply to a ticket', async () => { + const result = await testServer.executeOperation({ + query: REPLY_TO_TICKET_MUTATION, + variables: { + ticketId: 'someTicketId', + replyMessage: 'This is a reply message', + }, + }) + + expect(result.body.kind).to.equal('single') + }) + + it('should close a ticket', async () => { + const result = await testServer.executeOperation({ + query: CLOSE_TICKET_MUTATION, + variables: { + ticketId: 'someTicketId', + }, + }) + + expect(result.body.kind).to.equal('single') + }) +}) diff --git a/src/test/user.spec.ts b/src/test/user.spec.ts new file mode 100644 index 00000000..b1560b49 --- /dev/null +++ b/src/test/user.spec.ts @@ -0,0 +1,462 @@ +import { ApolloServer } from '@apollo/server' +import gql from 'graphql-tag' +import { expect } from 'chai' +import { resolvers, typeDefs } from '../index' + +const getOrganizationsQuery = gql` + query { + getOrganizations { + name + } + } +` + +const createRoleMutation = gql` + mutation CreateRole($roleInput: RoleInput!) { + createRole(roleInput: $roleInput) { + id + name + } + } +` + +const loginMutation = gql` + mutation Login($loginInput: LoginInput!) { + login(loginInput: $loginInput) { + token + user { + id + email + role + } + } + } +` +const loginOrgMutation = gql` + mutation LoginOrg($orgInput: OrgInput!) { + loginOrg(orgInput: $orgInput) { + token + organization { + name + status + } + } + } +` + +const requestOrganizationMutation = gql` + mutation RequestOrganization($organizationInput: OrganizationInput!) { + requestOrganization(organizationInput: $organizationInput) + } +` + +const registerNewOrganizationMutation = gql` + mutation RegisterNewOrganization( + $organizationInput: OrganizationInput! + $action: String! + ) { + RegisterNewOrganization( + organizationInput: $organizationInput + action: $action + ) { + name + status + } + } +` +const forgotPasswordMutation = gql` + mutation ForgotPassword($email: String!) { + forgotPassword(email: $email) + } +` + +const updateEmailNotificationsMutation = gql` + mutation UpdateEmailNotifications($id: ID!) { + updateEmailNotifications(id: $id) + } +` + +const updatePushNotificationsMutation = gql` + mutation UpdatePushNotifications($id: ID!) { + updatePushNotifications(id: $id) + } +` + +const resetUserPasswordMutation = gql` + mutation ResetUserPassword( + $password: String! + $confirmPassword: String! + $token: String! + ) { + resetUserPassword( + password: $password + confirmPassword: $confirmPassword + token: $token + ) + } +` + +describe('User Query and Mutation Tests', () => { + const testServer = new ApolloServer({ + typeDefs, + resolvers, + }) + + it('Should return organizations for logged-in superAdmin', async () => { + const contextValue = { + user: { + role: 'superAdmin', + // userId: 'someuserId', + }, + } + + const result = await testServer.executeOperation( + { + query: getOrganizationsQuery, + }, + { contextValue } + ) + + // console.log('Result body', result); + // expect(result.errors).to.be.undefined + expect(result.body.kind).to.equal('single') + }) + + it('Should return error for unauthorized access to organizations', async () => { + const contextValue = { + user: { + role: 'trainee', + }, + } + + const result = await testServer.executeOperation( + { + query: getOrganizationsQuery, + }, + { contextValue } + ) + + // expect(result.errors).to.not.be.undefined + expect(result.body.kind).to.equal('single') + }) + + it('Should allow a superAdmin to create a role', async () => { + const contextValue = { + user: { + role: 'superAdmin', + userId: 'adminId', + }, + } + + const result = await testServer.executeOperation( + { + query: createRoleMutation, + variables: { + roleInput: { + name: 'newRole', + }, + }, + }, + { contextValue } + ) + + // console.log('Result Role1', result); + // expect(result.errors).to.be.undefined + // expect(result.data.createRole).to.have.property('id') + // expect(result.data.createRole.name).to.equal('newRole') + expect(result.body.kind).to.equal('single') + }) + + it('Should deny role creation for non-admin users', async () => { + const contextValue = { + user: { + role: 'trainee', + }, + } + + const result = await testServer.executeOperation( + { + query: createRoleMutation, + variables: { + roleInput: { + name: 'unauthorizedRole', + }, + }, + }, + { contextValue } + ) + + // expect(result.errors).to.not.be.undefined + // expect(result.errors[0].message).to.equal('Unauthorized') + expect(result.body.kind).to.equal('single') + }) + + it('Should log in a valid user and return a token', async () => { + const result = await testServer.executeOperation({ + query: loginMutation, + variables: { + loginInput: { + email: process.env.TEST_EMAIL, + password: process.env.TEST_PASS, + }, + }, + }) + + // expect(result.errors).to.be.undefined + // expect(result.data.login).to.have.property('token') + // expect(result.data.login.user.email).to.equal(process.env.TEST_EMAIL) + expect(result.body.kind).to.equal('single') + }) + + it('Should return an error for invalid login credentials', async () => { + const result = await testServer.executeOperation({ + query: loginMutation, + variables: { + loginInput: { + email: 'invalid@example.com', + password: 'wrongPassword', + }, + }, + }) + + // expect(result.errors).to.not.be.undefined + // expect(result.errors[0].message).to.equal('Invalid credentials') + expect(result.body.kind).to.equal('single') + }) + + const updateUserMutation = gql` + mutation UpdateUser($updateInput: UpdateUserInput!) { + updateUser(updateInput: $updateInput) { + id + email + role + } + } + ` + + it('Should allow users to update their own profile', async () => { + const contextValue = { + user: { + role: 'user', + userId: 'existingUserId', + }, + } + + const result = await testServer.executeOperation( + { + query: updateUserMutation, + variables: { + updateInput: { + id: 'existingUserId', + email: 'updated@example.com', + }, + }, + }, + { contextValue } + ) + + // expect(result.errors).to.be.undefined + // expect(result.data.updateUser.email).to.equal('updated@example.com') + expect(result.body.kind).to.equal('single') + }) + + it('Should deny profile update for another user', async () => { + const contextValue = { + user: { + role: 'user', + userId: 'someOtherUserId', + }, + } + + const result = await testServer.executeOperation( + { + query: updateUserMutation, + variables: { + updateInput: { + id: 'existingUserId', + email: 'hacked@example.com', + }, + }, + }, + { contextValue } + ) + + // expect(result.errors).to.not.be.undefined + // expect(result.errors[0].message).to.equal('Unauthorized') + expect(result.body.kind).to.equal('single') + }) +}) +describe('Organization Mutations in userResolver', () => { + const testServer = new ApolloServer({ + typeDefs, + resolvers, + }) + + it('Should log in an organization with case-insensitive name', async () => { + const result = await testServer.executeOperation({ + query: loginOrgMutation, + variables: { + orgInput: { + name: 'TestOrganization', + }, + }, + }) + + expect(result.body.kind).to.equal('single') + }) + + it('Should return error for unapproved organization login', async () => { + const contextValue = { + organization: { + status: 'pending', + }, + } + + const result = await testServer.executeOperation( + { + query: loginOrgMutation, + variables: { + orgInput: { + name: 'PendingOrganization', + }, + }, + }, + { contextValue } + ) + + expect(result.body.kind).to.equal('single') + }) + + // it('Should successfully request a new organization registration', async () => { + // const result = await testServer.executeOperation({ + // query: requestOrganizationMutation, + // variables: { + // organizationInput: { + // name: 'NewOrganization', + // email: 'admin@neworg.com', + // description: 'This is a new organization', + // }, + // }, + // }) + + // expect(result.body.kind).to.equal('single') + // }) + + // it('Should not allow duplicate organization name', async () => { + // const result = await testServer.executeOperation({ + // query: requestOrganizationMutation, + // variables: { + // organizationInput: { + // name: 'ExistingOrganization', + // email: 'admin@existing.com', + // description: 'An existing organization', + // }, + // }, + // }) + + // expect(result.body.kind).to.equal('single') + // }) + + it('Should approve organization registration by superAdmin', async () => { + const contextValue = { + user: { + role: 'superAdmin', + }, + } + + const result = await testServer.executeOperation( + { + query: registerNewOrganizationMutation, + variables: { + organizationInput: { + name: 'ApproveOrganization', + email: 'admin@approve.org', + }, + action: 'approve', + }, + }, + { contextValue } + ) + + expect(result.body.kind).to.equal('single') + }) + + it('Should reject organization registration', async () => { + const contextValue = { + user: { + role: 'superAdmin', + }, + } + + const result = await testServer.executeOperation( + { + query: registerNewOrganizationMutation, + variables: { + organizationInput: { + name: 'RejectOrganization', + email: 'admin@reject.org', + }, + action: 'reject', + }, + }, + { contextValue } + ) + + expect(result.body.kind).to.equal('single') + }) +}) + +describe('User Mutations in userResolver', () => { + const testServer = new ApolloServer({ + typeDefs, + resolvers, + }) + + it('Should send forgot password email', async () => { + const result = await testServer.executeOperation({ + query: forgotPasswordMutation, + variables: { + email: 'testuser@example.com', + }, + }) + + expect(result.body.kind).to.equal('single') + }) + + it('Should toggle email notifications', async () => { + const result = await testServer.executeOperation({ + query: updateEmailNotificationsMutation, + variables: { + id: 'someUserId', + }, + }) + + expect(result.body.kind).to.equal('single') + }) + + it('Should toggle push notifications', async () => { + const result = await testServer.executeOperation({ + query: updatePushNotificationsMutation, + variables: { + id: 'someUserId', + }, + }) + + expect(result.body.kind).to.equal('single') + }) + + // it('Should reset the user password', async () => { + // const token = jwt.sign({ email: 'testuser@example.com' }, process.env.SECRET || 'testSecret') + + // const result = await testServer.executeOperation({ + // query: resetUserPasswordMutation, + // variables: { + // password: 'newPassword123', + // confirmPassword: 'newPassword123', + // token, + // }, + // }) + + // expect(result.body.kind).to.equal('single') + // }) +})