Skip to content

Commit

Permalink
test: add test for depreciation functions
Browse files Browse the repository at this point in the history
  • Loading branch information
zz-hh-aa committed Nov 29, 2024
1 parent 243942e commit b59f360
Showing 1 changed file with 178 additions and 66 deletions.
244 changes: 178 additions & 66 deletions app/models/Property.test.ts
Original file line number Diff line number Diff line change
@@ -1,71 +1,183 @@
import { Property } from "./Property";
import { HOUSE_BREAKDOWN_PERCENTAGES, MAINTENANCE_LEVELS } from './constants';

let property: Property;

beforeEach(() => {
property = new Property({
postcode: "WV8 1HG",
houseType: "T",
numberOfBedrooms: 2,
age: 10,
size: 88,
maintenancePercentage: 0.02,
newBuildPricePerMetre: 2120,
averageMarketPrice: 218091.58,
itl3: "TLG24",
describe('Property', () => {
beforeEach(() => {
property = new Property({
postcode: "MK14 7AG",
houseType: "T",
numberOfBedrooms: 2,
age: 10,
size: 88,
maintenancePercentage: 0.02,
newBuildPricePerMetre: 2120,
averageMarketPrice: 219135,
itl3: "TLJ12",
});
});
});

it("can be instantiated", () => {
expect(property).toBeInstanceOf(Property);
});

it("correctly calculates the newBuildPrice", () => {
expect(property.newBuildPrice).toBeCloseTo(186560);
});

it("correctly calculates the depreciatedBuildPrice for existing builds (age > 0)", () => {
expect(property.depreciatedBuildPrice).toBeCloseTo(172988.92);
});

it("correctly calculates depreciatedBuildPrice for newbuilds (age = 0)", () => {
property = new Property({
postcode: "WV8 1HG",
houseType: "T",
numberOfBedrooms: 2,
age: 1,
size: 88,
maintenancePercentage: 0.02,
newBuildPricePerMetre: 2120,
averageMarketPrice: 218091.58,
itl3: "TLG24",

let property: Property;

it("can be instantiated", () => {
expect(property).toBeInstanceOf(Property);
});

it("correctly calculates the newBuildPrice", () => {
expect(property.newBuildPrice).toBeCloseTo(186560);
});

//
it("correctly calculates the bedWeightedAveragePrice", () => {
expect(property.bedWeightedAveragePrice).toBeCloseTo(219135);
});

it("correctly calculates the landPrice", () => {
expect(property.landPrice).toBeCloseTo(32575);
});

it("correctly calculates the landToTotalRatio", () => {
expect(property.landToTotalRatio).toBeCloseTo(0.1446);
});

it("correctly calculates the values even for number of bedroooms exceeding the max ", () => {
property = new Property({
postcode: "WV8 1HG",
houseType: "T",
numberOfBedrooms: 20,
age: 11,
size: 88,
maintenancePercentage: 0.02,
newBuildPricePerMetre: 2120,
averageMarketPrice: 218091.58,
itl3: "TLG24",
});
expect(property).toBeInstanceOf(Property);
});
expect(property.depreciatedBuildPrice).toBeCloseTo(186560)
});

it("correctly calculates the bedWeightedAveragePrice", () => {
expect(property.bedWeightedAveragePrice).toBeCloseTo(218091.58);
});

it("correctly calculates the landPrice", () => {
expect(property.landPrice).toBeCloseTo(31531.579);
});

it("correctly calculates the landToTotalRatio", () => {
expect(property.landToTotalRatio).toBeCloseTo(0.14);
});

it("correctly calculates the values even for number of bedroooms exceeding the max ", () => {
property = new Property({
postcode: "WV8 1HG",
houseType: "T",
numberOfBedrooms: 20,
age: 9,
size: 88,
maintenancePercentage: 0.02,
newBuildPricePerMetre: 2120,
averageMarketPrice: 218091.58,
itl3: "TLG24",

it("correctly returns newBuildPrice if newbuild", () => {
property = new Property({
postcode: "WV8 1HG",
houseType: "T",
numberOfBedrooms: 20,
age: 1,
size: 88,
maintenancePercentage: 0.02,
newBuildPricePerMetre: 2120,
averageMarketPrice: 218091.58,
itl3: "TLG24",
});

expect(property.depreciatedBuildPrice).toBe(property.newBuildPrice);
});

describe('depreciation calculations (existing build)', () => {

it("correctly calculates newComponentValue for foundations", () => {
const result = property.calculateComponentValue(
'foundations',
property.newBuildPrice,
property.age,
MAINTENANCE_LEVELS[0]
);

const expectedNewComponentValue =
property.newBuildPrice *
HOUSE_BREAKDOWN_PERCENTAGES.foundations.percentageOfHouse;

expect(result.newComponentValue).toBe(expectedNewComponentValue);
expect(result.maintenanceAddition).toBe(0); // Foundations should have no maintenance
});

it("correctly calculates depreciationFactor for internal linings", () => {
const result = property.calculateComponentValue(
'internalLinings',
property.newBuildPrice,
property.age,
MAINTENANCE_LEVELS[0]
);

const expectedDepreciationFactor =
1 - (HOUSE_BREAKDOWN_PERCENTAGES.internalLinings.depreciationPercentageYearly * property.age);

expect(result.depreciationFactor).toBe(expectedDepreciationFactor);
});

it("correctly calculates maintenanceAddition for electrical appliances", () => {
const result = property.calculateComponentValue(
'electricalAppliances',
property.newBuildPrice,
property.age,
MAINTENANCE_LEVELS[0]
);

const expectedMaintenanceAddition =
MAINTENANCE_LEVELS[0] *
property.newBuildPrice *
property.age *
HOUSE_BREAKDOWN_PERCENTAGES.electricalAppliances.percentOfMaintenanceYearly;

expect(result.maintenanceAddition).toBe(expectedMaintenanceAddition);
});

it("correctly calculates depreciatedComponentValue for ventilation services", () => {
const result = property.calculateComponentValue(
'ventilationServices',
property.newBuildPrice,
property.age,
MAINTENANCE_LEVELS[0]
);

const component = HOUSE_BREAKDOWN_PERCENTAGES.ventilationServices;
const newComponentValue = property.newBuildPrice * component.percentageOfHouse;
const depreciationFactor = 1 - (component.depreciationPercentageYearly * property.age);
const maintenanceAddition =
MAINTENANCE_LEVELS[0] *
property.newBuildPrice *
property.age *
component.percentOfMaintenanceYearly;

const expectedValue = Math.max(
(newComponentValue * depreciationFactor) + maintenanceAddition,
0
);

expect(result.depreciatedComponentValue).toBe(expectedValue);
});

it("ensures depreciatedComponentValue never goes below 0", () => {
const result = property.calculateComponentValue(
'ventilationServices',
property.newBuildPrice,
100, // High age to test possible negative values
MAINTENANCE_LEVELS[0]
);

expect(result.depreciatedComponentValue).toBeGreaterThanOrEqual(0);
});

it('should calculate correct depreciation for a 10-year-old house', () => {
// Calculate the expected value manually
const newBuildPrice = property.newBuildPrice;
let expectedDepreciatedPrice = 0;

// Calculate for each component (mimicking calculateComponentValue())
for (const [key, value] of Object.entries(HOUSE_BREAKDOWN_PERCENTAGES)) {
const newComponentValue = newBuildPrice * value.percentageOfHouse;
const depreciationFactor = 1 - (value.depreciationPercentageYearly * property.age);

const maintenanceAddition = (key === 'foundations' || key === 'structureEnvelope')
? 0
: MAINTENANCE_LEVELS[0] * newBuildPrice * property.age * value.percentOfMaintenanceYearly;

const depreciatedComponentValue = Math.max(
(newComponentValue * depreciationFactor) + maintenanceAddition,
0
);

expectedDepreciatedPrice += depreciatedComponentValue;
}

expect(property.depreciatedBuildPrice).toBeCloseTo(Number(expectedDepreciatedPrice.toFixed(2)));
});
});
expect(property).toBeInstanceOf(Property);
});
});

0 comments on commit b59f360

Please sign in to comment.