Server/project/tests/controllers/InsuranceController.test.ts
Brent c71aaa7fb3 Use dynamic ragfair pricing for insurance deletion sorting (!294)
Change sort value for insured attachments to use dynamic ragfair prices.
Small change to method to return directly instead of setting passed-by-value parameter.

https://dev.sp-tarkov.com/SPT-AKI/Issues/issues/618

Reviewed-on: https://dev.sp-tarkov.com/SPT-AKI/Server/pulls/294
Co-authored-by: Brent <wiggyvidyadev@gmail.com>
Co-committed-by: Brent <wiggyvidyadev@gmail.com>
2024-04-17 13:10:43 +00:00

1670 lines
69 KiB
TypeScript

/* eslint-disable @typescript-eslint/naming-convention */
import "reflect-metadata";
import { container } from "tsyringe";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { InsuranceController } from "@spt-aki/controllers/InsuranceController";
import { ProfileInsuranceFactory } from "@tests/__factories__/ProfileInsurance.factory";
import { ItemHelper } from "@spt-aki/helpers/ItemHelper";
import { TraderHelper } from "@spt-aki/helpers/TraderHelper";
import { Item } from "@spt-aki/models/eft/common/tables/IItem";
import { Insurance } from "@spt-aki/models/eft/profile/IAkiProfile";
import { MessageType } from "@spt-aki/models/enums/MessageType";
describe("InsuranceController", () =>
{
let insuranceController: any; // Using "any" to access private/protected methods without type errors.
let insuranceFixture: Insurance[];
beforeEach(() =>
{
insuranceController = container.resolve<InsuranceController>("InsuranceController");
insuranceFixture = new ProfileInsuranceFactory().adjustPackageDates().get();
});
afterEach(() =>
{
vi.restoreAllMocks();
});
describe("processReturn", () =>
{
it("should process return for all profiles", () =>
{
const session1 = "session1";
const session2 = "session2";
const profiles = { [session1]: {}, [session2]: {} };
const getProfilesSpy = vi.spyOn(insuranceController.saveServer, "getProfiles").mockReturnValue(profiles);
const processReturnByProfileSpy = vi.spyOn(insuranceController, "processReturnByProfile").mockReturnValue(
vi.fn(),
);
// Execute the method.
insuranceController.processReturn();
// Should make a call to get all of the profiles.
expect(getProfilesSpy).toHaveBeenCalledTimes(1);
// Should process each returned profile.
expect(processReturnByProfileSpy).toHaveBeenCalledTimes(2);
expect(processReturnByProfileSpy).toHaveBeenCalledWith(session1);
expect(processReturnByProfileSpy).toHaveBeenCalledWith(session2);
});
it("should not attempt to process profiles if no profiles exist", () =>
{
const processReturnByProfileSpy = vi.spyOn(insuranceController, "processReturnByProfile")
.mockImplementation(vi.fn());
// Execute the method.
insuranceController.processReturn();
// Should not process any profiles.
expect(processReturnByProfileSpy).not.toHaveBeenCalled();
});
});
describe("processReturnByProfile", () =>
{
it("should process insurance for a profile", () =>
{
const sessionId = "session-id";
// Mock internal methods.
const mockFilterInsuredItems = vi.spyOn(insuranceController, "filterInsuredItems").mockReturnValue(
insuranceFixture,
);
const mockProcessInsuredItems = vi.spyOn(insuranceController, "processInsuredItems").mockImplementation(
vi.fn(),
);
insuranceController.processReturnByProfile(sessionId);
// Verify that the correct methods were called.
expect(mockFilterInsuredItems).toBeCalledTimes(1);
expect(mockProcessInsuredItems).toHaveBeenNthCalledWith(1, insuranceFixture, sessionId);
});
it("should skip processing if there are no insurance packages found within the profile", () =>
{
const sessionId = "session-id";
// Mock internal methods.
const mockFilterInsuredItems = vi.spyOn(insuranceController, "filterInsuredItems").mockReturnValue([]); // Return an empty array.
const mockProcessInsuredItems = vi.spyOn(insuranceController, "processInsuredItems").mockImplementation(
vi.fn(),
);
insuranceController.processReturnByProfile(sessionId);
// Verify that the correct methods were called.
expect(mockFilterInsuredItems).toBeCalledTimes(1);
expect(mockProcessInsuredItems).not.toHaveBeenCalled();
});
});
describe("filterInsuredItems", () =>
{
it("should return all insurance packages if no time is specified", () =>
{
const sessionID = "session-id";
const insured = JSON.parse(JSON.stringify(insuranceFixture));
// Mock getProfile to return the fixture.
const mockGetProfile = vi.spyOn(insuranceController.saveServer, "getProfile").mockReturnValue({
insurance: insured,
});
const mockLoggerDebug = vi.spyOn(insuranceController.logger, "debug");
// Execute the method.
const insuredFiltered = insuranceController.filterInsuredItems(sessionID);
// Verify that the correct methods were called.
expect(mockGetProfile).toBeCalledTimes(1);
expect(mockLoggerDebug).toBeCalledTimes(1);
expect(insuredFiltered.length).toBe(insuranceFixture.length);
});
it("should filter out insurance packages with scheduledTime values in the future", () =>
{
const sessionID = "session-id";
const insured = JSON.parse(JSON.stringify(insuranceFixture));
// Set the scheduledTime to 2 hours in the future so it should be skipped over.
insured[0].scheduledTime = Math.floor((Date.now() / 1000) + (2 * 60 * 60));
// Mock getProfile to return the fixture.
const mockGetProfile = vi.spyOn(insuranceController.saveServer, "getProfile").mockReturnValue({
insurance: insured,
});
const mockLoggerDebug = vi.spyOn(insuranceController.logger, "debug");
// Execute the method.
const insuredFiltered = insuranceController.filterInsuredItems(sessionID);
// Verify that the correct methods were called.
expect(mockGetProfile).toBeCalledTimes(1);
expect(mockLoggerDebug).toBeCalledTimes(1);
expect(insuredFiltered.length).toBe(insuranceFixture.length - 1); // Should be 1 less than the original fixture.
});
it("should return an empty array if no insurance packages match the criteria", () =>
{
const sessionID = "session-id";
const insured = JSON.parse(JSON.stringify(insuranceFixture));
// Mock getProfile to return the fixture.
const mockGetProfile = vi.spyOn(insuranceController.saveServer, "getProfile").mockReturnValue({
insurance: insured,
});
const mockLoggerDebug = vi.spyOn(insuranceController.logger, "debug");
// Execute the method, passing in a time that's two hours in the past. The function should use this past
// date as the target to judge if an insurance package is ready to be sent or not.
const insuredFiltered = insuranceController.filterInsuredItems(
sessionID,
Math.floor((Date.now() / 1000) - (2 * 60 * 60)),
);
// Verify that the correct methods were called.
expect(mockGetProfile).toBeCalledTimes(1);
expect(mockLoggerDebug).toBeCalledTimes(1);
// Verify that the returned array is empty.
expect(insuredFiltered.length).toBe(0);
});
});
describe("processInsuredItems", () =>
{
it("should log information about the insurance package", () =>
{
const sessionId = "session-id";
const numberOfItems = 666;
// Spy on the logger.debug method.
const mockLoggerDebug = vi.spyOn(insuranceController.logger, "debug");
vi.spyOn(insuranceController, "countAllInsuranceItems").mockReturnValue(numberOfItems);
vi.spyOn(insuranceController, "findItemsToDelete").mockImplementation(vi.fn());
vi.spyOn(insuranceController, "removeItemsFromInsurance").mockImplementation(vi.fn());
vi.spyOn(insuranceController.itemHelper, "adoptOrphanedItems").mockImplementation(vi.fn());
vi.spyOn(insuranceController, "sendMail").mockImplementation(vi.fn());
vi.spyOn(insuranceController, "removeInsurancePackageFromProfile").mockImplementation(vi.fn());
// Execute the method.
insuranceController.processInsuredItems(insuranceFixture, sessionId);
// Verify that the log was written.
expect(mockLoggerDebug).toBeCalledWith(
`Processing ${insuranceFixture.length} insurance packages, which includes a total of ${numberOfItems} items, in profile ${sessionId}`,
);
});
it("should call processing methods once per insurance package", () =>
{
const sessionId = "session-id";
const packageCount = insuranceFixture.length;
// Spy on the processing methods.
const mockFindItemsToDelete = vi.spyOn(insuranceController, "findItemsToDelete").mockImplementation(
vi.fn(),
);
const mockRemoveItemsFromInsurance = vi.spyOn(insuranceController, "removeItemsFromInsurance")
.mockImplementation(vi.fn());
const mockAdoptOrphanedItems = vi.spyOn(insuranceController.itemHelper, "adoptOrphanedItems")
.mockImplementation(vi.fn());
const mockSendMail = vi.spyOn(insuranceController, "sendMail").mockImplementation(vi.fn());
const mockRemoveInsurancePackageFromProfile = vi.spyOn(
insuranceController,
"removeInsurancePackageFromProfile",
).mockImplementation(vi.fn());
// Execute the method.
insuranceController.processInsuredItems(insuranceFixture, sessionId);
// Verify that the processing methods were called once per insurance package.
expect(mockFindItemsToDelete).toBeCalledTimes(packageCount);
expect(mockRemoveItemsFromInsurance).toBeCalledTimes(packageCount);
expect(mockAdoptOrphanedItems).toBeCalledTimes(packageCount);
expect(mockSendMail).toBeCalledTimes(packageCount);
expect(mockRemoveInsurancePackageFromProfile).toBeCalledTimes(packageCount);
});
});
describe("countAllInsuranceItems", () =>
{
it("should return the total number of items in all insurance packages", () =>
{
const insurance = [{
_id: "1",
upd: 1234567890,
items: [{ _id: "1", parentId: "1", slotId: "1" }, { _id: "2", parentId: "1", slotId: "2" }],
}, {
_id: "2",
upd: 1234567890,
items: [{ _id: "3", parentId: "2", slotId: "1" }, { _id: "4", parentId: "2", slotId: "2" }, {
_id: "5",
parentId: "2",
slotId: "3",
}],
}];
const expectedCount = 5; // 2 items in the first package + 3 items in the second package.
// Execute the method.
const actualCount = insuranceController.countAllInsuranceItems(insurance);
// Verify that the result is correct.
expect(actualCount).toBe(expectedCount);
});
it("should return 0 if there are no insurance packages", () =>
{
const insurance = [];
const expectedCount = 0;
// Execute the method.
const actualCount = insuranceController.countAllInsuranceItems(insurance);
// Verify that the result is correct.
expect(actualCount).toBe(expectedCount);
});
it("should return 0 if there are no items in any of the insurance packages", () =>
{
const insurance = [{ _id: "1", upd: 1234567890, items: [] }, { _id: "2", upd: 1234567890, items: [] }];
const expectedCount = 0;
// Execute the method.
const actualCount = insuranceController.countAllInsuranceItems(insurance);
// Verify that the result is correct.
expect(actualCount).toBe(expectedCount);
});
});
describe("removeInsurancePackageFromProfile", () =>
{
it("should remove the specified insurance package from the profile", () =>
{
const sessionID = "session-id";
const packageToRemove = {
traderId: "54cb50c76803fa8b248b4571",
systemData: { date: "01.11.2023", time: "11:18", location: "factory4_day" },
};
const profile = {
insurance: [{
traderId: "54cb50c76803fa8b248b4571",
systemData: { date: "01.11.2023", time: "11:18", location: "factory4_day" },
}, {
traderId: "54cb57776803fa99248b456e",
systemData: { date: "01.11.2023", time: "10:51", location: "factory4_day" },
}],
};
// Mock the getProfile method to return the above profile.
vi.spyOn(insuranceController.saveServer, "getProfile").mockReturnValue(profile);
// Execute the method.
insuranceController.removeInsurancePackageFromProfile(sessionID, packageToRemove);
// Verify that the specified insurance package was removed.
expect(profile.insurance.length).toBe(1);
expect(profile.insurance).toStrictEqual([{
traderId: "54cb57776803fa99248b456e",
systemData: { date: "01.11.2023", time: "10:51", location: "factory4_day" },
}]);
});
it("should log a message indicating that the package was removed", () =>
{
const sessionID = "session-id";
const packageToRemove = {
traderId: "54cb50c76803fa8b248b4571",
systemData: { date: "01.11.2023", time: "11:18", location: "factory4_day" },
};
const profile = {
insurance: [{
traderId: "54cb50c76803fa8b248b4571",
systemData: { date: "01.11.2023", time: "11:18", location: "factory4_day" },
}],
};
// Mock the getProfile method to return the above profile.
vi.spyOn(insuranceController.saveServer, "getProfile").mockReturnValue(profile);
// Spy on the logger.debug method.
const mockLoggerDebug = vi.spyOn(insuranceController.logger, "debug");
// Execute the method.
insuranceController.removeInsurancePackageFromProfile(sessionID, packageToRemove);
// Verify that the log was written.
expect(mockLoggerDebug).toBeCalledWith(
`Removed processed insurance package. Remaining packages: ${profile.insurance.length}`,
);
});
it("should not remove any packages if the specified package is not found", () =>
{
const sessionID = "session-id";
const packageToRemove = {
traderId: "54cb50c76803fa8b248b4571",
systemData: { date: "01.11.2023", time: "11:25", location: "factory4_day" },
};
const profile = {
insurance: [{
traderId: "54cb50c76803fa8b248b4571",
systemData: { date: "01.11.2023", time: "11:18", location: "factory4_day" },
}, {
traderId: "54cb57776803fa99248b456e",
systemData: { date: "01.11.2023", time: "10:51", location: "factory4_day" },
}],
};
// Mock the getProfile method to return the above profile.
vi.spyOn(insuranceController.saveServer, "getProfile").mockReturnValue(profile);
// Execute the method.
insuranceController.removeInsurancePackageFromProfile(sessionID, packageToRemove);
// Verify that the specified insurance package was removed.
expect(profile.insurance.length).toBe(2);
});
});
describe("findItemsToDelete", () =>
{
it("should handle an empty insurance package", () =>
{
const insurancePackage = insuranceFixture[0];
insurancePackage.items = [];
// Execute the method.
const result = insuranceController.findItemsToDelete(
insuranceController.hashUtil.generate(),
insurancePackage,
);
// Verify that the result is correct.
expect(result.size).toBe(0);
});
it("should handle regular items", () =>
{
// Remove attachment items from the fixture.
insuranceFixture = new ProfileInsuranceFactory().adjustPackageDates().removeAttachmentItems().get();
const insured = insuranceFixture[0];
const numberOfItems = insured.items.length;
// Mock helper methods.
const mockPopulateParentAttachmentsMap = vi.spyOn(insuranceController, "populateParentAttachmentsMap");
const mockIsAttachmentAttached = vi.spyOn(insuranceController.itemHelper, "isAttachmentAttached");
const mockProcessAttachments = vi.spyOn(insuranceController, "processAttachments").mockImplementation(
vi.fn(),
);
// Add all items to the toDelete set. Not realistic, but it's fine for this test.
const mockProcessRegularItems = vi.fn((insured, toDelete) =>
{
for (const item of insured.items)
{
toDelete.add(item._id);
}
});
vi.spyOn(insuranceController, "processRegularItems").mockImplementation(mockProcessRegularItems);
// Execute the method.
const result = insuranceController.findItemsToDelete(insuranceController.hashUtil.generate(), insured);
// Verify that the correct methods were called.
expect(mockPopulateParentAttachmentsMap).toHaveBeenCalledTimes(1);
expect(mockIsAttachmentAttached).toHaveBeenCalled();
expect(mockProcessRegularItems).toHaveBeenCalledTimes(1);
expect(mockProcessAttachments).not.toHaveBeenCalled();
// Verify that the result is correct.
expect(result.size).toBe(numberOfItems);
expect(result).toEqual(new Set(insured.items.map((item) => item._id)));
});
it("should ignore orphaned attachments", () =>
{
// Remove regular items from the fixture, creating orphaned attachments.
insuranceFixture = new ProfileInsuranceFactory().adjustPackageDates().removeRegularItems().get();
const insured = insuranceFixture[0];
// Mock helper methods.
const mockProcessRegularItems = vi.spyOn(insuranceController, "processRegularItems");
const mockProcessAttachments = vi.spyOn(insuranceController, "processAttachments");
// Since no parent attachments exist, the map should be empty.
const mockPopulateParentAttachmentsMap = vi.fn(() =>
{
return new Map<string, Item[]>();
});
vi.spyOn(insuranceController, "populateParentAttachmentsMap").mockImplementation(
mockPopulateParentAttachmentsMap,
);
// Execute the method.
const result = insuranceController.findItemsToDelete(insuranceController.hashUtil.generate(), insured);
// Verify that the correct methods were called.
expect(mockPopulateParentAttachmentsMap).toHaveBeenCalled();
expect(mockProcessRegularItems).not.toHaveBeenCalled();
expect(mockProcessAttachments).not.toHaveBeenCalled();
// Verify that the result is correct.
expect(result.size).toBe(0);
expect(result).toEqual(new Set());
});
it("should handle a mix of regular items and attachments", () =>
{
const insured = insuranceFixture[0];
const numberOfItems = insured.items.length;
// Mock helper methods.
const mockPopulateParentAttachmentsMap = vi.spyOn(insuranceController, "populateParentAttachmentsMap");
// Add all items to the toDelete set. Not realistic, but it's fine for this test.
const mockProcessRegularItems = vi.fn((insured, toDelete) =>
{
for (const item of insured.items)
{
toDelete.add(item._id);
}
});
vi.spyOn(insuranceController, "processRegularItems").mockImplementation(mockProcessRegularItems);
const mockProcessAttachments = vi.fn((parentAttachmentsMap, itemsMap, traderId, toDelete) =>
{
for (const item of insured.items)
{
toDelete.add(item._id);
}
});
vi.spyOn(insuranceController, "processAttachments").mockImplementation(mockProcessAttachments);
// Execute the method.
const result = insuranceController.findItemsToDelete(insuranceController.hashUtil.generate(), insured);
// Verify that the correct methods were called.
expect(mockPopulateParentAttachmentsMap).toHaveBeenCalled();
expect(mockProcessRegularItems).toHaveBeenCalled();
expect(mockProcessAttachments).toHaveBeenCalled();
// Verify that the result is correct.
expect(result.size).toBe(numberOfItems);
expect(result).toEqual(new Set(insured.items.map((item) => item._id)));
});
it("should return an empty set if no items are to be deleted", () =>
{
const insured = insuranceFixture[0];
// Mock helper methods.
const mockPopulateParentAttachmentsMap = vi.spyOn(insuranceController, "populateParentAttachmentsMap");
// Don't add any items to the toDelete set.
const mockProcessRegularItems = vi.spyOn(insuranceController, "processRegularItems").mockImplementation(
vi.fn(),
);
const mockProcessAttachments = vi.spyOn(insuranceController, "processAttachments").mockImplementation(
vi.fn(),
);
// Execute the method.
const result = insuranceController.findItemsToDelete(insuranceController.hashUtil.generate(), insured);
// Verify that the correct methods were called.
expect(mockPopulateParentAttachmentsMap).toHaveBeenCalled();
expect(mockProcessRegularItems).toHaveBeenCalled();
expect(mockProcessAttachments).toHaveBeenCalled();
// Verify that the result is correct.
expect(result.size).toBe(0);
expect(result).toEqual(new Set());
});
it("should log the number of items to be deleted", () =>
{
const insured = insuranceFixture[0];
const numberOfItems = insured.items.length;
// Mock helper methods.
const mockLoggerDebug = vi.spyOn(insuranceController.logger, "debug");
// Add all items to the toDelete set. Not realistic, but it's fine for this test.
const mockProcessRegularItems = vi.fn((insured, toDelete) =>
{
for (const item of insured.items)
{
toDelete.add(item._id);
}
});
vi.spyOn(insuranceController, "processRegularItems").mockImplementation(mockProcessRegularItems);
const mockProcessAttachments = vi.fn((parentAttachmentsMap, itemsMap, traderId, toDelete) =>
{
for (const item of insured.items)
{
toDelete.add(item._id);
}
});
vi.spyOn(insuranceController, "processAttachments").mockImplementation(mockProcessAttachments);
// Execute the method.
const result = insuranceController.findItemsToDelete(insuranceController.hashUtil.generate(), insured);
// Verify that the result is the correct size, and the size is logged.
expect(result.size).toBe(numberOfItems);
expect(mockLoggerDebug).toBeCalledWith(`Marked ${numberOfItems} items for deletion from insurance.`);
});
});
describe("populateParentAttachmentsMap", () =>
{
it("should correctly map gun to all of its attachments", () =>
{
const insured = insuranceFixture[0];
// Generate the items map.
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
// Execute the method.
const result = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
// Verify that the map is populated correctly.
expect(result.size).toBe(9); // There are 9 base-level items in this insurance package.
const gun = result.get("35111c9b72a87b6b7d95ad35");
expect(gun.length).toBe(31); // This gun has 31 attachments.
// The attachments should be mapped to the gun properly...
const validAttachmentTemplates = [
"7c42d3dce0ddbc4806bce48b",
"10b97872c5f4e0e1949a0369",
"a6cd9986dde4cabddcd2dce2",
"b65635b515712f990fdcc201",
"0e11045873efe3625695c1ae",
"94c4161abe8bf654fb986063",
"9b284ccfd0d535acec1ff58b",
"d730caa83a11fd01250a7261",
"24291c7bcf91e362adb6d68b",
"0d98fd0769cce8e473bbe540",
"11b174510f039e8217fbd202",
"c435230e530574b1d7c32300",
"15666fe6fd2d95206612e418",
"a54de8b9014eee71fdf1d01d",
"c34555bc95a9a7a23150a36f",
"91cae4ae30d1366b87158238",
"48f23df4509164cf397b9ab5",
"a55f05f689978ac65c7da654",
"8ae4ea81a2d6074162d87a9c",
"312cc0f6687963305457235e",
"e1e5aaf474b7282a52ac9a14",
"bb9a34648e08f005db5d7484",
"dd9ac99d3ea4c9656221bcc9",
"b22748de8da5f3c1362dd8e0",
"e3cc1be8954c4889f94b435a",
"e73f05be5a306168e847da82",
"847cf35ec92d8af8e4814ea8",
"bb4b7a4475fea0f0135305f6",
"d0ac8e688a0bb17668589909",
"5dbcf8cbbb3f8ef669836320",
"f996645c809968f8033593a6",
];
for (const value of validAttachmentTemplates)
{
// Verify that each template is present in the array of attachments.
expect(gun.some((item) => item._id === value)).toBe(true);
}
});
it("should correctly map helmet to all of its attachments", () =>
{
const insured = insuranceFixture[0];
// Generate the items map.
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
// Execute the method.
const result = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
// Verify that the map is populated correctly.
expect(result.size).toBe(9); // There are 9 base-level items in this insurance package.
const helmet = result.get("b2405216e5730f3511884a10");
expect(helmet.length).toBe(4); // This helmet has 2 valid attachments.
// The attachments should be mapped to the helmet properly...
const validAttachmentTemplates = [
"7a0675280dbbad69ce592d74",
"c0c182942f54d3c183f0e179",
"f7066fdfeefb29eca1d2dbeb",
"ee0ec86e9608abe773175e3a",
];
for (const value of validAttachmentTemplates)
{
// Verify that each template is present in the array of attachments.
expect(helmet.some((item) => item._id === value)).toBe(true);
}
});
it("should correctly map gun to all of its attachments when gun is within a container", () =>
{
const insured = insuranceFixture[0];
// Generate the items map.
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
// Execute the method.
const result = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
// Verify that the map is populated correctly.
expect(result.size).toBe(9); // There are 9 base-level items in this insurance package.
const gun = result.get("26598f88d49198c4a0a9391c");
expect(insured.items.find((item) => item._id === "26598f88d49198c4a0a9391c").slotId).toBe("main");
expect(gun.length).toBe(3);
});
it("should not map items that do not have a main-parent", () =>
{
// Remove regular items from the fixture.
insuranceFixture = new ProfileInsuranceFactory().adjustPackageDates().removeRegularItems().get();
const insured = insuranceFixture[0];
// Generate the items map.
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
// Suppress warnings.
const mockLoggerWarning = vi.spyOn(insuranceController.logger, "warning").mockImplementation(vi.fn());
// Execute the method.
const result = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
// Verify that the map is populated correctly.
expect(result.size).toBe(0);
});
it("should log a warning when an item does not have a main-parent", () =>
{
// Remove regular items from the fixture.
insuranceFixture = new ProfileInsuranceFactory().adjustPackageDates().removeRegularItems().get();
const insured = insuranceFixture[0];
// Generate the items map.
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
// Suppress warnings.
const mockLoggerWarning = vi.spyOn(insuranceController.logger, "warning").mockImplementation(vi.fn());
// Execute the method.
insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
// Verify that the warning was logged.
expect(mockLoggerWarning).toHaveBeenCalled();
});
});
describe("removeNonModdableAttachments", () =>
{
it("should return a Map where each parent item ID is mapped to only moddable attachments", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
// Execute the method.
const result = insuranceController.removeNonModdableAttachments(parentAttachmentsMap, itemsMap);
// Verify that the map is populated correctly.
for (const [parentId, attachments] of result)
{
for (const attachment of attachments)
{
// Verify that each attachment is moddable.
const attachmentParentItem = itemsMap.get(parentId);
expect(insuranceController.itemHelper.isRaidModdable(attachment, attachmentParentItem)).toBe(true);
}
}
});
it("should remove parents that do not have any moddable attachments", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
// Mock isRaidModdable to return false for all attachments.
vi.spyOn(insuranceController.itemHelper, "isRaidModdable").mockReturnValue(false);
// Execute the method.
const result = insuranceController.removeNonModdableAttachments(parentAttachmentsMap, itemsMap);
// Verify that the map is now empty.
expect(result.size).toBe(0);
});
});
describe("processRegularItems", () =>
{
it("should process regular items and their non-attachment children", () =>
{
// Remove attachment items from the fixture.
insuranceFixture = new ProfileInsuranceFactory().adjustPackageDates().removeAttachmentItems().get();
const insured = insuranceFixture[0];
const numberOfItems = insured.items.length;
const toDelete = new Set<string>();
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
// Mock helper methods.
const mockIsAttachmentAttached = vi.spyOn(insuranceController.itemHelper, "isAttachmentAttached");
const mockFindAndReturnChildrenAsItems = vi.spyOn(
insuranceController.itemHelper,
"findAndReturnChildrenAsItems",
);
// Mock rollForDelete to return true for all items. Not realistic, but it's fine for this test.
const mockRollForDelete = vi.spyOn(insuranceController, "rollForDelete").mockReturnValue(true);
// Execute the method.
insuranceController.processRegularItems(insured, toDelete, parentAttachmentsMap);
// Verify that the correct methods were called.
expect(mockIsAttachmentAttached).toHaveBeenCalledTimes(numberOfItems);
expect(mockFindAndReturnChildrenAsItems).not.toHaveBeenCalled();
expect(mockRollForDelete).toHaveBeenCalledTimes(numberOfItems);
// Verify that all items were added to the toDelete set.
expect(toDelete).toEqual(new Set(insured.items.map((item) => item._id)));
});
it("should not roll attached attachments", () =>
{
const insured = insuranceFixture[0];
const toDelete = new Set<string>();
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
// Mock isAttachmentAttached to return true for all items.
vi.spyOn(insuranceController.itemHelper, "isAttachmentAttached").mockReturnValue(true);
// Mock rollForDelete to return true for all items.
const mockRollForDelete = vi.spyOn(insuranceController, "rollForDelete").mockReturnValue(true);
// Execute the method.
insuranceController.processRegularItems(insured, toDelete, parentAttachmentsMap);
// Verify that a roll was not made for any items.
expect(mockRollForDelete).not.toHaveBeenCalled();
// Verify that no items were added to the toDelete set.
expect(toDelete).toEqual(new Set());
});
it("should mark attachments for deletion when parent is marked for deletion", () =>
{
const insured = insuranceFixture[0];
const toDelete = new Set<string>();
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
// Mock rollForDelete to return true for all base-parent items.
const mockRollForDelete = vi.fn((traderId, insuredItem) =>
{
return !insuranceController.itemHelper.isAttachmentAttached(insuredItem);
});
vi.spyOn(insuranceController, "rollForDelete").mockImplementation(mockRollForDelete);
// Execute the method.
insuranceController.processRegularItems(insured, toDelete, parentAttachmentsMap);
// Verify that all items were added to the toDelete set.
expect(toDelete).toEqual(new Set(insured.items.map((item) => item._id)));
});
});
describe("processAttachments", () =>
{
it("should iterate over each parent item", () =>
{
const insured = insuranceFixture[0];
const toDelete = new Set<string>();
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
// Mock helper methods.
const mockProcessAttachmentByParent = vi.spyOn(insuranceController, "processAttachmentByParent");
// Execute the method.
insuranceController.processAttachments(parentAttachmentsMap, itemsMap, insured.traderId, toDelete);
// Verify
expect(mockProcessAttachmentByParent).toHaveBeenCalledTimes(parentAttachmentsMap.size);
});
it("should log the name of each parent item", () =>
{
const insured = insuranceFixture[0];
const toDelete = new Set<string>();
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
// Mock helper methods.
const mockLoggerDebug = vi.spyOn(insuranceController.logger, "debug").mockImplementation(vi.fn());
// Mock processAttachmentByParent to prevent it from being called.
vi.spyOn(insuranceController, "processAttachmentByParent").mockImplementation(vi.fn());
// Execute the method.
insuranceController.processAttachments(parentAttachmentsMap, itemsMap, insured.traderId, toDelete);
// Verify that the name of each parent item is logged.
for (const [parentId] of parentAttachmentsMap)
{
const parentItem = itemsMap.get(parentId);
if (parentItem)
{
const expectedMessage = `Processing attachments of parent "${
insuranceController.itemHelper.getItemName(parentItem._tpl)
}":`;
expect(mockLoggerDebug).toHaveBeenCalledWith(expectedMessage);
}
}
});
});
describe("processAttachmentByParent", () =>
{
it("should handle sorting, rolling, and deleting attachments by calling helper methods", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
const attachments = parentAttachmentsMap.entries().next().value;
const toDelete = new Set<string>();
// Mock helper methods.
const mockSortAttachmentsByPrice = vi.spyOn(insuranceController, "sortAttachmentsByPrice");
const mockCountSuccessfulRolls = vi.spyOn(insuranceController, "countSuccessfulRolls").mockReturnValue(4);
const mockAttachmentDeletionByValue = vi.spyOn(insuranceController, "attachmentDeletionByValue");
// Execute the method.
insuranceController.processAttachmentByParent(attachments, insured.traderId, toDelete);
// Verify that helper methods are called.
expect(mockSortAttachmentsByPrice).toHaveBeenCalledWith(attachments);
expect(mockCountSuccessfulRolls).toHaveBeenCalled();
expect(mockAttachmentDeletionByValue).toHaveBeenCalled();
});
it("should log attachment details and number of attachments to be deleted", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
const attachments = parentAttachmentsMap.entries().next().value;
const toDelete = new Set<string>();
const successfulRolls = 4;
// Mock helper methods.
const mockLogAttachmentsDetails = vi.spyOn(insuranceController, "logAttachmentsDetails");
vi.spyOn(insuranceController, "countSuccessfulRolls").mockReturnValue(successfulRolls);
const mockLoggerDebug = vi.spyOn(insuranceController.logger, "debug").mockImplementation(vi.fn());
// Execute the method.
insuranceController.processAttachmentByParent(attachments, insured.traderId, toDelete);
// Verify that the logs were called/written.
expect(mockLogAttachmentsDetails).toBeCalled();
expect(mockLoggerDebug).toHaveBeenCalledWith(`Number of attachments to be deleted: ${successfulRolls}`);
});
});
describe("sortAttachmentsByPrice", () =>
{
it("should sort the attachments array by dynamicPrice in descending order", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
const attachments = parentAttachmentsMap.entries().next().value;
const attachmentCount = attachments.length;
// Execute the method.
const sortedAttachments = insuranceController.sortAttachmentsByPrice(attachments);
// Verify the length of the sorted attachments array is unchanged
expect(sortedAttachments.length).toBe(attachmentCount);
// Verify that the attachments are sorted by dynamicPrice in descending order
for (let i = 1; i < sortedAttachments.length; i++)
{
expect(sortedAttachments[i - 1].dynamicPrice).toBeGreaterThanOrEqual(sortedAttachments[i].dynamicPrice);
}
});
it("should place attachments with null dynamicPrice at the bottom of the sorted list", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
const attachments = parentAttachmentsMap.entries().next().value;
// Set the dynamicPrice of the first attachment to null.
vi.spyOn(insuranceController.ragfairPriceService, "getDynamicItemPrice").mockReturnValue(666).mockReturnValueOnce(null);
// Execute the method.
const sortedAttachments = insuranceController.sortAttachmentsByPrice(attachments);
// Verify that the attachments with null dynamicPrice are at the bottom of the list
const nullPriceAttachments = sortedAttachments.slice(-1);
for (const attachment of nullPriceAttachments)
{
expect(attachment.dynamicPrice).toBeNull();
}
// Verify that the rest of the attachments are sorted by dynamicPrice in descending order
for (let i = 1; i < sortedAttachments.length - 2; i++)
{
expect(sortedAttachments[i - 1].dynamicPrice).toBeGreaterThanOrEqual(sortedAttachments[i].dynamicPrice);
}
});
});
describe("logAttachmentsDetails", () =>
{
it("should log details for each attachment", () =>
{
const attachments = [{ _id: "item1", name: "Item 1", dynamicPrice: 100 }, {
_id: "item2",
name: "Item 2",
dynamicPrice: 200,
}];
// Mock the logger.debug function.
const loggerDebugSpy = vi.spyOn(insuranceController.logger, "debug");
// Execute the method.
insuranceController.logAttachmentsDetails(attachments);
// Verify that logger.debug was called correctly.
expect(loggerDebugSpy).toHaveBeenCalledTimes(2);
expect(loggerDebugSpy).toHaveBeenNthCalledWith(1, "Attachment 1: \"Item 1\" - Price: 100");
expect(loggerDebugSpy).toHaveBeenNthCalledWith(2, "Attachment 2: \"Item 2\" - Price: 200");
});
it("should not log anything when there are no attachments", () =>
{
const attachments = [];
// Mock the logger.debug function.
const loggerDebugSpy = vi.spyOn(insuranceController.logger, "debug");
// Execute the method.
insuranceController.logAttachmentsDetails(attachments);
// Verify that logger.debug was called correctly.
expect(loggerDebugSpy).not.toHaveBeenCalled();
});
});
describe("countSuccessfulRolls", () =>
{
it("should count the number of successful rolls made in the rollForDelete method", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
const attachments = parentAttachmentsMap.values().next().value;
// Mock rollForDelete to return true for the first two attachments.
const mockRollForDelete = vi.spyOn(insuranceController, "rollForDelete").mockReturnValue(false)
.mockReturnValueOnce(true).mockReturnValueOnce(true);
// Execute the method.
const result = insuranceController.countSuccessfulRolls(attachments, insured.traderId);
// Verify that two successful rolls were counted.
expect(mockRollForDelete).toHaveBeenCalledTimes(attachments.length);
expect(result).toBe(2);
});
it("should return zero if no successful rolls were made in the rollForDelete method", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
const attachments = parentAttachmentsMap.values().next().value;
// Mock rollForDelete to return false.
const mockRollForDelete = vi.spyOn(insuranceController, "rollForDelete").mockReturnValue(false);
// Execute the method.
const result = insuranceController.countSuccessfulRolls(attachments, insured.traderId);
// Verify that zero successful rolls were counted.
expect(mockRollForDelete).toHaveBeenCalledTimes(attachments.length);
expect(result).toBe(0);
});
it("should return zero if there are no attachments", () =>
{
const insured = insuranceFixture[0];
const attachments = [];
// Spy on rollForDelete to ensure it is not called.
const mockRollForDelete = vi.spyOn(insuranceController, "rollForDelete");
// Execute the method.
const result = insuranceController.countSuccessfulRolls(attachments, insured.traderId);
// Verify that zero successful rolls were returned.
expect(mockRollForDelete).not.toHaveBeenCalled();
expect(result).toBe(0);
});
});
describe("attachmentDeletionByValue", () =>
{
it("should add the correct number of attachments to the toDelete set", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
const attachments = parentAttachmentsMap.values().next().value;
const successfulRolls = 2;
const toDelete = new Set<string>();
// Execute the method.
insuranceController.attachmentDeletionByValue(attachments, successfulRolls, toDelete);
// Should add the first two valuable attachments to the toDelete set.
expect(toDelete.size).toEqual(successfulRolls);
});
it("should not add any attachments to toDelete if successfulRolls is zero", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
const attachments = parentAttachmentsMap.values().next().value;
const successfulRolls = 0;
const toDelete = new Set<string>();
// Execute the method.
insuranceController.attachmentDeletionByValue(attachments, successfulRolls, toDelete);
// Should be empty.
expect(toDelete.size).toEqual(successfulRolls);
});
it("should add all attachments to toDelete if successfulRolls is greater than the number of attachments", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.itemHelper.generateItemsMap(insured.items);
const parentAttachmentsMap = insuranceController.populateParentAttachmentsMap(
insuranceController.hashUtil.generate(),
insured,
itemsMap,
);
const attachments = parentAttachmentsMap.values().next().value;
const successfulRolls = 999;
const toDelete = new Set<string>();
// Execute the method.
insuranceController.attachmentDeletionByValue(attachments, successfulRolls, toDelete);
// Should be empty.
expect(toDelete.size).toBeLessThan(successfulRolls);
expect(toDelete.size).toEqual(attachments.length);
});
});
describe("removeItemsFromInsurance", () =>
{
it("should remove items from insurance based on the toDelete set", () =>
{
const insured = insuranceFixture[0];
const toDelete = new Set<string>();
const numberToDelete = 2;
for (let i = 0; i < numberToDelete; i++)
{
toDelete.add(insured.items[i]._id);
}
// Execute the method.
insuranceController.removeItemsFromInsurance(insured, toDelete);
// Ensure that the items in the toDelete set are not present in the insured items array.
for (const toDeleteId of toDelete)
{
expect(insured.items.some((item) => item._id === toDeleteId)).toBe(false);
}
});
it("should not remove any items if toDelete set is empty", () =>
{
const insured = insuranceFixture[0];
const originalCount = insured.items.length;
const toDelete = new Set<string>();
// Execute the method.
insuranceController.removeItemsFromInsurance(insured, toDelete);
// Ensure that no items were removed.
expect(insured.items.length).toBe(originalCount);
});
it("should leave the insurance items empty if all are to be deleted", () =>
{
const insured = insuranceFixture[0];
const originalCount = insured.items.length;
const toDelete = new Set<string>();
for (const item of insured.items)
{
toDelete.add(item._id);
}
// All of the items should be added to the toDelete set.
expect(originalCount).toBe(toDelete.size);
// Execute the method.
insuranceController.removeItemsFromInsurance(insured, toDelete);
// Ensure that all items were removed.
expect(insured.items.length).toBe(0);
});
});
describe("sendMail", () =>
{
it("should send insurance failed message when no items are present", () =>
{
const insurance = insuranceFixture[0];
insurance.items = []; // Empty the items array
const sessionID = "session-id";
const insuranceFailedTpl = "failed-message-template";
// Mock the randomUtil to return a static failed template string.
const mockGetArrayValue = vi.spyOn(insuranceController.randomUtil, "getArrayValue").mockReturnValue(
insuranceFailedTpl,
);
// Don't actually send the message.
const sendMessageSpy = vi.spyOn(insuranceController.mailSendService, "sendLocalisedNpcMessageToPlayer")
.mockImplementation(vi.fn());
// Execute the method.
insuranceController.sendMail(sessionID, insurance);
// Verify that the randomUtil.getArrayValue method was called.
expect(mockGetArrayValue).toBeCalled();
// Verify that the insurance failed message was sent.
expect(sendMessageSpy).toHaveBeenCalledWith(
sessionID,
insuranceController.traderHelper.getTraderById(insurance.traderId),
MessageType.INSURANCE_RETURN,
insuranceFailedTpl,
insurance.items,
insurance.maxStorageTime,
insurance.systemData,
);
});
it("should not send insurance failed message when items are present", () =>
{
const insurance = insuranceFixture[0];
const sessionID = "session-id";
const insuranceFailedTpl = "failed-message-template";
// Mock the randomUtil to return a static failed template string.
const mockGetArrayValue = vi.spyOn(insuranceController.randomUtil, "getArrayValue").mockReturnValue(
insuranceFailedTpl,
);
// Don't actually send the message.
const sendMessageSpy = vi.spyOn(insuranceController.mailSendService, "sendLocalisedNpcMessageToPlayer")
.mockImplementation(vi.fn());
// Execute the method.
insuranceController.sendMail(sessionID, insurance);
// Verify that the randomUtil.getArrayValue method was not called.
expect(mockGetArrayValue).not.toBeCalled();
// Verify that the insurance failed message was not sent.
expect(sendMessageSpy).toHaveBeenCalledWith(
sessionID,
insuranceController.traderHelper.getTraderById(insurance.traderId),
MessageType.INSURANCE_RETURN,
insurance.messageTemplateId,
insurance.items,
insurance.maxStorageTime,
insurance.systemData,
);
});
});
describe("rollForDelete", () =>
{
it("should return true when random roll is equal to trader return chance", () =>
{
vi.spyOn(insuranceController.randomUtil, "getInt").mockReturnValue(8500); // Our "random" roll.
const traderId = "54cb57776803fa99248b456e"; // Therapist (85% return chance)
insuranceController.insuranceConfig = {
returnChancePercent: {
[traderId]: 85, // Force 85% return chance
},
};
// Execute the method.
const result = insuranceController.rollForDelete(traderId);
// Verify that the result is true.
expect(result).toBe(true);
});
it("should return true when random roll is greater than trader return chance", () =>
{
vi.spyOn(insuranceController.randomUtil, "getInt").mockReturnValue(8501); // Our "random" roll.
const traderId = "54cb57776803fa99248b456e"; // Therapist (85% return chance)
insuranceController.insuranceConfig = {
returnChancePercent: {
[traderId]: 85, // Force 85% return chance
},
};
// Execute the method.
const result = insuranceController.rollForDelete(traderId);
// Verify that the result is true.
expect(result).toBe(true);
});
it("should return false when random roll is less than trader return chance", () =>
{
vi.spyOn(insuranceController.randomUtil, "getInt").mockReturnValue(8499); // Our "random" roll.
const traderId = "54cb57776803fa99248b456e"; // Therapist (85% return chance)
insuranceController.insuranceConfig = {
returnChancePercent: {
[traderId]: 85, // Force 85% return chance
},
};
// Execute the method.
const result = insuranceController.rollForDelete(traderId);
// Verify that the result is false.
expect(result).toBe(false);
});
it("should log error if trader can not be found", () =>
{
const traderId = "invalid-trader-id";
const loggerErrorSpy = vi.spyOn(insuranceController.logger, "error");
// Execute the method.
insuranceController.rollForDelete(traderId);
// Verify that the logger.error method was called.
expect(loggerErrorSpy).toHaveBeenCalled();
});
it("should return null if trader can not be found", () =>
{
const traderId = "invalid-trader-id";
// Execute the method.
const result = insuranceController.rollForDelete(traderId);
// Verify that the result is null.
expect(result).toBe(null);
});
});
describe("insure", () =>
{
let pmcData: any;
let body: any;
let sessionId: string;
let insuranceController: any;
let mockGetPremium: any;
let mockPayMoney: any;
let mockGetOutput: any;
beforeEach(() =>
{
insuranceController = container.resolve<InsuranceController>("InsuranceController");
// Setup shared test data.
pmcData = {
Inventory: { items: [{ _id: "item1", otherProps: "value1" }, { _id: "item2", otherProps: "value2" }] },
InsuredItems: [],
};
body = { items: ["item1", "item2"], tid: "someTraderId" };
sessionId = "session-id";
// Setup shared mocks.
mockGetPremium = vi.spyOn(insuranceController.insuranceService, "getPremium").mockReturnValue(100);
mockPayMoney = vi.spyOn(insuranceController.paymentService, "payMoney").mockReturnValue({
warnings: [],
otherProperty: "property-value",
});
mockGetOutput = vi.spyOn(insuranceController.eventOutputHolder, "getOutput").mockReturnValue({
warnings: [],
otherProperty: "property-value",
});
});
it("should create a hash of inventory items by ID", () =>
{
// Execute the method.
insuranceController.insure(pmcData, body, sessionId);
// Since the inventoryItemsHash is internal to the function, we cannot check it directly. However, we can
// infer its correctness by ensuring the payMoney function is called with the right "scheme_items" property.
expect(mockPayMoney).toHaveBeenCalledWith(
pmcData,
{
scheme_items: [{ id: "5449016a4bdc2d6f028b456f", count: 100 }, {
id: "5449016a4bdc2d6f028b456f",
count: 100,
}],
tid: "someTraderId",
Action: "SptInsure",
type: "",
item_id: "",
count: 0,
scheme_id: 0,
},
sessionId,
{ warnings: [], otherProperty: "property-value" },
);
});
it("should calculate the insurance premium for each item to insure", () =>
{
// Execute the method.
insuranceController.insure(pmcData, body, sessionId);
// Verify that getPremium is called with each item from the pmcData.Inventory.items array.
for (const item of pmcData.Inventory.items)
{
expect(mockGetPremium).toHaveBeenCalledWith(pmcData, item, body.tid);
}
// Verify that getPremium was called the correct number of times.
expect(mockGetPremium).toHaveBeenCalledTimes(body.items.length);
});
it("should call the payment service with the correct parameters", () =>
{
// Execute the method.
insuranceController.insure(pmcData, body, sessionId);
// Define the expected payment options structure based on the setup data.
const expectedPaymentOptions = {
scheme_items: [{ id: "5449016a4bdc2d6f028b456f", count: 100 }, {
id: "5449016a4bdc2d6f028b456f",
count: 100,
}],
tid: body.tid,
Action: "SptInsure",
type: "",
item_id: "",
count: 0,
scheme_id: 0,
};
// Verify that the paymentService's payMoney method was called once with the expected parameters.
expect(mockPayMoney).toHaveBeenCalledWith(pmcData, expectedPaymentOptions, sessionId, expect.any(Object));
// Verify that the output passed to payMoney is the one obtained from getOutput.
expect(mockPayMoney).toHaveBeenCalledWith(
pmcData,
expectedPaymentOptions,
sessionId,
mockGetOutput.mock.results[0].value,
);
});
it("should add items to InsuredItems after successful payment", () =>
{
// Execute the method.
insuranceController.insure(pmcData, body, sessionId);
// Verify that the InsuredItems array has been populated with the correct items.
const insuredItemIds = pmcData.InsuredItems.map((insuredItem) => insuredItem.itemId);
expect(insuredItemIds).toContain("item1");
expect(insuredItemIds).toContain("item2");
// Verify that the number of InsuredItems matches the number of items intended to be insured.
expect(pmcData.InsuredItems.length).toBe(body.items.length);
});
it("should update output with warnings if payment fails", () =>
{
// Override the payMoney mock to simulate a payment failure with a warning.
const expectedPayMoneyReturn = {
warnings: [{ index: 0, errmsg: "You broke.", code: 500 }],
otherProperty: "property-value",
};
mockPayMoney.mockImplementation((pmcData, request, sessionID, output) =>
{
output.warnings = expectedPayMoneyReturn.warnings;
});
// Execute the method.
const response = insuranceController.insure(pmcData, body, sessionId);
// Verify that the response contains the warnings from the payment failure
expect(response.warnings).toStrictEqual(expectedPayMoneyReturn.warnings);
// Verify that other properties from the response are still present.
expect(response).toHaveProperty("otherProperty", "property-value");
});
it("should not add items to InsuredItems if payment fails", () =>
{
// Override the payMoney mock to simulate a payment failure with a warning.
const expectedPayMoneyReturn = {
warnings: [{ index: 0, errmsg: "You broke.", code: 500 }],
otherProperty: "property-value",
};
mockPayMoney.mockImplementation((pmcData, request, sessionID, output) =>
{
output.warnings = expectedPayMoneyReturn.warnings;
});
// Execute the method.
insuranceController.insure(pmcData, body, sessionId);
// Verify that the InsuredItems array has not been populated.
expect(pmcData.InsuredItems).toHaveLength(0);
});
});
describe("cost", () =>
{
let sessionId: string;
beforeEach(() =>
{
insuranceController = container.resolve<InsuranceController>("InsuranceController");
sessionId = "session-id";
vi.spyOn(insuranceController.profileHelper, "getPmcProfile").mockReturnValue({
Inventory: {
items: [{ _id: "itemId1", _tpl: "itemTpl1", otherProperty: "property-value1" }, {
_id: "itemId2",
_tpl: "itemTpl2",
otherProperty: "property-value2",
}, { _id: "itemId3", _tpl: "itemTpl3", otherProperty: "property-value3" }],
},
});
});
it("should return an empty object if no traders and items are specified", () =>
{
const request = { traders: [], items: [] };
const expected = {};
const result = insuranceController.cost(request, sessionId);
expect(result).toEqual(expected);
});
it("should return an empty object if no items are specified", () =>
{
const request = { traders: ["prapor"], items: [] };
const expected = { prapor: {} };
const result = insuranceController.cost(request, sessionId);
expect(result).toEqual(expected);
});
it("should return an empty object if no trader is specified but items are", () =>
{
const request = { traders: [], items: ["itemId1", "itemId2"] };
const expected = {};
const result = insuranceController.cost(request, sessionId);
expect(result).toEqual(expected);
});
it("should return the expected cost for each item and trader", () =>
{
const request = { traders: ["prapor", "therapist"], items: ["itemId1", "itemId2", "itemId3"] };
const expected = {
prapor: { itemTpl1: 100, itemTpl2: 200, itemTpl3: 300 },
therapist: { itemTpl1: 150, itemTpl2: 250, itemTpl3: 350 },
};
// Mock the InsuranceService.getPremium method to return the expected values.
vi.spyOn(insuranceController.insuranceService, "getPremium").mockReturnValueOnce(100).mockReturnValueOnce(
200,
).mockReturnValueOnce(300).mockReturnValueOnce(150).mockReturnValueOnce(250).mockReturnValueOnce(350);
const result = insuranceController.cost(request, sessionId);
expect(result).toEqual(expected);
});
it("should skip items that are not in the player's inventory", () =>
{
const request = {
traders: ["prapor"],
items: [
"itemId1",
"itemId2",
"itemId4", // Doesn't exist in the player's inventory.
],
};
const expected = { prapor: { itemTpl1: 100, itemTpl2: 200 } };
// Mock the InsuranceService.getPremium method to return the expected values.
vi.spyOn(insuranceController.insuranceService, "getPremium").mockReturnValueOnce(100).mockReturnValueOnce(
200,
);
const result = insuranceController.cost(request, sessionId);
expect(result).toEqual(expected);
});
});
});