Best Value: candidate-priced fills, key copy, detail total

This commit is contained in:
FuelBoard Contributor
2026-10-14 10:43:12 +01:00
parent 2ed9cbf4e8
commit 3c424ab397
9 changed files with 218 additions and 80 deletions
@@ -284,9 +284,9 @@ final class FuelSavingsEstimateTests: XCTestCase {
/// (171.9p, 0.4 mi), candidate Shell Whetley Hill (167.9p, 3.8 mi).
///
/// Whetley Hill is 4p/litre cheaper, yet it is WORSE OFF — and that is
/// correct, not a calculation bug: £60 buys 34.90 L at 171.9p, so the
/// cheaper pump saves 34.90 × 4p = £1.40, while the extra 6.8 return miles
/// burn 6.8/30 × 4.54609 = 1.03 L at 167.9p = £1.73. Net ≈ −£0.33.
/// correct, not a calculation bug: £60 buys 35.74 L at 167.9p, so the
/// cheaper pump saves 35.74 × 4p = £1.43, while the extra 6.8 return miles
/// burn 6.8/30 × 4.54609 = 1.03 L at 167.9p = £1.73. Net ≈ −£0.30.
func testCheaperStationFurtherAwayIsWorseOffAfterTravel() {
let baseline = station("rontec-shelf", price: 171.9)
let candidate = station("shell-whetley-hill", price: 167.9)
@@ -304,16 +304,16 @@ final class FuelSavingsEstimateTests: XCTestCase {
guard let result = availability?.result else {
return XCTFail("expected an estimate, got \(String(describing: availability))")
}
XCTAssertEqual(result.fillLitres, 34.904, accuracy: 0.005,
"the £60 budget buys litres at the BASELINE pump price")
XCTAssertEqual(result.grossSavingGBP, 1.396, accuracy: 0.005)
XCTAssertEqual(result.fillLitres, 35.736, accuracy: 0.005,
"the £60 budget buys litres at the candidate pump price")
XCTAssertEqual(result.grossSavingGBP, 1.429, accuracy: 0.005)
XCTAssertEqual(result.extraDistanceMiles, 6.8, accuracy: 0.001,
"only the return-trip DELTA counts — the shared journey is never counted twice")
XCTAssertEqual(result.extraDrivingCostGBP, 1.730, accuracy: 0.005)
XCTAssertEqual(result.netSavingGBP, -0.334, accuracy: 0.005)
XCTAssertEqual(result.netSavingGBP, -0.301, accuracy: 0.005)
XCTAssertEqual(result.status, .notWorthTheDrive)
XCTAssertEqual(result.journeyConfidence, .estimated)
XCTAssertEqual(result.headline(compact: true), "Approx. £0.33 worse off after travel")
XCTAssertEqual(result.headline(compact: true), "Approx. Costs £0.30 more")
}
func testMeasuredRoadRoutesDropTheEstimateMarker() {
@@ -332,8 +332,8 @@ final class FuelSavingsEstimateTests: XCTestCase {
XCTAssertEqual(availability?.result?.distanceBasis, .roadRoute)
XCTAssertEqual(availability?.result?.journeyConfidence, .confirmed)
XCTAssertEqual(availability?.result?.headline(compact: true), "£0.33 worse off after travel")
XCTAssertEqual(availability?.result?.headline(), "£0.33 worse off after travel")
XCTAssertEqual(availability?.result?.headline(compact: true), "Costs £0.30 more")
XCTAssertEqual(availability?.result?.headline(), "Costs £0.30 more")
}
func testBaselineRowIsLabelledClosestStation() {
@@ -375,10 +375,10 @@ final class FuelSavingsEstimateTests: XCTestCase {
return XCTFail("expected an estimate, got \(String(describing: availability))")
}
XCTAssertEqual(result.status, .worthTheDrive)
// £60 buys 37.5 L at 160p; 10p/litre cheaper saves £3.75; 0.4 extra
// return miles cost ~£0.09. Net ≈ £3.66.
XCTAssertEqual(result.netSavingGBP, 3.659, accuracy: 0.005)
XCTAssertEqual(result.headline(compact: true), "Approx. £3.66 better off after travel")
// £60 buys 40 L at 150p; 10p/litre cheaper saves £4.00; 0.4 extra
// return miles cost ~£0.09. Net ≈ £3.91.
XCTAssertEqual(result.netSavingGBP, 3.909, accuracy: 0.005)
XCTAssertEqual(result.headline(compact: true), "Approx. Save £3.91")
}
func testNeutralResultHeadlineIsBreakEven() {
@@ -413,7 +413,7 @@ final class FuelSavingsEstimateTests: XCTestCase {
netSavingGBP: 0.52,
status: .worthTheDrive,
distanceBasis: .roadRoute)
XCTAssertEqual(saving.headline(), "£0.52 better off after travel")
XCTAssertEqual(saving.headline(), "Save £0.52")
let loss = FuelSavingsResult(fillLitres: 34.9,
grossSavingGBP: 1.40,
@@ -422,7 +422,7 @@ final class FuelSavingsEstimateTests: XCTestCase {
netSavingGBP: -0.31,
status: .notWorthTheDrive,
distanceBasis: .roadRoute)
XCTAssertEqual(loss.headline(), "£0.31 worse off after travel")
XCTAssertEqual(loss.headline(), "Costs £0.31 more")
}
/// The sort must order by NET saving, not pump price: a pricier station can
@@ -497,7 +497,7 @@ final class FuelSavingsEstimateTests: XCTestCase {
status: .worthTheDrive,
distanceBasis: .straightLine
)
XCTAssertEqual(saving.capsuleHeadline(), "Approx. £1.00 better off after travel")
XCTAssertEqual(saving.capsuleHeadline(), "Approx. Save £1.00")
let worse = FuelSavingsResult(
fillLitres: 30,
@@ -507,7 +507,7 @@ final class FuelSavingsEstimateTests: XCTestCase {
netSavingGBP: -0.31,
status: .notWorthTheDrive
)
XCTAssertEqual(worse.capsuleHeadline(), "£0.31 worse off after travel")
XCTAssertEqual(worse.capsuleHeadline(), "Costs £0.31 more")
}
// MARK: - Estimate wording (Approx. labels)
@@ -524,7 +524,7 @@ final class FuelSavingsEstimateTests: XCTestCase {
status: .worthTheDrive,
distanceBasis: .straightLine
)
XCTAssertEqual(saving.headline(), "Approx. £1.25 better off after travel")
XCTAssertEqual(saving.headline(), "Approx. Save £1.25")
let loss = FuelSavingsResult(
fillLitres: 30,
@@ -535,7 +535,7 @@ final class FuelSavingsEstimateTests: XCTestCase {
status: .notWorthTheDrive,
distanceBasis: .straightLine
)
XCTAssertEqual(loss.headline(), "Approx. £0.77 worse off after travel")
XCTAssertEqual(loss.headline(), "Approx. Costs £0.77 more")
}
/// A confirmed road-route comparison must never carry the marker — in either
@@ -551,7 +551,7 @@ final class FuelSavingsEstimateTests: XCTestCase {
distanceBasis: .roadRoute,
journeyConfidence: .confirmed
)
XCTAssertEqual(saving.headline(), "£1.25 better off after travel")
XCTAssertEqual(saving.headline(), "Save £1.25")
let loss = FuelSavingsResult(
fillLitres: 30,
@@ -563,7 +563,7 @@ final class FuelSavingsEstimateTests: XCTestCase {
distanceBasis: .roadRoute,
journeyConfidence: .confirmed
)
XCTAssertEqual(loss.headline(), "£0.77 worse off after travel")
XCTAssertEqual(loss.headline(), "Costs £0.77 more")
for headline in [saving.headline(), loss.headline(), saving.capsuleHeadline()] {
XCTAssertFalse(headline.contains("Approx"), "\"\(headline)\" must not claim to be an estimate")
@@ -597,7 +597,7 @@ final class FuelSavingsEstimateTests: XCTestCase {
status: .notWorthTheDrive,
distanceBasis: .straightLine
)
XCTAssertEqual(estimated.spokenHeadline(), "Approximately £0.77 worse off after travel")
XCTAssertEqual(estimated.spokenHeadline(), "Approximately Costs £0.77 more")
XCTAssertEqual(
FuelSavingsFormat.extraTravelSpokenLabel(extraDistanceMiles: 6.4, unit: .miles, confidence: .estimated),