150 lines
5 KiB
Python
150 lines
5 KiB
Python
"""Nomad, Malaysia, Thailand, Cyprus, Bulgaria, UAE regimes."""
|
||
from decimal import Decimal
|
||
|
||
import pytest
|
||
|
||
from fire_planner.tax.base import TaxInputs, TaxRegime
|
||
from fire_planner.tax.bulgaria import BulgariaTaxRegime
|
||
from fire_planner.tax.cyprus import CyprusTaxRegime
|
||
from fire_planner.tax.malaysia import MalaysiaTaxRegime
|
||
from fire_planner.tax.nomad import NomadTaxRegime
|
||
from fire_planner.tax.thailand import ThailandTaxRegime
|
||
from fire_planner.tax.uae import UaeTaxRegime
|
||
|
||
|
||
def test_nomad_zero_inputs() -> None:
|
||
assert NomadTaxRegime().compute_tax(TaxInputs()).total == Decimal("0")
|
||
|
||
|
||
def test_nomad_one_pc_premium() -> None:
|
||
b = NomadTaxRegime().compute_tax(
|
||
TaxInputs(capital_gains=Decimal("100000"), dividends=Decimal("20000")))
|
||
assert b.other == Decimal("1200")
|
||
assert b.total == Decimal("1200")
|
||
|
||
|
||
def test_nomad_isa_excluded_from_premium() -> None:
|
||
b = NomadTaxRegime().compute_tax(TaxInputs(isa_withdrawals=Decimal("100000")))
|
||
assert b.total == Decimal("0")
|
||
|
||
|
||
def test_malaysia_zero_on_foreign_income() -> None:
|
||
b = MalaysiaTaxRegime().compute_tax(
|
||
TaxInputs(capital_gains=Decimal("500000"), dividends=Decimal("50000")))
|
||
assert b.total == Decimal("0")
|
||
|
||
|
||
def test_thailand_zero_on_foreign_income() -> None:
|
||
b = ThailandTaxRegime().compute_tax(
|
||
TaxInputs(capital_gains=Decimal("500000"), dividends=Decimal("50000")))
|
||
assert b.total == Decimal("0")
|
||
|
||
|
||
def test_cyprus_gesy_below_cap() -> None:
|
||
# £100k chargeable, below €180k cap (~£154,800 default)
|
||
# 2.65% × £100,000 = £2,650
|
||
b = CyprusTaxRegime().compute_tax(TaxInputs(dividends=Decimal("100000")))
|
||
assert b.healthcare_levy == Decimal("2650.0000")
|
||
assert b.income_tax == Decimal("0")
|
||
assert b.capital_gains_tax == Decimal("0")
|
||
|
||
|
||
def test_cyprus_gesy_above_cap() -> None:
|
||
# £200k chargeable; cap GBP = £154,800 (€180k × 0.86)
|
||
# 2.65% × £154,800 = £4,102.20
|
||
b = CyprusTaxRegime().compute_tax(TaxInputs(dividends=Decimal("200000")))
|
||
assert b.healthcare_levy == Decimal("4102.2000")
|
||
|
||
|
||
def test_cyprus_custom_fx() -> None:
|
||
# Cap = 180,000 × 0.90 = 162,000
|
||
b = CyprusTaxRegime(gbp_per_eur=Decimal("0.90")).compute_tax(
|
||
TaxInputs(dividends=Decimal("200000")))
|
||
assert b.healthcare_levy == Decimal("4293.0000")
|
||
|
||
|
||
def test_uae_zero_on_all_personal_income() -> None:
|
||
"""UAE has 0% PIT — capital gains, dividends, earned income all 0."""
|
||
b = UaeTaxRegime().compute_tax(
|
||
TaxInputs(
|
||
earned_income=Decimal("60000"),
|
||
capital_gains=Decimal("500000"),
|
||
dividends=Decimal("80000"),
|
||
interest=Decimal("5000"),
|
||
))
|
||
assert b.total == Decimal("0")
|
||
assert b.income_tax == Decimal("0")
|
||
assert b.capital_gains_tax == Decimal("0")
|
||
assert b.dividend_tax == Decimal("0")
|
||
assert b.healthcare_levy == Decimal("0")
|
||
assert b.other == Decimal("0")
|
||
|
||
|
||
def test_uae_no_regulatory_premium() -> None:
|
||
"""Unlike NomadTaxRegime, UAE charges no premium — it's a real
|
||
tax residence with a treaty network."""
|
||
b = UaeTaxRegime().compute_tax(TaxInputs(capital_gains=Decimal("100000")))
|
||
assert b.total == Decimal("0")
|
||
|
||
|
||
def test_uae_zero_inputs() -> None:
|
||
assert UaeTaxRegime().compute_tax(TaxInputs()).total == Decimal("0")
|
||
|
||
|
||
def test_bulgaria_flat_10_pc() -> None:
|
||
b = BulgariaTaxRegime().compute_tax(
|
||
TaxInputs(
|
||
earned_income=Decimal("50000"),
|
||
capital_gains=Decimal("30000"),
|
||
dividends=Decimal("10000"),
|
||
))
|
||
assert b.income_tax == Decimal("5000.00")
|
||
assert b.capital_gains_tax == Decimal("3000.00")
|
||
assert b.dividend_tax == Decimal("1000.00")
|
||
assert b.total == Decimal("9000.00")
|
||
|
||
|
||
@pytest.mark.parametrize("regime", [
|
||
NomadTaxRegime(),
|
||
MalaysiaTaxRegime(),
|
||
ThailandTaxRegime(),
|
||
CyprusTaxRegime(),
|
||
BulgariaTaxRegime(),
|
||
UaeTaxRegime(),
|
||
])
|
||
def test_total_equals_sum(regime: TaxRegime) -> None:
|
||
inputs = TaxInputs(
|
||
earned_income=Decimal("60000"),
|
||
capital_gains=Decimal("15000"),
|
||
dividends=Decimal("8000"),
|
||
interest=Decimal("500"),
|
||
)
|
||
b = regime.compute_tax(inputs)
|
||
assert (b.total == b.income_tax + b.national_insurance + b.capital_gains_tax + b.dividend_tax +
|
||
b.healthcare_levy + b.other)
|
||
|
||
|
||
@pytest.mark.parametrize("regime", [
|
||
NomadTaxRegime(),
|
||
MalaysiaTaxRegime(),
|
||
ThailandTaxRegime(),
|
||
CyprusTaxRegime(),
|
||
BulgariaTaxRegime(),
|
||
UaeTaxRegime(),
|
||
])
|
||
def test_each_regime_has_a_name(regime: TaxRegime) -> None:
|
||
assert regime.name
|
||
assert isinstance(regime.name, str)
|
||
|
||
|
||
@pytest.mark.parametrize("regime", [
|
||
BulgariaTaxRegime(),
|
||
NomadTaxRegime(),
|
||
CyprusTaxRegime(),
|
||
])
|
||
def test_lower_spend_lower_tax(regime: TaxRegime) -> None:
|
||
"""Sanity: more chargeable income → never less tax (for the
|
||
regimes that actually charge)."""
|
||
less = regime.compute_tax(TaxInputs(dividends=Decimal("10000")))
|
||
more = regime.compute_tax(TaxInputs(dividends=Decimal("100000")))
|
||
assert more.total >= less.total
|