Expense Report
Level: Advanced 60–90 minConcepts: Business LogicValidationStateEdge Cases
Solutions: C# | TypeScript | Python
Build an expense report system that enforces company spending policies and approval workflows.
Requirements
An expense report contains:
employeeName— who submitted itexpenses— a list of individual expense itemsstatus— Draft, Pending, Approved, Rejected
Each expense item has:
description— what the expense is foramount— cost in dollars (positive number)category— one of: Meals, Travel, Accommodation, Equipment, Other
Spending Limits
Each category has a per-item limit:
| Category | Per-Item Limit |
|---|---|
| Meals | $50 |
| Travel | $500 |
| Accommodation | $200 |
| Equipment | $1,000 |
| Other | $100 |
An expense item that exceeds its category limit is flagged as over-limit.
Report Limits
- Total report amount must not exceed $5,000
- A report with any over-limit items requires manager approval
- A report over $2,500 total requires manager approval regardless of individual items
Approval Workflow
- A report starts as Draft
- Submitting a report moves it to Pending
- A pending report can be Approved or Rejected
- Only pending reports can be approved or rejected
- Approved and rejected reports cannot be modified
- A draft report can have expenses added or removed
- Submitting an empty report is not allowed
Output
An expense report should produce a summary:
Expense Report: Alice Johnson
Status: Pending
Meals: Team lunch $45.00
Travel: Flight to NYC $350.00
Meals: Client dinner $62.00 [OVER LIMIT]
Equipment: Laptop $1,200.00 [OVER LIMIT]
Total: $1,657.00
Requires Approval: Yes (over-limit items)
Test Cases
| Scenario | Expenses | Total | Approval Required? |
|---|---|---|---|
| All under limits | Meals $30, Travel $200 | $230 | No |
| One over-limit item | Meals $60 | $60 | Yes (over limit) |
| Total over $2,500 | Travel $500 x 6 | $3,000 | Yes (total) |
| Empty report submit | (none) | $0 | Error: cannot submit |
| Over $5,000 | Equipment $1,000 x 6 | $6,000 | Error: exceeds maximum |
Additional cases:
- Cannot add expenses to an approved report
- Cannot approve a draft report (must be pending)
- Rejecting a report requires a reason
- A report can be resubmitted after rejection (moves back to Draft first)
Bonus
- Add date tracking — expenses older than 90 days are flagged as stale
- Add receipt requirements — items over $25 must have a receipt attached (boolean flag)
- Add department budgets — each department has a monthly spending cap
Hint
Start with the simplest case: a report with one expense, no limits exceeded. Build the approval state machine separately from the spending policy rules. Compose them together only after both work independently.
Reference Walkthrough
Full C#, TypeScript, and Python implementations live at tddbuddy-reference-katas/expense-report with twenty-two scenarios across all three languages, fluent ReportBuilder and ExpenseItemBuilder, six domain-specific exception types, and a SpendingPolicy lookup for per-category limits.
- C# (.NET 8, xUnit, FluentAssertions) — walkthrough
- TypeScript (Node 20, Vitest, strict types) — walkthrough
- Python (3.11, pytest, dataclasses) — walkthrough
This kata ships in middle gear — one commit per language with the full domain design. See the repo’s Gears section for why that’s a deliberate teaching choice.
Related reading
- Examples Pin Intent. Properties Pin the Invariants.
Example-based tests anchor scenarios in the team's vocabulary. Property-based tests anchor invariants across the input space. The agent needs both axes, and most suites only carry one. - The Test Pyramid Was an Economic Argument
The test pyramid was not a quality law. It was a cost structure: unit tests were cheap, integration tests were expensive, so you wrote many of the first and few of the second. Agents collapsed the cost of writing tests at every level, and the cheapest test that still tells the truth is the one that pins a seam the agent cannot fake. - Agents Amplify Whatever Vocabulary They Find
Agents do not bring vocabulary to your codebase. They mirror what's already there. Strong vocabulary gets agent contributions that read like the team. Weak vocabulary gets faster mediocrity. The work you did for craft reasons just became leverage.