Files
edr-platform/apps/edr-freight-api/src/modules/companies/companies.profile-approval.spec.ts
2026-08-18 12:44:02 +00:00

116 lines
3.7 KiB
TypeScript

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<CompanyProfile> = {
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<CompanyProfile>[] = [];
const profileRepo = {
update: jest.fn(async (_id: string, patch: Partial<CompanyProfile>) => {
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<unknown>) =>
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",
});
});
});