3 · Reliability and project · 45 MIN
Project · A tested expense report
Pure calculations make success and failure easy to verify.
This report groups whole-unit amounts in a Dictionary and uses checked addition. An invalid record or overflow throws before the method returns its local dictionary, so callers receive either a complete result or a failure. The example’s small Check helper runs regardless of build configuration. Convert these checks to your preferred test framework when the project grows. Persisting balances would require a transaction; this in-memory function deliberately does not imply cross-request or cross-server durability.
Keep the dictionary local until every row has passed validation.
Read the example
using System;
using System.Collections.Generic;
static Dictionary<string,int> Report((string Category,int Amount)[] rows) {
var totals = new Dictionary<string,int>();
foreach (var row in rows) {
if (string.IsNullOrWhiteSpace(row.Category) || row.Amount < 0) throw new ArgumentException("Invalid expense");
totals.TryGetValue(row.Category, out var previous);
totals[row.Category] = checked(previous + row.Amount);
}
return totals;
}
static void Check(bool ok) { if (!ok) throw new Exception("Check failed"); }
Check(Report(Array.Empty<(string,int)>()).Count == 0);
Check(Report(new[]{("food",5),("food",7)})["food"] == 12);
Console.WriteLine("report checks passed");Check the expected output
report checks passed
Your challenge
Build a test project covering grouping, invalid records and overflow; add stable sorted report formatting without changing calculation order.
Solution cost: Expected O(n) grouping with O(k) category storage. time · Account for collection storage separately from the returned result. space
Common trap
Returning partial results after a caught exception silently changes an all-or-nothing contract.
Study the project implementation
using System;
using System.Collections.Generic;
static Dictionary<string,int> Report((string Category,int Amount)[] rows) {
var totals = new Dictionary<string,int>();
foreach (var row in rows) {
if (string.IsNullOrWhiteSpace(row.Category) || row.Amount < 0) throw new ArgumentException("Invalid expense");
totals.TryGetValue(row.Category, out var previous);
totals[row.Category] = checked(previous + row.Amount);
}
return totals;
}
static void Check(bool ok) { if (!ok) throw new Exception("Check failed"); }
Check(Report(Array.Empty<(string,int)>()).Count == 0);
Check(Report(new[]{("food",5),("food",7)})["food"] == 12);
Console.WriteLine("report checks passed");Further reading: Official documentation