Server/project/tests/controllers/InsuranceController.test.ts
Refringe 047884e9c7
Additional InsuranceController Tests
- Adds tests for the remaining methods in the `InsuranceController`.
- Adds a `countAllInsuranceItems()` method to the `InsuranceController`.
- Adds the Vitest UI and coverage packages.
- Updates the `profileInsuranceFactory` to use second-epoch dates instead of millisecond-epoch dates.
- Updates the `InsuranceController.fetchHideoutItemParent()` method to log a warning when an item with a slotId of 'hideout' cannot be found.

TODO:
- The Vitest coverage options are not working.
2023-11-03 17:39:33 -04:00

1669 lines
70 KiB
TypeScript

/* eslint-disable @typescript-eslint/naming-convention */
import "reflect-metadata";
import { container } from "tsyringe";
import { vi, afterEach, describe, expect, it, beforeEach } from "vitest";
import { InsuranceController } from "@spt-aki/controllers/InsuranceController";
import { ProfileInsuranceFactory } from "@tests/__factories__/ProfileInsurance.factory";
import { MessageType } from "@spt-aki/models/enums/MessageType";
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 { ItemHelper } from "@spt-aki/helpers/ItemHelper";
describe("InsuranceController", () =>
{
let insuranceController: any; // Using "any" to access private/protected methods without type errors.
let insuranceFixture: Insurance[];
beforeEach(() =>
{
// (Re)resolve the test target.
insuranceController = container.resolve<InsuranceController>("InsuranceController");
// Reset the insurance fixture before each test.
insuranceFixture = new ProfileInsuranceFactory().adjustPackageDates().get();
});
afterEach(() =>
{
// Restore all mocks to their original implementations.
vi.resetAllMocks();
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).toBeCalledWith(`Found ${insuranceFixture.length} insurance packages in profile ${sessionID}`);
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).toBeCalledWith(`Found ${insuranceFixture.length} insurance packages in profile ${sessionID}`);
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).toBeCalledWith(`Found ${insuranceFixture.length} insurance packages in profile ${sessionID}`);
// 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";
// Spy on the logger.debug method.
const mockLoggerDebug = vi.spyOn(insuranceController.logger, "debug");
vi.spyOn(insuranceController, "findItemsToDelete").mockImplementation(vi.fn());
vi.spyOn(insuranceController, "removeItemsFromInsurance").mockImplementation(vi.fn());
vi.spyOn(insuranceController, "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 ${insuranceController.countAllInsuranceItems(insuranceFixture)} 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, "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 = {
date: "01.11.2023",
time: "10:51",
location: "factory4_day"
};
const profile = {
insurance: [
{
messageContent: {
systemData: {
date: "01.11.2023",
time: "11:18",
location: "factory4_day"
}
}
},
{ // This one should be removed
messageContent: {
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[0].messageContent.systemData).toStrictEqual({
date: "01.11.2023",
time: "11:18",
location: "factory4_day"
});
});
it("should log a message indicating that the package was removed", () =>
{
const sessionID = "session-id";
const packageToRemove = {
date: "01.11.2023",
time: "10:51",
location: "factory4_day"
};
const profile = {
insurance: [
{
messageContent: {
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);
// 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 insurance package with date: ${packageToRemove.date}, time: ${packageToRemove.time}, and location: ${packageToRemove.location} from profile ${sessionID}. Remaining packages: ${profile.insurance.length}`);
});
it("should not remove any packages if the specified package is not found", () =>
{
const sessionID = "session-id";
const packageToRemove = {
date: "01.11.2023",
time: "10:51",
location: "factory4_day"
};
const profile = {
insurance: [
{
messageContent: {
systemData: {
date: "02.11.2023",
time: "10:50",
location: "factory4_night"
}
}
}
]
};
// 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 no packages were removed.
expect(profile.insurance.length).toBe(1);
});
});
describe("findItemsToDelete", () =>
{
it("should handle an empty insurance package", () =>
{
const insurancePackage = insuranceFixture[0];
insurancePackage.items = [];
// Execute the method.
const result = insuranceController.findItemsToDelete(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 mockPopulateItemsMap = vi.spyOn(insuranceController, "populateItemsMap");
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) =>
{
insured.items.forEach(item => toDelete.add(item._id));
});
vi.spyOn(insuranceController, "processRegularItems").mockImplementation(mockProcessRegularItems);
// Execute the method.
const result = insuranceController.findItemsToDelete(insured);
// Verify that the correct methods were called.
expect(mockPopulateItemsMap).toHaveBeenCalledTimes(1);
expect(mockPopulateParentAttachmentsMap).toHaveBeenCalledTimes(1);
expect(mockIsAttachmentAttached).toHaveBeenCalledTimes(numberOfItems + 1); // Once for each item, plus once more
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 mockPopulateItemsMap = vi.spyOn(insuranceController, "populateItemsMap");
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(insured);
// Verify that the correct methods were called.
expect(mockPopulateItemsMap).toHaveBeenCalled();
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 mockPopulateItemsMap = vi.spyOn(insuranceController, "populateItemsMap");
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) =>
{
insured.items.forEach(item => toDelete.add(item._id));
});
vi.spyOn(insuranceController, "processRegularItems").mockImplementation(mockProcessRegularItems);
const mockProcessAttachments = vi.fn((parentAttachmentsMap, itemsMap, traderId, toDelete) =>
{
insured.items.forEach(item => toDelete.add(item._id));
});
vi.spyOn(insuranceController, "processAttachments").mockImplementation(mockProcessAttachments);
// Execute the method.
const result = insuranceController.findItemsToDelete(insured);
// Verify that the correct methods were called.
expect(mockPopulateItemsMap).toHaveBeenCalled();
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 mockPopulateItemsMap = vi.spyOn(insuranceController, "populateItemsMap");
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(insured);
// Verify that the correct methods were called.
expect(mockPopulateItemsMap).toHaveBeenCalled();
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) =>
{
insured.items.forEach(item => toDelete.add(item._id));
});
vi.spyOn(insuranceController, "processRegularItems").mockImplementation(mockProcessRegularItems);
const mockProcessAttachments = vi.fn((parentAttachmentsMap, itemsMap, traderId, toDelete) =>
{
insured.items.forEach(item => toDelete.add(item._id));
});
vi.spyOn(insuranceController, "processAttachments").mockImplementation(mockProcessAttachments);
// Execute the method.
const result = insuranceController.findItemsToDelete(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.populateItemsMap(insured);
// Execute the method.
const result = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
// Verify that the map is populated correctly.
expect(result.size).toBe(6); // There are 6 base-level items in this insurance package.
const gun = result.get("911a0f04d5d9c7e239807ae0");
expect(gun.length).toBe(7); // This AK has 7 attachments.
// The attachments should be mapped to the AK properly...
const validAttachmentTemplates = [
"677c209ebb45445ebb42c405",
"4bd10f89836fd9f86aedcac1",
"8b1327270791b142ac341b03",
"da8cde1b3024c336f6e06152",
"bc041c0011d76f714b898400",
"9f8d7880a6e0a47a211ec5d3",
"db2ef9442178910eba985b51"
];
validAttachmentTemplates.forEach(value =>
{
// Verify that each template is present in the array of attachments.
expect(gun.some(item => item._id === value)).toBe(true);
});
});
it("should ignore gun accessories that cannot be modified in-raid", () =>
{
const insured = insuranceFixture[0];
// Generate the items map.
const itemsMap = insuranceController.populateItemsMap(insured);
// Execute the method.
const result = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
// Verify that the map is populated correctly.
expect(result.size).toBe(6); // There are 6 base-level items in this insurance package.
const gun = result.get("911a0f04d5d9c7e239807ae0");
expect(gun.length).toBe(7); // This AK has 7 valid attachments.
// These are attachments for the AK, but they are not raid moddable, so they should not be mapped.
const invalidAttachmentTemplates = [
"1e0b177df108c0c117028812",
"c9278dd8251e99578bf7a274",
"402b4086535a50ef7d9cef88",
"566335b3df586f34b47f5e35"
];
invalidAttachmentTemplates.forEach(value =>
{
expect(gun.every(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.populateItemsMap(insured);
// Execute the method.
const result = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
// Verify that the map is populated correctly.
expect(result.size).toBe(6); // There are 6 base-level items in this insurance package.
const gun = result.get("3679078e05f5b14466d6a730");
expect(gun.length).toBe(5); // This LShZ-2DTM has 5 valid attachments.
// The attachments should be mapped to the AK properly...
const validAttachmentTemplates = [
"a2b0c716162c5e31ec28c55a",
"dc565f750342cb2d19eeda06",
"e9ff62601669d9e2ea9c2fbb",
"ac134d7cf6c9d8e25edd0015",
"22274b895ecc80d51c3cba1c"
];
validAttachmentTemplates.forEach(value =>
{
// Verify that each template is present in the array of attachments.
expect(gun.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.populateItemsMap(insured);
// Execute the method.
const result = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
// Verify that the map is populated correctly.
expect(result.size).toBe(6); // There are 6 base-level items in this insurance package.
const gun = result.get("351180f3248d45c71cb2ebdc");
expect(insured.items.find(item => item._id === "351180f3248d45c71cb2ebdc").slotId).toBe("main");
expect(gun.length).toBe(14); // This AS VAL has 14 valid attachments.
});
it("should not map items that do not have a main-parent", () =>
{
insuranceFixture = new ProfileInsuranceFactory().adjustPackageDates().removeRegularItems().get();
const insured = insuranceFixture[0];
// Generate the items map.
const itemsMap = insuranceController.populateItemsMap(insured);
// Execute the method.
const result = insuranceController.populateParentAttachmentsMap(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", () =>
{
insuranceFixture = new ProfileInsuranceFactory().adjustPackageDates().removeRegularItems().get();
const insured = insuranceFixture[0];
// Generate the items map.
const itemsMap = insuranceController.populateItemsMap(insured);
// Suppress warnings.
const mockLoggerWarning = vi.spyOn(insuranceController.logger, "warning").mockImplementation(vi.fn());
// Execute the method.
insuranceController.populateParentAttachmentsMap(insured, itemsMap);
// Verify that the warning was logged.
expect(mockLoggerWarning).toHaveBeenCalled();
});
});
describe("processRegularItems", () =>
{
it("should process regular items and their non-attachment children", () =>
{
insuranceFixture = new ProfileInsuranceFactory().adjustPackageDates().removeAttachmentItems().get();
const insured = insuranceFixture[0];
const numberOfItems = insured.items.length;
const toDelete = new Set<string>();
// 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);
// Verify that the correct methods were called.
expect(mockIsAttachmentAttached).toHaveBeenCalled();
expect(mockFindAndReturnChildrenAsItems).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>();
// 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);
// 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 itemHelper = container.resolve<ItemHelper>("ItemHelper");
const insured = insuranceFixture[0];
const toDelete = new Set<string>();
// Mock rollForDelete to return true for all base-parent items.
const mockRollForDelete = vi.fn((traderId, insuredItem) =>
{
return !itemHelper.isAttachmentAttached(insuredItem);
});
vi.spyOn(insuranceController, "rollForDelete").mockImplementation(mockRollForDelete);
// Execute the method.
insuranceController.processRegularItems(insured, toDelete);
// 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 itemsMap = insuranceController.populateItemsMap(insured);
const parentToAttachmentMap = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
const toDelete = new Set<string>();
// Mock helper methods.
const mockProcessAttachmentByParent = vi.spyOn(insuranceController, "processAttachmentByParent");
// Execute the method.
insuranceController.processAttachments(parentToAttachmentMap, itemsMap, insured.traderId, toDelete);
// Verify
expect(mockProcessAttachmentByParent).toHaveBeenCalledTimes(parentToAttachmentMap.size);
});
it("should log the name of each parent item", () =>
{
const itemHelper = container.resolve<ItemHelper>("ItemHelper");
const insured = insuranceFixture[0];
const itemsMap = insuranceController.populateItemsMap(insured);
const parentToAttachmentMap = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
const toDelete = new Set<string>();
// Mock helper methods.
const mockLoggerDebug = vi.spyOn(insuranceController.logger, "debug");
// Execute the method.
insuranceController.processAttachments(parentToAttachmentMap, itemsMap, insured.traderId, toDelete);
// Verify that the name of each parent item is logged.
for (const [parentId] of parentToAttachmentMap)
{
const parentItem = itemsMap.get(parentId);
if (parentItem)
{
const expectedMessage = `Processing attachments for parent item: ${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.populateItemsMap(insured);
const parentToAttachmentMap = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
const attachments = parentToAttachmentMap.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 successful rolls", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.populateItemsMap(insured);
const parentToAttachmentMap = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
const attachments = parentToAttachmentMap.values().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 successful rolls: ${successfulRolls}`);
});
});
describe("sortAttachmentsByPrice", () =>
{
it("should sort the attachments array by maxPrice in descending order", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.populateItemsMap(insured);
const parentToAttachmentMap = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
const attachments = parentToAttachmentMap.values().next().value;
// Execute the method.
const sortedAttachments = insuranceController.sortAttachmentsByPrice(attachments);
// Verify the length of the sorted attachments array
expect(sortedAttachments.length).toBe(5);
// Verify that the attachments are sorted by maxPrice in descending order
for (let i = 1; i < sortedAttachments.length; i++)
{
expect(sortedAttachments[i - 1].maxPrice).toBeGreaterThanOrEqual(sortedAttachments[i].maxPrice);
}
});
it("should place attachments with null maxPrice at the bottom of the sorted list", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.populateItemsMap(insured);
const parentToAttachmentMap = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
const attachments = parentToAttachmentMap.values().next().value;
// Set the maxPrice of the first two attachments to null.
vi.spyOn(insuranceController.itemHelper, "getItemMaxPrice").mockReturnValueOnce(null).mockReturnValueOnce(null);
// Execute the method.
const sortedAttachments = insuranceController.sortAttachmentsByPrice(attachments);
// Verify that the attachments with null maxPrice are at the bottom of the list
const nullPriceAttachments = sortedAttachments.slice(-2);
nullPriceAttachments.forEach(attachment =>
{
expect(attachment.maxPrice).toBeNull();
});
// Verify that the rest of the attachments are sorted by maxPrice in descending order
for (let i = 1; i < sortedAttachments.length - 2; i++)
{
expect(sortedAttachments[i - 1].maxPrice).toBeGreaterThanOrEqual(sortedAttachments[i].maxPrice);
}
});
});
describe("logAttachmentsDetails", () =>
{
it("should log details for each attachment", () =>
{
const attachments = [
{ _id: "item1", name: "Item 1", maxPrice: 100 },
{ _id: "item2", name: "Item 2", maxPrice: 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, "Child Item - Name: Item 1, Max Price: 100");
expect(loggerDebugSpy).toHaveBeenNthCalledWith(2, "Child Item - Name: Item 2, Max 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 based on the rollForDelete method", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.populateItemsMap(insured);
const parentToAttachmentMap = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
const attachments = parentToAttachmentMap.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 count the number of successful rolls based on the rollForDelete method", () =>
{
const insured = insuranceFixture[0];
const itemsMap = insuranceController.populateItemsMap(insured);
const parentToAttachmentMap = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
const attachments = parentToAttachmentMap.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.populateItemsMap(insured);
const parentToAttachmentMap = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
const attachments = parentToAttachmentMap.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.populateItemsMap(insured);
const parentToAttachmentMap = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
const attachments = parentToAttachmentMap.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.populateItemsMap(insured);
const parentToAttachmentMap = insuranceController.populateParentAttachmentsMap(insured, itemsMap);
const attachments = parentToAttachmentMap.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.
toDelete.forEach(toDeleteId =>
{
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>();
insured.items.forEach(item => 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("adoptOrphanedItems", () =>
{
it("should adopt orphaned items by resetting them as base-level items", () =>
{
// Get all of the items, so that we can dynamically find the hideout item.
const insured = insuranceFixture[0];
const hideoutParentId = insuranceController.fetchHideoutItemParent(insured.items);
// Manually set one of the items to be orphaned.
insured.items[0].parentId = "9999"; // Should not exist in the items array.
insured.items[0].slotId = "main"; // Should not be "hideout".
// Iterate over the items and find an individual orphaned item.
const orphanedItem = insured.items.find(item => !insured.items.some(parent => parent._id === item.parentId));
// Setup tests to verify that the orphaned item we added is in fact orphaned.
expect(orphanedItem.parentId).toBe(insured.items[0].parentId);
expect(orphanedItem.slotId).toBe(insured.items[0].slotId);
// Execute the method.
insuranceController.adoptOrphanedItems(insured);
// Verify that the orphaned items have been adopted.
expect(orphanedItem.parentId).toBe(hideoutParentId);
expect(orphanedItem.slotId).toBe("hideout");
});
it("should not adopt items that are not orphaned", () =>
{
const unmodified = insuranceFixture[0];
// Create a deep copy of the insured items array.
const insured = JSON.parse(JSON.stringify(insuranceFixture[0]));
// Execute the method.
insuranceController.adoptOrphanedItems(insured);
// Verify that the orphaned items have been adopted.
expect(insured).toStrictEqual(unmodified);
});
it("should remove location data from adopted items", () =>
{
const insured = insuranceFixture[0];
// Manually set one of the items to be orphaned.
insured.items[0].parentId = "9999"; // Should not exist in the items array.
insured.items[0].slotId = "main"; // Should not be "hideout".
insured.items[0].location = {x: 1, y: 2, r: 3, isSearched: true}; // Should be removed.
// Iterate over the items and find an individual orphaned item.
const orphanedItem = insured.items.find(item => !insured.items.some(parent => parent._id === item.parentId));
// Setup tests to verify that the orphaned item we added is in fact orphaned.
expect(orphanedItem.parentId).toBe(insured.items[0].parentId);
expect(orphanedItem.slotId).toBe(insured.items[0].slotId);
// Execute the method.
insuranceController.adoptOrphanedItems(insured);
// Verify that the orphaned items have been adopted.
expect(orphanedItem).not.toHaveProperty("location");
});
});
describe("fetchHideoutItemParent", () =>
{
it("should return the parentId value of an item that has a slotId of 'hideout'", () =>
{
const insured = insuranceFixture[0];
const hideoutParentId = insuranceController.fetchHideoutItemParent(insured.items);
// Execute the method.
const result = insuranceController.fetchHideoutItemParent(insured.items);
// Verify that the hideout item parentId is returned.
expect(result).toBe(hideoutParentId);
});
it("should return an empty string if no item with a slotId of 'hideout' could be found", () =>
{
// Fetch a bunch of orphaned items that don't have a hideout parent.
const insuranceFixture = new ProfileInsuranceFactory().adjustPackageDates().removeRegularItems().get();
const insured = insuranceFixture[0];
// Execute the method.
const result = insuranceController.fetchHideoutItemParent(insured.items);
// Verify that the hideout item parentId is returned.
expect(result).toBe("");
});
it("should log a warning if the base-level item does not exist", () =>
{
// Fetch a bunch of orphaned items that don't have a hideout parent.
const insuranceFixture = new ProfileInsuranceFactory().adjustPackageDates().removeRegularItems().get();
const insured = insuranceFixture[0];
// Spy on the logger.
const loggerWarningSpy = vi.spyOn(insuranceController.logger, "warning");
// Execute the method.
insuranceController.fetchHideoutItemParent(insured.items);
// Verify that the hideout item parentId is returned.
expect(loggerWarningSpy).toHaveBeenCalled();
});
});
describe("sendMail", () =>
{
it("should send insurance failed message when no items are present", () =>
{
const traderHelper = container.resolve<TraderHelper>("TraderHelper");
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,
traderHelper.getTraderById(insurance.traderId),
MessageType.INSURANCE_RETURN,
insuranceFailedTpl,
insurance.items,
insurance.messageContent.maxStorageTime,
insurance.messageContent.systemData
);
});
it("should not send insurance failed message when items are present", () =>
{
const traderHelper = container.resolve<TraderHelper>("TraderHelper");
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,
traderHelper.getTraderById(insurance.traderId),
MessageType.INSURANCE_RETURN,
insurance.messageContent.templateId,
insurance.items,
insurance.messageContent.maxStorageTime,
insurance.messageContent.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, body: any, sessionId: string, insuranceController: any, mockGetPremium: any, mockPayMoney: any, 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: "item1", count: 100 },
{ id: "item2", count: 100 }
],
tid: "someTraderId",
Action: "",
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: "item1", count: 100 },
{ id: "item2", count: 100 }
],
tid: body.tid,
Action: "",
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 return the output with warnings if payment fails", () =>
{
// Override the payMoney mock to simulate a payment failure with a warning.
const expectedPayMoneyReturn = {
warnings: [{
index: 0,
errmsg: "Not enough money to complete transaction",
code: 500
}],
otherProperty: "property-value"
};
mockPayMoney.mockReturnValue(expectedPayMoneyReturn);
// 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: "Not enough money to complete transaction",
code: 500
}],
otherProperty: "property-value"
};
mockPayMoney.mockReturnValue(expectedPayMoneyReturn);
// 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);
});
});
});