← back to Govarbitrage
src/engines/scoring.test.ts
73 lines
import { describe, expect, it } from "vitest";
import { computeScores, computeComponents } from "./scoring";
import type { ScoreInput } from "./scoring";
const base: ScoreInput = {
roi: 0.6,
expectedNetProfit: 3000,
demandScore: 70,
daysUntilSold: 25,
weightLbs: 40,
condition: "USED_GOOD",
bidCount: 2,
localPickup: false,
hasBuyerLeads: false,
confidenceScore: 80,
probabilityOfSale: 0.7,
isFreight: false,
};
describe("computeScores", () => {
it("returns all nine scoring profiles", () => {
const scores = computeScores(base);
expect(scores).toHaveLength(9);
const profiles = scores.map((s) => s.profile).sort();
expect(profiles).toContain("OVERALL_OPPORTUNITY");
expect(profiles).toContain("BEST_ARBITRAGE");
expect(profiles).toContain("PARTS_ONLY");
});
it("every score is 0..100 and carries a non-empty explanation", () => {
for (const s of computeScores(base)) {
expect(s.value).toBeGreaterThanOrEqual(0);
expect(s.value).toBeLessThanOrEqual(100);
expect(s.explanation.length).toBeGreaterThan(20);
expect(s.factors.length).toBeGreaterThan(0);
}
});
it("rewards a genuine salvage lot under PARTS_ONLY and penalizes non-parts", () => {
const parts = computeScores({ ...base, condition: "FOR_PARTS" }).find(
(s) => s.profile === "PARTS_ONLY",
)!;
const notParts = computeScores({ ...base, condition: "NEW" }).find(
(s) => s.profile === "PARTS_ONLY",
)!;
expect(parts.value).toBeGreaterThan(notParts.value);
});
it("higher ROI raises the arbitrage component and BEST_ARBITRAGE score", () => {
const low = computeScores({ ...base, roi: 0.05 }).find((s) => s.profile === "BEST_ARBITRAGE")!;
const high = computeScores({ ...base, roi: 1.5 }).find((s) => s.profile === "BEST_ARBITRAGE")!;
expect(high.value).toBeGreaterThan(low.value);
});
it("flags HIGH risk on low confidence + thin margin", () => {
const risky = computeScores({
...base,
confidenceScore: 30,
roi: 0.05,
condition: "UNKNOWN",
daysUntilSold: 100,
})[0];
expect(risky.risk).toBe("HIGH");
});
it("marks light non-pickup items as EASY drop-ship, heavy freight as harder", () => {
const easy = computeComponents(base);
expect(easy.logistics).toBeGreaterThan(50);
const heavy = computeScores({ ...base, weightLbs: 700, isFreight: true })[0];
expect(["DIFFICULT", "INFEASIBLE", "MODERATE"]).toContain(heavy.dropShip);
});
});