import { BadRequestException } from "@nestjs/common"; import { CompaniesService } from "./companies.service"; import { Company, CompanyStatus } from "./entities/company.entity"; import { CompanyProfile, ProfileStatus, ProfileType, } from "./entities/company-profile.entity"; /** * A rejection hands the role back to the customer: they fix what was flagged * and resubmit (`reapplyCompanyProfile` → Pending). The reviewer used to be * able to skip that entirely and approve straight out of Rejected — granting * the role over the documents that were just refused, while the customer's * "please fix this" note was still on their screen. */ function makeService(status: ProfileStatus) { const profile: Partial = { id: "profile-1", companyId: "company-1", type: ProfileType.importer, status, reference: null, reviewNote: status === ProfileStatus.Rejected ? "Licence expired" : null, }; const company = { id: "company-1", status: CompanyStatus.Pending, attributes: {}, }; const written: Partial[] = []; const profileRepo = { update: jest.fn(async (_id: string, patch: Partial) => { written.push(patch); Object.assign(profile, patch); return null; }), findOne: jest.fn(async () => profile), }; const companyRepo = { findOne: jest.fn(async () => company), update: jest.fn() }; const companyProfilesRepo = { findById: jest.fn(async () => profile), generateReference: jest.fn(async () => "IM-A00001"), }; const profilesRepo = { // Onboarding submitted — the other gate in this method is not what these // tests are about. findByCompanyId: jest.fn(async () => [{ onboardingCompleted: true }]), }; const filesService = { findWithOpenChangeRequest: jest.fn(async () => []) }; const dataSource = { transaction: jest.fn(async (cb: (m: unknown) => Promise) => cb({ findOne: jest.fn(async () => company), getRepository: (entity: unknown) => entity === Company ? companyRepo : profileRepo, }), ), }; const companyNotifier = { profileStatusChanged: jest.fn(), companyApproved: jest.fn() }; const service = new CompaniesService( {} as never, companyProfilesRepo as never, {} as never, {} as never, profilesRepo as never, {} as never, filesService as never, {} as never, {} as never, companyNotifier as never, dataSource as never, {} as never, ); return { service, profile, written, companyProfilesRepo }; } describe("approving an operational role", () => { it("refuses to approve a role the customer has not resubmitted", async () => { const { service, companyProfilesRepo } = makeService(ProfileStatus.Rejected); await expect( service.setCompanyProfileStatus("profile-1", ProfileStatus.Active), ).rejects.toBeInstanceOf(BadRequestException); // Refused before any reference could be minted against the rejected role. expect(companyProfilesRepo.generateReference).not.toHaveBeenCalled(); }); it("lets a reviewer undo their own rejection, and drops the note with it", async () => { const { service, written } = makeService(ProfileStatus.Rejected); await service.setCompanyProfileStatus("profile-1", ProfileStatus.Pending); expect(written[0]).toMatchObject({ status: ProfileStatus.Pending, reviewNote: null, }); }); it("still approves a role that is awaiting its first decision", async () => { const { service, written } = makeService(ProfileStatus.Pending); await service.setCompanyProfileStatus("profile-1", ProfileStatus.Active); expect(written[0]).toMatchObject({ status: ProfileStatus.Active, reference: "IM-A00001", }); }); });