# ----------------------------------------------------------------------------- # Immo-Ka — Agrégateur de maisons à vendre (province de Québec) # Auteur : Simon-Pierre Boucher — contact@spboucher.ai # tests/test_mortgage_cmhc.py : assurance prêt hypothécaire (SCHL) — mise de # fonds minimale, barème de primes, admissibilité, TVQ sur la prime. # ----------------------------------------------------------------------------- import unittest from immoka.mortgage import cmhc class TestMinDownPayment(unittest.TestCase): def test_tiers(self): self.assertEqual(cmhc.min_down_payment(400_000), 20_000) # 5 % self.assertEqual(cmhc.min_down_payment(500_000), 25_000) self.assertEqual(cmhc.min_down_payment(600_000), 35_000) # 25k + 10k self.assertEqual(cmhc.min_down_payment(1_000_000), 75_000) self.assertEqual(cmhc.min_down_payment(1_500_000), 300_000) # 20 % self.assertEqual(cmhc.min_down_payment(2_000_000), 400_000) def test_zero(self): self.assertEqual(cmhc.min_down_payment(0), 0.0) class TestPremiumRate(unittest.TestCase): def test_bands(self): self.assertEqual(cmhc.premium_rate(0.65), 0.0060) self.assertEqual(cmhc.premium_rate(0.80), 0.0240) self.assertEqual(cmhc.premium_rate(0.90), 0.0310) self.assertEqual(cmhc.premium_rate(0.95), 0.0400) self.assertIsNone(cmhc.premium_rate(0.96)) # > 95 % : non assurable def test_30y_surcharge(self): self.assertAlmostEqual(cmhc.premium_rate(0.95, amort_years=30), 0.0420) class TestInsuranceQuote(unittest.TestCase): def test_insured_600k_40k_down(self): # prêt 560 k$, RPV 93,3 % → prime 4 % = 22 400 $, TVQ 9,975 % q = cmhc.insurance_quote(600_000, 40_000) self.assertTrue(q["required"] and q["eligible"]) self.assertEqual(q["premium"], 22_400) self.assertEqual(q["total_mortgage"], 582_400) self.assertEqual(q["qc_tax"], round(22_400 * 0.09975, 2)) self.assertEqual(q["premium_rate"], 4.0) def test_uninsured_20_pct_down(self): q = cmhc.insurance_quote(500_000, 100_000) self.assertFalse(q["required"]) self.assertEqual(q["premium"], 0.0) self.assertEqual(q["total_mortgage"], 400_000) def test_over_max_price_not_eligible(self): q = cmhc.insurance_quote(1_600_000, 200_000) self.assertTrue(q["required"]) self.assertFalse(q["eligible"]) self.assertEqual(q["premium"], 0.0) self.assertTrue(any("1,5 M$" in i for i in q["issues"])) def test_below_min_down_not_eligible(self): q = cmhc.insurance_quote(600_000, 20_000) # min légal = 35 000 $ self.assertFalse(q["eligible"]) self.assertTrue(any("minimum légal" in i for i in q["issues"])) def test_insured_30y_flagged(self): q = cmhc.insurance_quote(400_000, 40_000, amort_years=30) self.assertTrue(q["eligible"]) self.assertTrue(any("premier acheteur" in i for i in q["issues"])) # surprime 0,20 % appliquée (RPV 90 % : 3,10 + 0,20 = 3,30 %) self.assertEqual(q["premium_rate"], 3.30) def test_insured_over_30y_refused(self): q = cmhc.insurance_quote(400_000, 40_000, amort_years=35) self.assertFalse(q["eligible"]) def test_invalid_params(self): q = cmhc.insurance_quote(0, 0) self.assertFalse(q["eligible"]) self.assertIn("paramètres invalides", q["issues"]) if __name__ == "__main__": unittest.main()