Backlog, tests, logo compression, security audit, App Store icon

- BACKLOG.md: P0-P3 roadmap incl. govuk API switch (blocked on credentials)
- APPSTORE.md: review-gate analysis (2 blockers: LAN data source, Always-location notes)
- SECURITY.md: audit - no secrets, no ATS holes, one MEDIUM (relay IP in binary, planned fix)
- FuelBoardTests/: SPM package, 28 tests on real Shared/ sources (sanitizer, price guard, RAG, sort, distance, brands)
- Logos: proper 2x/3x pairs, palette-quantized, 75% smaller (315KB -> 79KB)
- App icon: 1024px fuelpump on gradient (was missing entirely -> instant rejection)
- decodeStations(from:) exposed for testability; relay fetch reuses it
This commit is contained in:
FuelBoard Contributor
2026-08-11 23:14:28 +01:00
parent 05f3ef0a70
commit 2ed18d81a3
57 changed files with 608 additions and 25 deletions
+20 -14
View File
@@ -22,6 +22,25 @@ enum FuelPriceProvider {
/// Default: the keyless relay (full-UK Fuel Finder data). Falls back to
/// the England-wide sample set when the relay is unreachable.
static let active: FuelPriceProviding = RelayFuelProvider()
/// Decodes a relay payload into stations, applying the defensive price
/// band. Internal so the unit-test target can exercise the guard.
static func decodeStations(from data: Data) throws -> [FuelStation] {
let payload = try JSONDecoder().decode(RelayResponse.self, from: data)
return payload.stations.map { relay in
FuelStation(
id: relay.id ?? relay.name ?? UUID().uuidString,
name: (relay.name ?? "Unknown").sanitizedStationTitle,
brand: relay.brand ?? "",
address: relay.address ?? "",
postcode: relay.postcode ?? "",
lat: relay.lat ?? 0,
lng: relay.lng ?? 0,
prices: relay.allPrices,
priceUpdated: nil
)
}
}
}
// MARK: - Relay provider (default)
@@ -54,20 +73,7 @@ struct RelayFuelProvider: FuelPriceProviding {
guard let http = response as? HTTPURLResponse, http.statusCode == 200 else {
throw FuelProviderError.relayUnavailable
}
let payload = try JSONDecoder().decode(RelayResponse.self, from: data)
return payload.stations.map { relay in
FuelStation(
id: relay.id ?? relay.name ?? UUID().uuidString,
name: (relay.name ?? "Unknown").sanitizedStationTitle,
brand: relay.brand ?? "",
address: relay.address ?? "",
postcode: relay.postcode ?? "",
lat: relay.lat ?? 0,
lng: relay.lng ?? 0,
prices: relay.allPrices,
priceUpdated: nil
)
}
return try FuelPriceProvider.decodeStations(from: data)
}
}