""" Unit tests for tax simulation service. """ import pytest from app.services.tax_simulation import ( calculate_tax_deduction, calculate_pension_tax, simulate_accumulation, ) class TestCalculateTaxDeduction: def test_low_income_deduction_rate(self): """총급여 5,500만원 이하 → 공제율 16.5%""" result = calculate_tax_deduction( annual_income=40_000_000, contribution=9_000_000, account_type="irp", ) assert result["deduction_rate"] == 16.5 assert result["deductible_contribution"] == 9_000_000 assert result["tax_deduction"] == 9_000_000 * 0.165 def test_high_income_deduction_rate(self): """총급여 5,500만원 초과 → 공제율 13.2%""" result = calculate_tax_deduction( annual_income=80_000_000, contribution=9_000_000, account_type="irp", ) assert result["deduction_rate"] == 13.2 assert result["tax_deduction"] == 9_000_000 * 0.132 def test_boundary_income_55m(self): """정확히 5,500만원은 16.5% 적용""" result = calculate_tax_deduction( annual_income=55_000_000, contribution=5_000_000, account_type="irp", ) assert result["deduction_rate"] == 16.5 def test_contribution_exceeds_limit(self): """납입액이 900만원 한도 초과 시 900만원까지만 공제""" result = calculate_tax_deduction( annual_income=40_000_000, contribution=12_000_000, account_type="irp", ) assert result["deductible_contribution"] == 9_000_000 assert result["tax_deduction"] == 9_000_000 * 0.165 def test_contribution_below_limit(self): """납입액이 한도 미만이면 실제 납입액 기준 공제""" result = calculate_tax_deduction( annual_income=40_000_000, contribution=3_000_000, account_type="irp", ) assert result["deductible_contribution"] == 3_000_000 assert result["tax_deduction"] == 3_000_000 * 0.165 def test_dc_account_type(self): """DC 계좌도 동일 한도 적용 (DC+IRP 합산 900만원)""" result = calculate_tax_deduction( annual_income=60_000_000, contribution=9_000_000, account_type="dc", ) assert result["deduction_rate"] == 13.2 assert result["deductible_contribution"] == 9_000_000 def test_zero_contribution(self): result = calculate_tax_deduction( annual_income=50_000_000, contribution=0, account_type="irp", ) assert result["tax_deduction"] == 0 def test_result_structure(self): result = calculate_tax_deduction( annual_income=50_000_000, contribution=5_000_000, account_type="irp", ) assert "annual_income" in result assert "contribution" in result assert "account_type" in result assert "deduction_rate" in result assert "deductible_contribution" in result assert "tax_deduction" in result assert "irp_limit" in result class TestCalculatePensionTax: def test_pension_tax_under_70(self): """70세 미만 연금소득세 5.5%""" result = calculate_pension_tax( withdrawal_amount=10_000_000, withdrawal_type="pension", age=65, ) assert result["pension_tax_rate"] == 5.5 assert result["pension_tax"] == 10_000_000 * 0.055 def test_pension_tax_70_to_79(self): """70~79세 연금소득세 4.4%""" result = calculate_pension_tax( withdrawal_amount=10_000_000, withdrawal_type="pension", age=75, ) assert result["pension_tax_rate"] == 4.4 assert result["pension_tax"] == pytest.approx(10_000_000 * 0.044) def test_pension_tax_80_and_over(self): """80세 이상 연금소득세 3.3%""" result = calculate_pension_tax( withdrawal_amount=10_000_000, withdrawal_type="pension", age=85, ) assert result["pension_tax_rate"] == 3.3 assert result["pension_tax"] == pytest.approx(10_000_000 * 0.033) def test_pension_tax_boundary_70(self): """정확히 70세는 4.4% 적용""" result = calculate_pension_tax( withdrawal_amount=10_000_000, withdrawal_type="pension", age=70, ) assert result["pension_tax_rate"] == 4.4 def test_pension_tax_boundary_80(self): """정확히 80세는 3.3% 적용""" result = calculate_pension_tax( withdrawal_amount=10_000_000, withdrawal_type="pension", age=80, ) assert result["pension_tax_rate"] == 3.3 def test_lump_sum_tax(self): """일시금 수령 시 기타소득세 16.5%""" result = calculate_pension_tax( withdrawal_amount=10_000_000, withdrawal_type="lump_sum", age=65, ) assert result["lump_sum_tax_rate"] == 16.5 assert result["lump_sum_tax"] == 10_000_000 * 0.165 def test_comparison_shows_savings(self): """연금 수령이 일시금보다 세금이 적음""" result = calculate_pension_tax( withdrawal_amount=10_000_000, withdrawal_type="pension", age=65, ) assert result["tax_saving"] > 0 assert result["tax_saving"] == result["lump_sum_tax"] - result["pension_tax"] def test_result_structure(self): result = calculate_pension_tax( withdrawal_amount=10_000_000, withdrawal_type="pension", age=65, ) assert "withdrawal_amount" in result assert "pension_tax_rate" in result assert "pension_tax" in result assert "lump_sum_tax_rate" in result assert "lump_sum_tax" in result assert "tax_saving" in result class TestSimulateAccumulation: def test_basic_accumulation(self): """기본 적립 시뮬레이션""" result = simulate_accumulation( monthly_contribution=500_000, years=20, annual_return=7.0, tax_deduction_rate=16.5, ) assert len(result["yearly_data"]) == 20 assert result["total_contribution"] == 500_000 * 12 * 20 assert result["final_value"] > result["total_contribution"] def test_yearly_data_structure(self): result = simulate_accumulation( monthly_contribution=300_000, years=5, annual_return=5.0, tax_deduction_rate=13.2, ) first_year = result["yearly_data"][0] assert "year" in first_year assert "contribution" in first_year assert "cumulative_contribution" in first_year assert "investment_value" in first_year assert "tax_deduction" in first_year assert "cumulative_tax_deduction" in first_year def test_tax_deduction_accumulates(self): result = simulate_accumulation( monthly_contribution=500_000, years=3, annual_return=5.0, tax_deduction_rate=16.5, ) yearly = result["yearly_data"] annual_contribution = 500_000 * 12 deductible = min(annual_contribution, 9_000_000) expected_deduction = deductible * 0.165 assert yearly[0]["tax_deduction"] == expected_deduction assert yearly[2]["cumulative_tax_deduction"] == pytest.approx( expected_deduction * 3, rel=1e-6 ) def test_zero_return(self): result = simulate_accumulation( monthly_contribution=100_000, years=10, annual_return=0.0, tax_deduction_rate=16.5, ) assert result["final_value"] == result["total_contribution"] assert result["total_return"] == 0 def test_result_summary(self): result = simulate_accumulation( monthly_contribution=500_000, years=20, annual_return=7.0, tax_deduction_rate=16.5, ) assert "total_contribution" in result assert "final_value" in result assert "total_return" in result assert "total_tax_deduction" in result assert "yearly_data" in result