102 lines
3.2 KiB
TypeScript
102 lines
3.2 KiB
TypeScript
import { content } from "@deadhead/content";
|
||
import { describe, expect, it } from "vitest";
|
||
import {
|
||
createDispatchRecord,
|
||
resolveDispatch,
|
||
splitLoanRepayment,
|
||
type Contract,
|
||
} from "../src/index";
|
||
|
||
describe("loan repayment split", () => {
|
||
it("takes min(balance, credits × share), rounded down to whole credits", () => {
|
||
expect(splitLoanRepayment(1000, 5000, 0.3)).toEqual({ repayment: 300, net: 700 });
|
||
expect(splitLoanRepayment(1001, 5000, 0.3)).toEqual({ repayment: 300, net: 701 });
|
||
expect(splitLoanRepayment(1000, 120, 0.3)).toEqual({ repayment: 120, net: 880 });
|
||
});
|
||
|
||
it("takes nothing when there is no balance, or no positive payout", () => {
|
||
expect(splitLoanRepayment(1000, 0, 0.3)).toEqual({ repayment: 0, net: 1000 });
|
||
expect(splitLoanRepayment(0, 5000, 0.3)).toEqual({ repayment: 0, net: 0 });
|
||
expect(splitLoanRepayment(-60, 5000, 0.3)).toEqual({ repayment: 0, net: -60 });
|
||
});
|
||
|
||
it("always splits the whole amount and never repays more than the balance", () => {
|
||
for (const credits of [-500, -1, 0, 1, 7, 99, 1000, 123_457]) {
|
||
for (const balance of [0, 1, 50, 299, 5000, 1_000_000]) {
|
||
for (const share of [0, 0.1, 0.3, 1]) {
|
||
const { repayment, net } = splitLoanRepayment(credits, balance, share);
|
||
expect(net + repayment).toBe(credits);
|
||
expect(repayment).toBeLessThanOrEqual(balance);
|
||
expect(repayment).toBeGreaterThanOrEqual(0);
|
||
expect(repayment).toBe(
|
||
credits > 0 && balance > 0 ? Math.min(balance, Math.floor(credits * share)) : 0,
|
||
);
|
||
}
|
||
}
|
||
}
|
||
});
|
||
});
|
||
|
||
describe("resolution with a loan", () => {
|
||
const now = Date.parse("2100-01-01T00:30:00Z");
|
||
const contract: Contract = {
|
||
id: "c1",
|
||
originId: "earth",
|
||
destinationId: "moon",
|
||
cargoTypeId: "standard",
|
||
goodId: "consumer-goods",
|
||
shipper: "Pacific Orbital Logistics",
|
||
priority: false,
|
||
deliveryWindowGameHours: null,
|
||
tier: 0,
|
||
massTonnes: 10,
|
||
payout: 2000,
|
||
requiredSkill: null,
|
||
requiredLongHaulLevel: 0,
|
||
offeredAt: now,
|
||
expiresAt: now + 3_600_000,
|
||
firstContact: false,
|
||
};
|
||
const record = createDispatchRecord(
|
||
{
|
||
id: "d1",
|
||
ship: { id: "s1", modelId: "courier", locationId: "earth", inFlight: false },
|
||
pilot: {
|
||
id: "p1",
|
||
isAvatar: false,
|
||
assignedShipId: "s1",
|
||
inFlight: false,
|
||
skills: {},
|
||
restEndsAt: null,
|
||
},
|
||
contract,
|
||
tutorial: false,
|
||
now,
|
||
worldSeed: 7,
|
||
},
|
||
content,
|
||
);
|
||
|
||
it("returns the repayment alongside the credits to add, after pilot pay", () => {
|
||
const result = resolveDispatch(
|
||
{
|
||
record,
|
||
pilotIsAvatar: false,
|
||
discoveredIds: new Set(["earth", "moon"]),
|
||
loanBalance: 5000,
|
||
pilotDutyHours: 0,
|
||
pilotSkills: {},
|
||
now: record.arrivalAt,
|
||
},
|
||
content,
|
||
);
|
||
if (result.status !== "resolved") throw new Error("expected a resolution");
|
||
const afterPay = 2000 - content.config.pilotPayPerDelivery;
|
||
const repayment = Math.floor(afterPay * content.config.loanRepaymentShare);
|
||
expect(result.resolution).toMatchObject({
|
||
loanRepayment: repayment,
|
||
credits: afterPay - repayment,
|
||
});
|
||
});
|
||
});
|