betagouv/service-national-universel

View on GitHub
api/src/young/youngService.test.ts

Summary

Maintainability
A
0 mins
Test Coverage
import * as youngService from "./youngService";
import {
  findYoungByIdOrThrow,
  shouldSwitchYoungByIdToLC,
  switchYoungByIdToLC,
  findYoungsByClasseId,
  getValidatedYoungsWithSession,
  getYoungsImageRight,
  getYoungsParentAllowSNU,
  mightAddInProgressStatus,
} from "./youngService";
import { generatePdfIntoBuffer } from "../utils/pdf-renderer";
import { YoungDocument, YoungModel } from "../models";
import { ERRORS, UserDto, YOUNG_PHASE, YOUNG_STATUS, YOUNG_STATUS_PHASE1 } from "snu-lib";

const mockBuffer = Buffer.from("pdf");

afterEach(() => {
  jest.clearAllMocks();
});

jest.mock("../utils/pdf-renderer", () => ({
  ...jest.requireActual("../utils/pdf-renderer"),
  generatePdfIntoBuffer: jest.fn().mockReturnValue(Buffer.from("pdf")),
}));

describe("YoungService.generateConvocationsForMultipleYoungs", () => {
  afterEach(() => {
    jest.clearAllMocks();
  });
  it("should return one PDF convocations for 2 youngs", async () => {
    const young1 = {
      _id: "1",
      status: YOUNG_STATUS.VALIDATED,
      sessionPhase1Id: "session1",
      statusPhase1: YOUNG_STATUS_PHASE1.DONE,
      transportInfoGivenByLocal: "true",
      source: "OTHER",
    };
    const young2 = { _id: "2", status: YOUNG_STATUS.VALIDATED, sessionPhase1Id: "session2", statusPhase1: YOUNG_STATUS_PHASE1.NOT_DONE, meetingPointId: "mp2", source: "CLE" };

    const youngsPdfCreated = await youngService.generateConvocationsForMultipleYoungs([young1, young2]);

    expect(youngsPdfCreated).toEqual(mockBuffer);
    expect(generatePdfIntoBuffer).toHaveBeenCalledTimes(1);
  });
  it("should return one PDF consentment for 2 youngs", async () => {
    const young1 = { _id: "1", name: "John Doe", status: YOUNG_STATUS.VALIDATED, parentAllowSNU: "true" };
    const young2 = { _id: "3", name: "Alice Brown", status: YOUNG_STATUS.VALIDATED, parentAllowSNU: "true" };

    const youngsPdfCreated = await youngService.generateConsentementForMultipleYoungs([young1, young2]);

    expect(youngsPdfCreated).toEqual(mockBuffer);
    expect(generatePdfIntoBuffer).toHaveBeenCalledTimes(1);
  });
  it("should return one PDF imageRight for 2 youngs", async () => {
    const young1 = { _id: "1", name: "John Doe", status: YOUNG_STATUS.VALIDATED, imageRight: "true" };
    const young2 = { _id: "2", name: "Jane Smith", status: YOUNG_STATUS.IN_PROGRESS, imageRight: "false" };

    const youngsPdfCreated = await youngService.generateImageRightForMultipleYoungs([young1, young2]);

    expect(youngsPdfCreated).toEqual(mockBuffer);
    expect(generatePdfIntoBuffer).toHaveBeenCalledTimes(1);
  });
});

describe("YoungService.findYoungsByClasseId", () => {
  it("should return an array of young objects when found", async () => {
    const mockYoungs = [
      { _id: "1", name: "John Doe", classeId: "classe1" },
      { _id: "2", name: "Jane Smith", classeId: "classe1" },
    ];
    // Mock the YoungModel.find method
    YoungModel.find = jest.fn().mockResolvedValue(mockYoungs);

    const result = await findYoungsByClasseId("classe1");
    expect(result).toEqual(mockYoungs);
    expect(YoungModel.find).toHaveBeenCalledWith({ classeId: "classe1" });
  });

  it("should return an empty array when no youngs are found", async () => {
    // Mock the YoungModel.find method to return an empty array
    YoungModel.find = jest.fn().mockResolvedValue([]);

    const result = await findYoungsByClasseId("classe2");
    expect(result).toEqual([]);
    expect(YoungModel.find).toHaveBeenCalledWith({ classeId: "classe2" });
  });
});

describe("YoungService.getYoungsParentAllowSNU", () => {
  it("should return an array of young objects with valid status and parentAllowSNU true", () => {
    const youngs = [
      { _id: "1", name: "John Doe", status: YOUNG_STATUS.VALIDATED, parentAllowSNU: "true" },
      { _id: "2", name: "Jane Smith", status: YOUNG_STATUS.IN_PROGRESS, parentAllowSNU: "false" },
      { _id: "3", name: "Alice Brown", status: YOUNG_STATUS.WAITING_CORRECTION, parentAllowSNU: "true" },
      { _id: "4", name: "Bob Green", status: YOUNG_STATUS.WITHDRAWN, parentAllowSNU: "true" },
    ];

    const expected = [
      { _id: "1", name: "John Doe", status: YOUNG_STATUS.VALIDATED, parentAllowSNU: "true" },
      { _id: "3", name: "Alice Brown", status: YOUNG_STATUS.WAITING_CORRECTION, parentAllowSNU: "true" },
    ];

    const result = getYoungsParentAllowSNU(youngs);
    expect(result).toEqual(expected);
  });

  it("should return an empty array when no youngs have valid status or parentAllowSNU true", () => {
    const youngs = [
      { _id: "1", name: "John Doe", status: YOUNG_STATUS.WITHDRAWN, parentAllowSNU: "true" },
      { _id: "2", name: "Jane Smith", status: YOUNG_STATUS.IN_PROGRESS, parentAllowSNU: "false" },
    ];

    const result = getYoungsParentAllowSNU(youngs);
    expect(result).toEqual([]);
  });
});

describe("YoungService.getYoungsImageRight", () => {
  it("should return an array of young objects with valid status and imageRight set to true or false", () => {
    const youngs = [
      { _id: "1", name: "John Doe", status: YOUNG_STATUS.VALIDATED, imageRight: "true" },
      { _id: "2", name: "Jane Smith", status: YOUNG_STATUS.IN_PROGRESS, imageRight: "false" },
      { _id: "3", name: "Alice Brown", status: YOUNG_STATUS.WAITING_CORRECTION, imageRight: "true" },
      { _id: "4", name: "Bob Green", status: YOUNG_STATUS.WITHDRAWN, imageRight: "true" },
      { _id: "5", name: "Eve Black", status: YOUNG_STATUS.VALIDATED, imageRight: "undefined" },
    ];

    const expected = [
      { _id: "1", name: "John Doe", status: YOUNG_STATUS.VALIDATED, imageRight: "true" },
      { _id: "2", name: "Jane Smith", status: YOUNG_STATUS.IN_PROGRESS, imageRight: "false" },
      { _id: "3", name: "Alice Brown", status: YOUNG_STATUS.WAITING_CORRECTION, imageRight: "true" },
    ];

    const result = getYoungsImageRight(youngs);
    expect(result).toEqual(expected);
  });

  it("should return an empty array when no youngs have valid status or imageRight set to true or false", () => {
    const youngs = [
      { _id: "1", name: "John Doe", status: YOUNG_STATUS.WITHDRAWN, imageRight: "true" },
      { _id: "2", name: "Jane Smith", status: YOUNG_STATUS.WITHDRAWN, imageRight: "false" },
      { _id: "3", name: "Alice Brown", status: YOUNG_STATUS.VALIDATED, imageRight: "undefined" },
    ];

    const result = getYoungsImageRight(youngs);
    expect(result).toEqual([]);
  });
});

describe("YoungService.getValidatedYoungsWithSession", () => {
  it("should return an array of young objects that are validated, have a session, and meet the criteria", () => {
    const youngs = [
      {
        _id: "1",
        status: YOUNG_STATUS.VALIDATED,
        sessionPhase1Id: "session1",
        statusPhase1: YOUNG_STATUS_PHASE1.AFFECTED,
        meetingPointId: "mp1",
        deplacementPhase1Autonomous: "true",
        source: "OTHER",
      },
      { _id: "2", status: YOUNG_STATUS.VALIDATED, sessionPhase1Id: "session2", statusPhase1: YOUNG_STATUS_PHASE1.DONE, transportInfoGivenByLocal: "true", source: "OTHER" },
      { _id: "3", status: YOUNG_STATUS.VALIDATED, sessionPhase1Id: "session3", statusPhase1: YOUNG_STATUS_PHASE1.NOT_DONE, meetingPointId: "mp2", source: "CLE" },
    ];

    const result = getValidatedYoungsWithSession(youngs);
    expect(result).toEqual(youngs);
  });

  it("should return an empty array when youngs do not have a valid session or do not meet the criteria", () => {
    const youngs = [
      { _id: "1", status: YOUNG_STATUS.VALIDATED, sessionPhase1Id: "session1", statusPhase1: YOUNG_STATUS_PHASE1.WITHDRAWN, meetingPointId: "mp1" },
      { _id: "2", status: YOUNG_STATUS.WITHDRAWN, sessionPhase1Id: "session2", statusPhase1: YOUNG_STATUS_PHASE1.AFFECTED, transportInfoGivenByLocal: "true" },
      { _id: "3", status: YOUNG_STATUS.VALIDATED, sessionPhase1Id: undefined, statusPhase1: YOUNG_STATUS_PHASE1.DONE, deplacementPhase1Autonomous: "false" },
    ];

    const result = getValidatedYoungsWithSession(youngs);
    expect(result).toEqual([]);
  });
});

describe("YoungService.findYoungByIdOrThrow", () => {
  it("should return a young object when found", async () => {
    const mockYoung = {
      _id: "1",
      name: "John Doe",
      status: YOUNG_STATUS.VALIDATED,
    };
    YoungModel.findById = jest.fn().mockResolvedValue(mockYoung);

    const result = await findYoungByIdOrThrow("1");
    expect(result).toBe(mockYoung);
    expect(YoungModel.findById).toHaveBeenCalledWith("1");
  });

  it("should throw an error when young is not found", async () => {
    YoungModel.findById = jest.fn().mockResolvedValue(null);

    await expect(findYoungByIdOrThrow("2")).rejects.toThrow(ERRORS.YOUNG_NOT_FOUND);
    expect(YoungModel.findById).toHaveBeenCalledWith("2");
  });
});

describe("YoungService.switchYoungByIdToLC()", () => {
  it("should update the young's properties and save successfully", async () => {
    const mockYoung = {
      set: jest.fn(),
      save: jest.fn().mockResolvedValue("mocked saved young"),
    };
    jest.spyOn(youngService, "findYoungByIdOrThrow").mockResolvedValue(mockYoung as unknown as YoungDocument);

    const result = await switchYoungByIdToLC("123");

    expect(mockYoung.set).toHaveBeenCalledWith({
      cohesionCenterId: undefined,
      sessionPhase1Id: undefined,
      meetingPointId: undefined,
      ligneId: undefined,
      deplacementPhase1Autonomous: undefined,
      transportInfoGivenByLocal: undefined,
      cohesionStayPresence: undefined,
      presenceJDM: undefined,
      departInform: undefined,
      departSejourAt: undefined,
      departSejourMotif: undefined,
      departSejourMotifComment: undefined,
      youngPhase1Agreement: "false",
      hasMeetingInformation: undefined,
      cohesionStayMedicalFileReceived: undefined,
    });
    expect(mockYoung.save).toHaveBeenCalledTimes(1);
    expect(result).toBe("mocked saved young");
  });
});

describe("shouldSwitchYoungByIdToLC", () => {
  const mockYoung = {
    status: YOUNG_STATUS.VALIDATED,
    phase: YOUNG_PHASE.INSCRIPTION,
  };

  it("returns true if conditions are met for switching to LC", async () => {
    jest.spyOn(youngService, "findYoungByIdOrThrow").mockResolvedValue(mockYoung as any);
    const result = await shouldSwitchYoungByIdToLC("youngId", YOUNG_STATUS.WAITING_LIST);
    expect(result).toBe(true);
  });

  it("returns false if the status is not VALIDATED", async () => {
    jest.spyOn(youngService, "findYoungByIdOrThrow").mockResolvedValue({ ...mockYoung, status: YOUNG_STATUS.ABANDONED } as any);
    const result = await shouldSwitchYoungByIdToLC("youngId", YOUNG_STATUS.WAITING_LIST);
    expect(result).toBe(false);
  });

  it("returns false if the phase is not INSCRIPTION", async () => {
    jest.spyOn(youngService, "findYoungByIdOrThrow").mockResolvedValue({ ...mockYoung, phase: YOUNG_PHASE.COHESION_STAY } as any);
    const result = await shouldSwitchYoungByIdToLC("youngId", YOUNG_STATUS.WAITING_LIST);
    expect(result).toBe(false);
  });

  it("returns false if the new status is not WAITING_LIST", async () => {
    jest.spyOn(youngService, "findYoungByIdOrThrow").mockResolvedValue(mockYoung as any);
    const result = await shouldSwitchYoungByIdToLC("youngId", YOUNG_STATUS.VALIDATED);
    expect(result).toBe(false);
  });
});

describe("addInProgressStatusToPatch", () => {
  let mockYoung;
  let mockUser;

  beforeEach(() => {
    mockYoung = {
      status: YOUNG_STATUS.WAITING_VALIDATION,
      save: jest.fn(),
      patches: {
        find: jest.fn().mockResolvedValue([{ ops: [{ path: "/status", value: YOUNG_STATUS.WAITING_CORRECTION }] }]),
      },
      set: jest.fn(),
    };
    mockUser = {} as UserDto;
    YoungModel.find = jest.fn().mockResolvedValue(mockYoung);
  });

  it("should add IN_PROGRESS status to patch if not already present", async () => {
    mockYoung.status = YOUNG_STATUS.VALIDATED;
    const findSpy = jest.spyOn(mockYoung.patches, "find");
    findSpy.mockReturnValue(Promise.resolve([{ ops: [{ path: "/status", value: YOUNG_STATUS.WAITING_CORRECTION }] }]));
    await mightAddInProgressStatus(mockYoung, mockUser);
    expect(findSpy).toHaveBeenCalled();
    expect(mockYoung.save).toHaveBeenCalledWith({ fromUser: mockUser });
    expect(mockYoung.save).toHaveBeenCalledTimes(2);
  });

  it("should not add IN_PROGRESS status to patch if already present", async () => {
    const findSpy = jest.spyOn(mockYoung.patches, "find");
    findSpy.mockReturnValue(Promise.resolve([{ ops: [{ path: "/status", value: YOUNG_STATUS.IN_PROGRESS }] }]));
    await mightAddInProgressStatus(mockYoung, mockUser);
    expect(findSpy).toHaveBeenCalled();
    expect(mockYoung.save).not.toHaveBeenCalled();
  });

  it("should not add IN_PROGRESS status to patch if status is IN_PROGRESS", async () => {
    mockYoung.status = YOUNG_STATUS.IN_PROGRESS;
    const findSpy = jest.spyOn(mockYoung.patches, "find");
    findSpy.mockReturnValue(Promise.resolve([{ ops: [{ path: "/status", value: YOUNG_STATUS.WAITING_CORRECTION }] }]));
    await mightAddInProgressStatus(mockYoung, mockUser);
    expect(findSpy).not.toHaveBeenCalled();
    expect(mockYoung.save).not.toHaveBeenCalled();
  });

  it("should not add IN_PROGRESS status if status is not VALIDATED", async () => {
    mockYoung.status = YOUNG_STATUS.WAITING_VALIDATION;
    const findSpy = jest.spyOn(mockYoung.patches, "find");
    findSpy.mockReturnValue(Promise.resolve([{ ops: [{ path: "/status", value: YOUNG_STATUS.WAITING_CORRECTION }] }]));
    await mightAddInProgressStatus(mockYoung, mockUser);
    expect(findSpy).toHaveBeenCalled();
    expect(mockYoung.save).not.toHaveBeenCalled();
  });
});

const buildYoung = (id = "id") => ({ firstName: "firstName", lastName: "lastName", _id: id });