Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add logic to initiate the cooperation closing process #1026

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions src/consts/validation.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,19 @@ const enums = {
LOGIN_ROLE_ENUM: ['student', 'tutor', 'admin'],
MAIN_ROLE_ENUM: ['student', 'tutor'],
STATUS_ENUM: ['active', 'blocked', 'deactivated'],
COOPERATION_STATUS_ENUM: ['pending', 'active', 'declined', 'closed'],
COOPERATION_STATUS_ENUM: ['pending', 'active', 'declined', 'closed', 'request to close'],
PARAMS_ENUM: ['id', 'categoryId', 'subjectId'],
OFFER_STATUS_ENUM: ['active', 'draft', 'closed'],
NOTIFICATION_TYPE_ENUM: ['new', 'requested', 'active', 'declined', 'updated', 'closed', 'deleted'],
NOTIFICATION_TYPE_ENUM: [
'new',
'requested',
'active',
'declined',
'updated',
'closed',
'deleted',
'request to close'
],
QUESTION_TYPE_ENUM: ['multipleChoice', 'openAnswer', 'oneAnswer'],
QUIZ_VIEW_ENUM: ['Stepper', 'Scroll'],
QUIZ_SETTINGS_ENUM: ['view', 'shuffle', 'pointValues', 'scoredResponses', 'correctAnswers'],
Expand Down
9 changes: 8 additions & 1 deletion src/services/cooperation.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ const handleResources = require('~/utils/handleResources')
const { createError, createForbiddenError } = require('~/utils/errorsHelper')
const { VALIDATION_ERROR, DOCUMENT_NOT_FOUND, ROLE_REQUIRED_FOR_ACTION } = require('~/consts/errors')
const { roles } = require('~/consts/auth')
const {
enums: { COOPERATION_STATUS_ENUM }
} = require('~/consts/validation')

const cooperationService = {
_validateCooperationUser: (cooperation, userId) => {
Expand Down Expand Up @@ -91,7 +94,11 @@ const cooperationService = {
await Cooperation.findByIdAndUpdate(id, { price, needAction: updatedNeedAction }).exec()
}
if (status) {
await Cooperation.findByIdAndUpdate(id, { status }).exec()
const isRequestToClose = status === COOPERATION_STATUS_ENUM[4]
const otherRole = currentUserRole === roles.STUDENT ? roles.TUTOR : roles.STUDENT
const updatedNeedAction = isRequestToClose ? otherRole : undefined

await Cooperation.findByIdAndUpdate(id, { status, needAction: updatedNeedAction }, { runValidators: true })
}
if (sections) {
cooperation.sections = await Promise.all(
Expand Down
142 changes: 109 additions & 33 deletions src/test/integration/controllers/cooperation.spec.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
const Offer = require('~/models/offer')
const User = require('~/models/user')
const Category = require('~/models/category')
const Subject = require('~/models/subject')
const Cooperation = require('~/models/cooperation')
Expand Down Expand Up @@ -104,6 +103,10 @@ const updatePrice = {
price: 150
}

const requestToCloseStatus = {
status: 'request to close'
}

const updatingSections = [
{
_id: '65bc2bec67c9f1ec287a1514',
Expand Down Expand Up @@ -161,10 +164,46 @@ const testInitiator = {
updatedAt: '2024-08-07T10:03:03.587Z'
}

const testTutor = {
_id: '66b346570182fc9e49b09647',
averageRating: {
student: 0,
tutor: 0
},
createdAt: '2024-08-07T10:03:03.488Z',
email: 'lovemagic@gmail.com',
firstName: 'albus',
lastLogin: '2024-08-07T10:03:03.587Z',
lastName: 'dumbledore',
mainSubjects: {
student: [],
tutor: []
},
nativeLanguage: null,
professionalBlock: {
awards: '',
education: '',
scientificActivities: '',
workExperience: ''
},
role: ['tutor'],
status: {
admin: 'active',
student: 'active',
tutor: 'active'
},
totalReviews: {
student: 0,
tutor: 0
},
updatedAt: '2024-08-07T10:03:03.587Z'
}

describe('Cooperation controller', () => {
let app,
server,
accessToken,
studentAccessToken,
tutorAccessToken,
testOffer,
anotherStudentAccessToken,
testCooperation,
Expand All @@ -177,10 +216,11 @@ describe('Cooperation controller', () => {
})

beforeEach(async () => {
accessToken = await testUserAuthentication(app, studentUserData)
studentAccessToken = await testUserAuthentication(app, studentUserData)
anotherStudentAccessToken = await testUserAuthentication(app, anotherStudentUserData)
testStudentUser = TokenService.validateAccessToken(accessToken)
testTutorUser = await User.create(tutorUserData)
testStudentUser = TokenService.validateAccessToken(studentAccessToken)
tutorAccessToken = await testUserAuthentication(app, tutorUserData)
testTutorUser = TokenService.validateAccessToken(tutorAccessToken)

const category = await Category.create({
name: 'Dark Magic',
Expand All @@ -196,20 +236,20 @@ describe('Cooperation controller', () => {
})

testOffer = await Offer.create({
author: testTutorUser._id,
author: testTutorUser.id,
subject: subject._id,
category: category._id,
...testOfferData
})

testActiveQuiz = await Quiz.create({
author: testTutorUser._id,
author: testTutorUser.id,
category: category._id,
...testActiveQuizData
})

const testLessonResource = await Lesson.create({
author: testTutorUser._id,
author: testTutorUser.id,
content: '<p><strong>Solving Quadratic Equations Using the Quadratic Formula</strong></p>',
description: 'The quadratic formula',
title: 'Solving Quadratic Equations Using the Quadratic Formula',
Expand All @@ -235,9 +275,9 @@ describe('Cooperation controller', () => {

testCooperation = await app
.post(endpointUrl)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])
.send({
receiver: testTutorUser._id,
receiver: testTutorUser.id,
receiverRole: tutorUserData.role[0],
offer: testOffer._id,
sections: updatedTestCooperationData.sections,
Expand All @@ -264,7 +304,7 @@ describe('Cooperation controller', () => {
const response = await app
.get(endpointUrl)
.query(query)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])

expect(response.status).toBe(200)
expect(response.body.count).toBe(1)
Expand All @@ -275,7 +315,7 @@ describe('Cooperation controller', () => {
_id: testOffer._id
},
initiator: testStudentUser.id,
receiver: testTutorUser._id,
receiver: testTutorUser.id,
proficiencyLevel: testCooperationData.proficiencyLevel,
price: testCooperationData.price,
title: testCooperationData.title,
Expand All @@ -297,7 +337,7 @@ describe('Cooperation controller', () => {
it('get cooperation by ID', async () => {
const response = await app
.get(endpointUrl + testCooperation.body._id)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])

expect(response.status).toBe(200)
expect(response.body).toMatchObject({
Expand All @@ -313,7 +353,13 @@ describe('Cooperation controller', () => {
lastLogin: expect.any(String),
_id: expect.any(String)
},
receiver: testTutorUser._id,
receiver: {
...testTutor,
createdAt: expect.any(String),
updatedAt: expect.any(String),
lastLogin: expect.any(String),
_id: expect.any(String)
},
receiverRole: tutorUserData.role[0],
proficiencyLevel: testCooperationData.proficiencyLevel,
price: testCooperationData.price,
Expand Down Expand Up @@ -353,7 +399,7 @@ describe('Cooperation controller', () => {
it('should throw DOCUMENT_NOT_FOUND', async () => {
const response = await app
.get(endpointUrl + nonExistingCooperationId)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])

expectError(404, DOCUMENT_NOT_FOUND([Cooperation.modelName]), response)
})
Expand All @@ -372,7 +418,7 @@ describe('Cooperation controller', () => {
_id: testCooperation._body._id,
offer: testOffer._id,
initiator: testStudentUser.id,
receiver: testTutorUser._id,
receiver: testTutorUser.id,
receiverRole: tutorUserData.role[0],
proficiencyLevel: testCooperationData.proficiencyLevel,
price: testCooperationData.price,
Expand All @@ -397,10 +443,10 @@ describe('Cooperation controller', () => {
it('should throw DOCUMENT_NOT_FOUND for offer entity', async () => {
const response = await app
.post(endpointUrl)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])
.send({
initiator: testStudentUser.id,
receiver: testTutorUser._id,
receiver: testTutorUser.id,
offer: nonExistingOfferId,
...testCooperationData
})
Expand All @@ -419,7 +465,7 @@ describe('Cooperation controller', () => {
it('should throw FORBIDDEN if user role does not match needAction role when updating price', async () => {
const response = await app
.patch(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])
.send(updatePrice)

expectError(403, FORBIDDEN, response)
Expand All @@ -428,26 +474,56 @@ describe('Cooperation controller', () => {
it('should update the status of a cooperation', async () => {
const updateResponse = await app
.patch(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])
.send(updateStatus)

const response = await app
.get(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])

expect(updateResponse.status).toBe(204)
expect(response.body.status).toBe(updateStatus.status)
})

it('should change needAction to "tutor" after closing request from student', async () => {
const updateResponse = await app
.patch(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${studentAccessToken}`])
.send(requestToCloseStatus)

const response = await app
.get(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${studentAccessToken}`])

expect(updateResponse.status).toBe(204)
expect(response.body.status).toBe(requestToCloseStatus.status)
expect(response.body.needAction).toBe('tutor')
})

it('should change needAction to "student" after closing request from tutor', async () => {
const updateResponse = await app
.patch(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${tutorAccessToken}`])
.send(requestToCloseStatus)

const response = await app
.get(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${tutorAccessToken}`])

expect(updateResponse.status).toBe(204)
expect(response.body.status).toBe(requestToCloseStatus.status)
expect(response.body.needAction).toBe('student')
})

it('should update the sections of a cooperation', async () => {
const updateResponse = await app
.patch(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])
.send({ sections: updatingSections })

const response = await app
.get(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])

expect(updateResponse.status).toBe(204)
expect(response.body.sections).toEqual(updatedSections)
Expand All @@ -456,12 +532,12 @@ describe('Cooperation controller', () => {
it('should update the available quizzes of a cooperation', async () => {
const updateResponse = await app
.patch(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])
.send({ availableQuizzes: [testActiveQuiz._id] })

const response = await app
.get(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])

expect(updateResponse.status).toBe(204)
expect(response.body.availableQuizzes).toEqual([testActiveQuiz._id.toString()])
Expand All @@ -470,12 +546,12 @@ describe('Cooperation controller', () => {
it('should update the finished quizzes of a cooperation', async () => {
const updateResponse = await app
.patch(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])
.send({ finishedQuizzes: [testActiveQuiz._id] })

const response = await app
.get(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])

expect(updateResponse.status).toBe(204)
expect(response.body.finishedQuizzes).toEqual([testActiveQuiz._id.toString()])
Expand All @@ -484,7 +560,7 @@ describe('Cooperation controller', () => {
it('should throw DOCUMENT_NOT_FOUND', async () => {
const response = (testCooperation = await app
.patch(endpointUrl + nonExistingCooperationId)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])
.send(updateStatus))

expectError(404, DOCUMENT_NOT_FOUND([Cooperation.modelName]), response)
Expand All @@ -499,7 +575,7 @@ describe('Cooperation controller', () => {
it('should throw VALIDATION_ERROR', async () => {
const response = await app
.patch(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])
.send({ ...updateStatus, ...updatePrice })

expectError(409, VALIDATION_ERROR(validationErrorMessage), response)
Expand All @@ -521,14 +597,14 @@ describe('Cooperation controller', () => {

const updateResponse = await app
.patch(`${endpointUrl}${testCooperation._body._id}/${resourceId}/completionStatus`)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])
.send(updatedResourceCompletionStatus)

expect(updateResponse.status).toBe(204)

const response = await app
.get(endpointUrl + testCooperation._body._id)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])

expect(response.body.sections[0].resources[0].resource.completionStatus).toBe(
updatedResourceCompletionStatus.status
Expand All @@ -541,7 +617,7 @@ describe('Cooperation controller', () => {

const response = await app
.patch(`${endpointUrl}${testCooperation._body._id}/${resourceId}/completionStatus`)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])
.send(invalidCompletionStatus)

expectError(422, FIELD_IS_NOT_OF_PROPER_ENUM_VALUE('completionStatus', RESOURCE_COMPLETION_STATUS_ENUM), response)
Expand All @@ -552,7 +628,7 @@ describe('Cooperation controller', () => {

const updateResponse = await app
.patch(`${endpointUrl}${testCooperation._body._id}/${resourceId}/completionStatus`)
.set('Cookie', [`accessToken=${accessToken}`])
.set('Cookie', [`accessToken=${studentAccessToken}`])
.send(updatedResourceCompletionStatus)

expectError(404, DOCUMENT_NOT_FOUND([`Resource in ${Cooperation.modelName}`]), updateResponse)
Expand Down
Loading