From 17291bdc04f536c845ed6b0e32db215e91f19ee2 Mon Sep 17 00:00:00 2001 From: kasun Date: Sun, 21 Jun 2026 18:26:52 +0200 Subject: [PATCH] added readme --- README.md | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..6436d14 --- /dev/null +++ b/README.md @@ -0,0 +1,109 @@ +# Pet Insurance — Code Challenge + +A Java coding challenge implementing the calculation logic for a pet health insurance product. + +## Build & test + +Requires **Java 25** and **Maven 3.x**. + +```bash +# Compile +mvn compile + +# Run all tests +mvn test + +# Full build +mvn package +``` + +## Project structure + +``` +src/ +├── main/ +│ ├── java/com/pfefferminzia/petinsurance/ +│ │ ├── animals/ # Animal sealed interface + Dog, Cat, SmallAnimal records +│ │ ├── calculations/ # InsuranceContract, Calculations, CalculationResult +│ │ ├── exception/ # InvalidInsuranceContractException +│ │ └── tariffs/ # TariffOption, TariffConfiguration, TariffLoader +│ └── resources/ +│ └── tariff.properties # Runtime tariff configuration +└── test/ + ├── java/.../calculations/ + │ └── CalculationsTest.java + └── resources/ + └── tariff.properties # Test tariff configuration (mirrors main) +``` + +## Architecture + +### Animals + +`Animal` is a sealed interface (`permits Dog, Cat, SmallAnimal`). Each type is an immutable record: + +- `Dog(LocalDate birthday, String breed)` — identifies with species key `"DOG"` +- `Cat(LocalDate birthday)` — species key `"CAT"` +- `SmallAnimal(LocalDate birthday)` — species key `"SMALLANIMAL"` + +The interface exposes `birthday()` and `speciesKey()`. Base insurance sums are **not** hardcoded in the animal types; they live in configuration (see below). + +### Tariff configuration (`tariff.properties`) + +All tariff data is loaded from `tariff.properties` at startup via `TariffLoader.loadFromFile()`, which produces a `TariffConfiguration` record. This makes adding new tariffs, animals, age brackets, or breed adjustments a config-only change. + +```properties +# Animal base insurance sums +baseSum.DOG=2500.00 +baseSum.CAT=2000.00 +baseSum.SMALLANIMAL=1000.00 + +# Tariffs: factor,availableFromDate +tariffs.KOMPAKT=1.0,1970-01-01 +tariffs.OPTIMAL=1.2,1970-01-01 +tariffs.PREMIUM=1.4,2026-07-01 + +# Age-based premium factors (≤ age → factor; elderlyFactor covers > 7) +age.2=0.20 +age.5=0.25 +age.7=0.28 +elderlyFactor=0.30 + +# Dog breed surcharge (positive) / discount (negative) +breed.DACKEL=-0.1 +breed.BERNHARDINER=0.1 +``` + +`TariffConfiguration.getTariffFactor(tariffName, startDate)` throws `IllegalStateException` if the chosen tariff is not yet available on the given start date (e.g. PREMIUM before 2026-07-01). + +### Calculation flow + +1. Build an `InsuranceContract(animal, insuranceStartDate, tariffName)`. The record constructor validates all fields eagerly and throws `InvalidInsuranceContractException` collecting all violations. +2. Call `Calculations.calculateInsuranceSumAndYearlyPremium(contract)`, which returns a `CalculationResult(insuranceSum, yearlyPremium)`. + +**Insurance sum** = `baseSum(animal) × tariffFactor` + +**Yearly premium** = `insuranceSum × ageFactor × (1 + breedFactor)` + +- `ageFactor` is looked up from the age bracket table based on exact years between `birthday` and `insuranceStartDate`. +- `breedFactor` only applies to `Dog`; all other animals use 0 (no adjustment). + +### Worked examples + +| Animal | Insurance sum | Yearly premium | +|-----------------------------|--------------|----------------| +| Husky, 7y, PREMIUM | 3500 | 980 | +| Dackel, 7y, PREMIUM | 3500 | 882 | +| Bernhardiner, 7y, PREMIUM | 3500 | 1078 | +| Cat, 3y, OPTIMAL | 2400 | 600 | +| SmallAnimal, 1y, KOMPAKT | 1000 | 200 | + +All five examples are covered by `CalculationsTest`. + +## Extensibility + +- **New animal type** — add a record implementing `Animal`, register its base sum in `tariff.properties`. +- **New tariff** — add one line to `tariff.properties` with a factor and availability date. +- **New breed adjustment** — add one line to the `breed.*` section. +- **New age bracket** — add one `age.N` entry; `TariffConfiguration.getAgeFactor` uses a `NavigableMap` ceiling lookup so ordering is automatic. +- **New calculation** — extend `Calculations` or add a new method; `CalculationResult` can be widened to carry additional fields.