import { describe, expect, it } from "vitest"; import { BalanceToBudgetRatio } from "./balanceToBudgetRatio"; import { Gbp } from "./currency/gbp"; describe("balanceToBudgetRatio", () => { it("new() returns null when budget is null", () => { const result = BalanceToBudgetRatio.new(new Gbp(1, 0, 1), null); expect(result).toBeNull(); }); it("toString() rounds to 2 decimal places", () => { const mockCurrencyPi = { valueOf: () => Math.PI }; const mockCurrencyUnit = { valueOf: () => 1 }; const sut = BalanceToBudgetRatio.new(mockCurrencyPi, { quantity: mockCurrencyUnit, period: 42, }); const result = sut?.toString(); expect(result).toBe("3.14"); }); it("new() sets normalisedRatio to 1 when balance and budget are equal and positive", () => { const twoFifty = new Gbp(2, 50, 1); const result = BalanceToBudgetRatio.new(twoFifty, { quantity: twoFifty, period: 42, }); expect(result?.normalisedRatio).toBeCloseTo(1.0); }); it("new() sets normalisedRatio to 2 when balance is twice budget", () => { const twoFifty = new Gbp(2, 50, 1); const oneTwentyFive = new Gbp(1, 25, 1); const result = BalanceToBudgetRatio.new(twoFifty, { quantity: oneTwentyFive, period: 42, }); expect(result?.normalisedRatio).toBeCloseTo(2.0); }); it("new() sets normalisedRatio to -2 when a negative balance is -1 * twice budget", () => { const minusTwoFifty = new Gbp(2, 50, -1); const oneTwentyFive = new Gbp(1, 25, 1); const result = BalanceToBudgetRatio.new(minusTwoFifty, { quantity: oneTwentyFive, period: 42, }); expect(result?.normalisedRatio).toBeCloseTo(-2.0); }); it("new() sets normalisedRatio to -0.5 when balance is -1 * half a negative budget", () => { const oneTwentyFive = new Gbp(1, 25, 1); const minusTwoFifty = new Gbp(2, 50, -1); const result = BalanceToBudgetRatio.new(oneTwentyFive, { quantity: minusTwoFifty, period: 42, }); expect(result?.normalisedRatio).toBeCloseTo(-0.5); }); it("new() sets normalisedRatio to 1 when balance and budget are equal and negative", () => { const minusTwoFifty = new Gbp(2, 50, -1); const result = BalanceToBudgetRatio.new(minusTwoFifty, { quantity: minusTwoFifty, period: 42, }); expect(result?.normalisedRatio).toBeCloseTo(1.0); }); it('new() sets recommended action to "arrears" when ratio is less than 0', () => { const oneTwentyFive = new Gbp(1, 25, 1); const minusTwoFifty = new Gbp(2, 50, -1); const result = BalanceToBudgetRatio.new(oneTwentyFive, { quantity: minusTwoFifty, period: 42, }); expect(result?.recommendedAction).toBe("arrears"); }); it('new() sets recommended action to "save" when ratio is between 0 and 1', () => { const oneTwentyFive = new Gbp(1, 25, 1); const twoFifty = new Gbp(2, 50, 1); const result = BalanceToBudgetRatio.new(oneTwentyFive, { quantity: twoFifty, period: 42, }); expect(result?.recommendedAction).toBe("save"); }); it('new() sets recommended action to "spend" when ratio is larger than 1', () => { const oneTwentyFive = new Gbp(1, 25, 1); const twoFifty = new Gbp(2, 50, 1); const result = BalanceToBudgetRatio.new(twoFifty, { quantity: oneTwentyFive, period: 42, }); expect(result?.recommendedAction).toBe("spend"); }); it("normalises to a factor of 2 when all ratios are positive", () => { const oneTwentyFive = new Gbp(1, 25, 1); const twoFifty = new Gbp(2, 50, 1); const twoOverOne = BalanceToBudgetRatio.new(twoFifty, { quantity: oneTwentyFive, period: 42, }); const twoOverTwo = BalanceToBudgetRatio.new(twoFifty, { quantity: twoFifty, period: 42, }); const oneOverOne = BalanceToBudgetRatio.new(oneTwentyFive, { quantity: oneTwentyFive, period: 42, }); const oneOverTwo = BalanceToBudgetRatio.new(oneTwentyFive, { quantity: twoFifty, period: 42, }); const normaliser = BalanceToBudgetRatio.makeNormaliser( twoOverOne, twoOverTwo, oneOverOne, oneOverTwo, ); expect(normaliser(twoOverOne).normalisedRatio).toBeCloseTo(1.0); expect(normaliser(twoOverTwo).normalisedRatio).toBeCloseTo(0.5); expect(normaliser(oneOverOne).normalisedRatio).toBeCloseTo(0.5); expect(normaliser(oneOverTwo).normalisedRatio).toBeCloseTo(0.25); }); it("normalises to a factor of 2 when all ratios are negative", () => { const oneTwentyFive = new Gbp(1, 25, 1); const twoFifty = new Gbp(2, 50, 1); const minusOneTwentyFive = new Gbp(1, 25, -1); const minusTwoFifty = new Gbp(2, 50, -1); const minusTwoOverOne = BalanceToBudgetRatio.new(minusTwoFifty, { quantity: oneTwentyFive, period: 42, }); const minusTwoOverTwo = BalanceToBudgetRatio.new(minusTwoFifty, { quantity: twoFifty, period: 42, }); const minusOneOverOne = BalanceToBudgetRatio.new(minusOneTwentyFive, { quantity: oneTwentyFive, period: 42, }); const minusOneOverTwo = BalanceToBudgetRatio.new(minusOneTwentyFive, { quantity: twoFifty, period: 42, }); const normaliser = BalanceToBudgetRatio.makeNormaliser( minusTwoOverOne, minusTwoOverTwo, minusOneOverOne, minusOneOverTwo, ); expect(normaliser(minusTwoOverOne).normalisedRatio).toBeCloseTo(-1.0); expect(normaliser(minusTwoOverTwo).normalisedRatio).toBeCloseTo(-0.5); expect(normaliser(minusOneOverOne).normalisedRatio).toBeCloseTo(-0.5); expect(normaliser(minusOneOverTwo).normalisedRatio).toBeCloseTo(-0.25); }); it("normalises to a factor of 2 when ratios are a mix of positive and negative", () => { const oneTwentyFive = new Gbp(1, 25, 1); const twoFifty = new Gbp(2, 50, 1); const minusOneTwentyFive = new Gbp(1, 25, -1); const minusTwoFifty = new Gbp(2, 50, -1); const twoOverOne = BalanceToBudgetRatio.new(twoFifty, { quantity: oneTwentyFive, period: 42, }); const twoOverTwo = BalanceToBudgetRatio.new(twoFifty, { quantity: twoFifty, period: 42, }); const oneOverOne = BalanceToBudgetRatio.new(oneTwentyFive, { quantity: oneTwentyFive, period: 42, }); const oneOverTwo = BalanceToBudgetRatio.new(oneTwentyFive, { quantity: twoFifty, period: 42, }); const minusTwoOverOne = BalanceToBudgetRatio.new(minusTwoFifty, { quantity: oneTwentyFive, period: 42, }); const minusTwoOverTwo = BalanceToBudgetRatio.new(minusTwoFifty, { quantity: twoFifty, period: 42, }); const minusOneOverOne = BalanceToBudgetRatio.new(minusOneTwentyFive, { quantity: oneTwentyFive, period: 42, }); const minusOneOverTwo = BalanceToBudgetRatio.new(minusOneTwentyFive, { quantity: twoFifty, period: 42, }); const normaliser = BalanceToBudgetRatio.makeNormaliser( twoOverOne, twoOverTwo, oneOverOne, oneOverTwo, minusTwoOverOne, minusTwoOverTwo, minusOneOverOne, minusOneOverTwo, ); expect(normaliser(twoOverOne).normalisedRatio).toBeCloseTo(1.0); expect(normaliser(twoOverTwo).normalisedRatio).toBeCloseTo(0.5); expect(normaliser(oneOverOne).normalisedRatio).toBeCloseTo(0.5); expect(normaliser(oneOverTwo).normalisedRatio).toBeCloseTo(0.25); expect(normaliser(minusTwoOverOne).normalisedRatio).toBeCloseTo(-1.0); expect(normaliser(minusTwoOverTwo).normalisedRatio).toBeCloseTo(-0.5); expect(normaliser(minusOneOverOne).normalisedRatio).toBeCloseTo(-0.5); expect(normaliser(minusOneOverTwo).normalisedRatio).toBeCloseTo(-0.25); }); it("sorts large positive ratios before small positive ratios", () => { const oneTwentyFive = new Gbp(1, 25, 1); const twoFifty = new Gbp(2, 50, 1); const twoOverTwo = BalanceToBudgetRatio.new(twoFifty, { quantity: twoFifty, period: 42, }); const oneOverTwo = BalanceToBudgetRatio.new(oneTwentyFive, { quantity: twoFifty, period: 42, }); const resultLeftways = BalanceToBudgetRatio.compare(twoOverTwo, oneOverTwo); const resultRightways = BalanceToBudgetRatio.compare( oneOverTwo, twoOverTwo, ); expect(resultLeftways).toBeCloseTo(-resultRightways); expect(resultLeftways).toBeLessThan(0); }); it("sorts small negative ratios before large negative ratios", () => { const twoFifty = new Gbp(2, 50, 1); const minusOneTwentyFive = new Gbp(1, 25, -1); const minusTwoFifty = new Gbp(2, 50, -1); const minusTwoOverTwo = BalanceToBudgetRatio.new(minusTwoFifty, { quantity: twoFifty, period: 42, }); const minusOneOverTwo = BalanceToBudgetRatio.new(minusOneTwentyFive, { quantity: twoFifty, period: 42, }); const resultLeftways = BalanceToBudgetRatio.compare( minusOneOverTwo, minusTwoOverTwo, ); const resultRightways = BalanceToBudgetRatio.compare( minusTwoOverTwo, minusOneOverTwo, ); expect(resultLeftways).toBeCloseTo(-resultRightways); expect(resultLeftways).toBeLessThan(0); }); it("sorts positive ratios before negative ratios", () => { const twoFifty = new Gbp(2, 50, 1); const minusTwoFifty = new Gbp(2, 50, -1); const twoOverTwo = BalanceToBudgetRatio.new(twoFifty, { quantity: twoFifty, period: 42, }); const minusTwoOverTwo = BalanceToBudgetRatio.new(minusTwoFifty, { quantity: twoFifty, period: 42, }); const resultLeftways = BalanceToBudgetRatio.compare( twoOverTwo, minusTwoOverTwo, ); const resultRightways = BalanceToBudgetRatio.compare( minusTwoOverTwo, twoOverTwo, ); expect(resultLeftways).toBeCloseTo(-resultRightways); expect(resultLeftways).toBeLessThan(0); }); });