Skip to content

Commit

Permalink
feat(api): prevent session from being cancelled when already finalized
Browse files Browse the repository at this point in the history
  • Loading branch information
alexandrecoin committed Jan 16, 2025
1 parent 1405167 commit 16052c5
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,10 @@ class SessionManagement {
return this.publishedAt !== null;
}

get isFinalized() {
return this.finalized !== null;
}

isSupervisable(invigilatorPassword) {
return this.invigilatorPassword === invigilatorPassword;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
/**
* @typedef {import('./index.js'.CertificationCourseRepository} CertificationCourseRepository
* @typedef {import('./index.js'.SessionRepository} SessionRepository
*/

import CertificationCancelled from '../../../../../lib/domain/events/CertificationCancelled.js';
import { SessionAlreadyFinalizedError } from '../errors.js';

/**
* @param {Object} params
* @param {number} params.certificationCourseId
* @param {CertificationCourseRepository} params.certificationCourseRepository
* @param {SessionRepository} params.sessionRepository
* @returns {Promise<CertificationCancelled>}
*/
export const cancelCertificationCourse = async function ({
certificationCourseId,
juryId,
certificationCourseRepository,
sessionRepository,
}) {
const certificationCourse = await certificationCourseRepository.get({ id: certificationCourseId });
const session = await sessionRepository.get({ id: certificationCourse.id });

if (session.isFinalized) {
throw new SessionAlreadyFinalizedError();
}

certificationCourse.cancel();
await certificationCourseRepository.update({ certificationCourse });

Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,74 @@
import CertificationCancelled from '../../../../../../lib/domain/events/CertificationCancelled.js';
import { SessionAlreadyFinalizedError } from '../../../../../../src/certification/session-management/domain/errors.js';
import { cancelCertificationCourse } from '../../../../../../src/certification/session-management/domain/usecases/cancel-certification-course.js';
import { domainBuilder, expect, sinon } from '../../../../../test-helper.js';
import { catchErr, domainBuilder, expect, sinon } from '../../../../../test-helper.js';

describe('Certification | Session-management | Unit | Domain | UseCases | cancel-certification-course', function () {
it('should cancel the certification course', async function () {
// given
const certificationCourse = domainBuilder.buildCertificationCourse({ id: 123 });
sinon.spy(certificationCourse, 'cancel');
const certificationCourseRepository = {
update: sinon.stub(),
get: sinon.stub(),
};
certificationCourseRepository.get.withArgs({ id: 123 }).resolves(certificationCourse);
certificationCourseRepository.update.resolves();
describe('when session is finalized', function () {
it('should cancel the certification course', async function () {
// given
const juryId = 123;
const session = domainBuilder.certification.sessionManagement.buildSession({
finalizedAt: new Date('2020-01-01'),
});
const certificationCourse = domainBuilder.buildCertificationCourse({ id: 123, sessionId: session.id });
sinon.spy(certificationCourse, 'cancel');
const certificationCourseRepository = {
update: sinon.stub(),
get: sinon.stub(),
};
const sessionRepository = {
get: sinon.stub(),
};
certificationCourseRepository.get.withArgs({ id: 123 }).resolves(certificationCourse);
certificationCourseRepository.update.resolves();
sessionRepository.get.withArgs({ id: certificationCourse.sessionId }).resolves(session);

// when
const cancelledEvent = await cancelCertificationCourse({
certificationCourseId: 123,
certificationCourseRepository,
// when
const cancelledEvent = await cancelCertificationCourse({
certificationCourseId: 123,
certificationCourseRepository,
sessionRepository,
juryId,
});

// then
expect(certificationCourse.cancel).to.have.been.calledOnce;
expect(certificationCourseRepository.update).to.have.been.calledWithExactly({ certificationCourse });
expect(cancelledEvent).to.deepEqualInstance(new CertificationCancelled({ certificationCourseId: 123, juryId }));
});
});

describe('when session is not finalized', function () {
it('should not cancel the certification course', async function () {
// given
const juryId = 123;
const session = domainBuilder.certification.sessionManagement.buildSession({ finalizedAt: null });
const certificationCourse = domainBuilder.buildCertificationCourse({ id: 123, sessionId: session.id });
sinon.spy(certificationCourse, 'cancel');
const certificationCourseRepository = {
update: sinon.stub(),
get: sinon.stub(),
};
const sessionRepository = {
get: sinon.stub(),
};
certificationCourseRepository.get.withArgs({ id: 123 }).resolves(certificationCourse);
certificationCourseRepository.update.resolves();
sessionRepository.get.withArgs({ id: certificationCourse.sessionId }).resolves(session);

// then
expect(certificationCourse.cancel).to.have.been.calledOnce;
expect(certificationCourseRepository.update).to.have.been.calledWithExactly({ certificationCourse });
expect(cancelledEvent).to.deepEqualInstance(new CertificationCancelled({ certificationCourseId: 123 }));
// when
const error = await catchErr(cancelCertificationCourse)({
certificationCourseId: 123,
certificationCourseRepository,
sessionRepository,
juryId,
});

// then
expect(certificationCourse.cancel).to.not.have.been.called;
expect(certificationCourseRepository.update).to.not.have.been.called;
expect(error).to.be.instanceOf(SessionAlreadyFinalizedError);
});
});
});

0 comments on commit 16052c5

Please sign in to comment.