Tied cheapest stations become notification action buttons

When several stations share the cheapest price within the radius (within
0.01p), the alert registers a dynamic UNNotificationCategory with up to
4 action buttons (brand + distance, nearest first) and taps open
directions to the chosen station. Shared FuelStore.tiedStations helper
used by both the live geofence path and the Settings real-data test;
35 tests pass.
This commit is contained in:
FuelBoard Contributor
2026-08-12 10:48:08 +01:00
parent cbe01f4219
commit 1f50d7dcf4
3 changed files with 173 additions and 13 deletions
@@ -146,6 +146,33 @@ final class SortBaselineTests: XCTestCase {
let sorted = [a, b, c].sorted { $0.prices[.e10]! < $1.prices[.e10]! }
XCTAssertEqual(sorted.map(\.id), ["b", "c", "a"])
}
// Ties: stations at the same cheapest price are found (within 0.01p) and
// sorted nearest-first from the reference location.
func testTiedStations() {
func station(_ id: String, _ lat: Double, _ lng: Double, _ price: Double) -> FuelStation {
FuelStation(id: id, name: id, brand: "X", address: "", postcode: "",
lat: lat, lng: lng, prices: [.e10: price], priceUpdated: nil)
}
let far = station("far", 53.72, -1.82, 137.0)
let near = station("near", 53.71, -1.81, 137.0)
let other = station("other", 53.70, -1.80, 141.0)
let tied = FuelStore.tiedStations(in: [far, other, near], fuel: .e10, price: 137.0,
fromLat: 53.7, lng: -1.8)
XCTAssertEqual(tied.map(\.id), ["near", "far"], "tied stations sorted nearest-first")
}
// A price 0.005p away still counts as a tie; 0.05p away does not.
func testTiedStationsTolerance() {
func station(_ id: String, _ price: Double) -> FuelStation {
FuelStation(id: id, name: id, brand: "X", address: "", postcode: "",
lat: 53.7, lng: -1.8, prices: [.e10: price], priceUpdated: nil)
}
let a = station("a", 137.005)
let b = station("b", 137.05)
XCTAssertEqual(FuelStore.tiedStations(in: [a, b], fuel: .e10, price: 137.0,
fromLat: 53.7, lng: -1.8).map(\.id), ["a"])
}
}
final class DistanceTests: XCTestCase {