128 lines
4.3 KiB
TypeScript
128 lines
4.3 KiB
TypeScript
// Ported from apps/server/test (CR-014 Step 14.5): now runs on the engine.
|
|
import { content } from "@deadhead/content";
|
|
import { companyResponseSchema, errorResponseSchema } from "@deadhead/schema";
|
|
import { describe, expect, it } from "vitest";
|
|
import {
|
|
useTestApp,
|
|
call,
|
|
getState,
|
|
edit,
|
|
setCredits,
|
|
testContract,
|
|
insertDispatch,
|
|
signUpOwner,
|
|
type Guest,
|
|
} from "../support";
|
|
|
|
const testApp = useTestApp();
|
|
|
|
async function setLoan(_guest: Guest, loanBalance: number) {
|
|
await edit(testApp().app, (save) => {
|
|
save.company.loanBalance = loanBalance;
|
|
});
|
|
}
|
|
|
|
const share = content.config.loanRepaymentShare;
|
|
|
|
describe("loan repayment on delivery", () => {
|
|
it("adds the net to credits, lowers the balance by the repayment, and records it", async () => {
|
|
const { app, clock } = testApp();
|
|
const owner = await signUpOwner(app);
|
|
await setCredits(testApp().app, 1000);
|
|
await setLoan(owner, 5000);
|
|
const record = await insertDispatch(testApp().app, {
|
|
shipId: owner.shipId,
|
|
pilotId: owner.avatarId,
|
|
departAt: clock.now,
|
|
contract: testContract({ payout: 2000 }),
|
|
});
|
|
clock.now = record.arrivalAt;
|
|
|
|
const state = await getState(app, owner);
|
|
// The avatar flew it, so there is no pilot pay.
|
|
const repayment = Math.floor(2000 * share);
|
|
expect(state.company).toMatchObject({
|
|
credits: 1000 + 2000 - repayment,
|
|
loanBalance: 5000 - repayment,
|
|
});
|
|
expect(state.summaries[0]).toMatchObject({
|
|
deadhead: false,
|
|
credits: 2000 - repayment,
|
|
loanRepayment: repayment,
|
|
});
|
|
});
|
|
|
|
it("repays only what is owed when the balance is small", async () => {
|
|
const { app, clock } = testApp();
|
|
const owner = await signUpOwner(app);
|
|
await setCredits(testApp().app, 0);
|
|
await setLoan(owner, 50);
|
|
const record = await insertDispatch(testApp().app, {
|
|
shipId: owner.shipId,
|
|
pilotId: owner.avatarId,
|
|
departAt: clock.now,
|
|
contract: testContract({ payout: 2000 }),
|
|
});
|
|
clock.now = record.arrivalAt;
|
|
const state = await getState(app, owner);
|
|
expect(state.company).toMatchObject({ credits: 1950, loanBalance: 0 });
|
|
});
|
|
|
|
it("puts the full amount into credits when there is no balance", async () => {
|
|
const { app, clock } = testApp();
|
|
const owner = await signUpOwner(app);
|
|
await setCredits(testApp().app, 0);
|
|
const record = await insertDispatch(testApp().app, {
|
|
shipId: owner.shipId,
|
|
pilotId: owner.avatarId,
|
|
departAt: clock.now,
|
|
contract: testContract({ payout: 2000 }),
|
|
});
|
|
clock.now = record.arrivalAt;
|
|
const state = await getState(app, owner);
|
|
expect(state.company).toMatchObject({ credits: 2000, loanBalance: 0 });
|
|
expect(state.summaries[0]!.loanRepayment).toBe(0);
|
|
});
|
|
});
|
|
|
|
describe("paying down the loan", () => {
|
|
const repay = (guest: Guest, amount: number) =>
|
|
call(testApp().app, guest, "POST", "/api/loan/repay", { amount });
|
|
|
|
it("moves the amount from credits to the loan", async () => {
|
|
const owner = await signUpOwner(testApp().app);
|
|
await setCredits(testApp().app, 3000);
|
|
await setLoan(owner, 5000);
|
|
const response = await repay(owner, 1200);
|
|
expect(response.statusCode).toBe(200);
|
|
expect(companyResponseSchema.parse(response.json()).company).toMatchObject({
|
|
credits: 1800,
|
|
loanBalance: 3800,
|
|
});
|
|
});
|
|
|
|
it("takes only the balance when asked to repay more", async () => {
|
|
const owner = await signUpOwner(testApp().app);
|
|
await setCredits(testApp().app, 3000);
|
|
await setLoan(owner, 400);
|
|
const { company } = companyResponseSchema.parse((await repay(owner, 2500)).json());
|
|
expect(company).toMatchObject({ credits: 2600, loanBalance: 0 });
|
|
});
|
|
|
|
it("fails with no changes when repaying more than the player has", async () => {
|
|
const { app } = testApp();
|
|
const owner = await signUpOwner(app);
|
|
await setCredits(testApp().app, 300);
|
|
await setLoan(owner, 5000);
|
|
const response = await repay(owner, 1000);
|
|
expect(response.statusCode).toBe(409);
|
|
expect(errorResponseSchema.parse(response.json()).error.code).toBe("insufficient-funds");
|
|
expect((await getState(app, owner)).company).toMatchObject({ credits: 300, loanBalance: 5000 });
|
|
});
|
|
|
|
it("rejects a non-positive amount", async () => {
|
|
const owner = await signUpOwner(testApp().app);
|
|
expect((await repay(owner, 0)).statusCode).toBe(400);
|
|
});
|
|
});
|