Files
2026-06-21 18:31:29 +02:00

110 lines
4.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Pet Insurance — Code Challenge
A Java coding challenge implementing a data driven calculation logic for a highly extensible 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.