import XCTest @testable import FuelBoardShared /// Shared payload storage must survive an install whose provisioning profile /// has NO `com.apple.security.application-groups` entitlement (free/personal /// team and SideStore installs) — the exact situation this test runner is in. /// /// Regression guard: `appGroupFileURL` used to return nil without a group /// container, so every save silently failed and every load returned empty. That /// left `loadStations()` permanently empty (letting the freshness gate skip the /// fetch for ever, i.e. a blank station list) and re-downloaded the ~2.8 MB /// dump on every refresh. Storage now falls back to the app's own container. final class FuelStoreFileCacheTests: XCTestCase { func testSharedFileURLExistsWithoutAppGroupContainer() { XCTAssertNotNil( FuelStore.appGroupFileURL("fuelboard.test.probe.json"), "storage must resolve to a writable location even when the App Group container is unprovisioned" ) } func testSharedFileRoundTripsWithoutAppGroupContainer() throws { let name = "fuelboard.test.roundtrip.json" let payload = Data("hello-cache".utf8) XCTAssertTrue( FuelStore.saveAppGroupFile(payload, name: name), "save must succeed without an App Group container" ) XCTAssertEqual(FuelStore.loadAppGroupFile(name), payload) if let url = FuelStore.appGroupFileURL(name) { try? FileManager.default.removeItem(at: url) } } /// The dump cache is what stops a ~2.8 MB re-download on every refresh, so /// it must round-trip too — not just the station list. func testDumpCacheRoundTripsWithoutAppGroupContainer() { let day = "2026-01-01" let data = Data(repeating: 7, count: 4096) MirrorFuelProvider.saveDumpCache(day: day, data: data) let cache = MirrorFuelProvider.loadDumpCache() XCTAssertEqual(cache?.day, day, "cached dump day must survive a round-trip") XCTAssertEqual(cache?.data, data, "cached dump payload must survive a round-trip") if let url = FuelStore.appGroupFileURL(MirrorFuelProvider.liveDumpCacheFileName) { try? FileManager.default.removeItem(at: url) } } /// The road-distance cache is memoized in memory because re-reading it per /// station (it lives in the keychain) stalled the main thread past the iOS /// launch watchdog. A save must therefore invalidate the memo — a stale /// in-memory cache would show wrong distances after a route refresh. func testRoadDistanceMemoRefreshesAfterSave() { FuelStore.saveRoadDistances(sourceLat: 51.0, sourceLng: -1.0, entries: [:]) XCTAssertEqual(FuelStore.loadRoadDistances()?.sourceLat, 51.0) FuelStore.saveRoadDistances(sourceLat: 52.0, sourceLng: -2.0, entries: [:]) XCTAssertEqual( FuelStore.loadRoadDistances()?.sourceLat, 52.0, "load must reflect the newest save, not a stale memoized cache" ) } }