added readme

This commit is contained in:
2026-06-21 18:26:52 +02:00
parent 63e9bc1d96
commit 17291bdc04
+109
View File
@@ -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.