Skip to content

Commit

Permalink
Add logic to initiate the cooperation closing process (#1026)
Browse files Browse the repository at this point in the history
* add 'request to close' status to validation enums

* add needAction field swapping after status updating

* update cooperation tests
  • Loading branch information
ShadowOfTheSpace authored Dec 4, 2024
1 parent bc960bc commit f73d300
Show file tree
Hide file tree
Showing 3 changed files with 128 additions and 36 deletions.
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: '[email protected]',
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

0 comments on commit f73d300

Please sign in to comment.