Search: FlightAware backbone, blob catalog, diagnostic infra
route-explorer's /api/token sits behind invisible Cloudflare Turnstile
that requires Apple's Private Access Token attestation. Third-party
iOS apps don't qualify for PAT issuance, and Linux Docker containers
can't pass it either (cross-OS fingerprint, even with patchright /
Camoufox). Migrates direct-flight search to FlightAware; multi-stop
and where-can-I-go remain via embedded SFSafariViewController.
- FlightAwareScheduleClient — scrapes route.rvt + trackpoll JSON for
real schedules without auth. T+0..2 day window. Tests against
captured HTML fixtures.
- BlobRouteClient — pulls the public Vercel blob route catalog
route-explorer's frontend reads (no auth, no Turnstile).
- DiagnosticLogger + LoggingURLSessionDelegate + DiagnosticsView —
device-shareable forensic trace. Boot header captures device, OS,
locale, UA; share-sheet export of session logs.
- TurnstileDebugView — live WKWebView gate inspector. Used to prove
the PAT-entitlement gap on a real device.
- RouteExplorerBrowserView — SFSafariViewController wrapper. Real
Safari clears Turnstile naturally; the in-app browser opens at
pre-filled search URLs. Surfaced from Search ("Open in
route-explorer") and Settings → Tools.
- RouteExplorerTokenStore + RouteExplorerSetupView — bookmarklet
capture flow (token round-tripped via flights://routeexplorer-token
URL scheme). Kept dormant for future use.
backend/ — Docker proxy attempts (Playwright, patchright, Camoufox).
All fail on Linux because Cloudflare auto-denies before the Turnstile
widget renders. Documented; kept as scaffolding for a future paid-
solver integration.
scripts/probe_flightaware.py — reference algorithm for the FA path.
scripts/probe_nodriver.py — local-Mac sanity check confirming the
gate clears with real macOS Chrome (proves the blocker is
fingerprint-level, not network-level).
This commit is contained in:
@@ -0,0 +1,84 @@
|
||||
import XCTest
|
||||
@testable import Flights
|
||||
|
||||
/// Guard test against a regression where the dead-code "Selftest" block in
|
||||
/// `RootView.swift` (a Task.detached that called
|
||||
/// `routeExplorer.searchSchedule` on app launch and printed results) gets
|
||||
/// re-introduced. That block touched the broken `RouteExplorerClient` and
|
||||
/// fired off a detached task at startup — exactly the shape we want to
|
||||
/// keep out of the launch path.
|
||||
///
|
||||
/// Strategy: locate `RootView.swift` on disk and assert none of the
|
||||
/// fingerprint substrings are present. We try a couple of paths because
|
||||
/// the working directory during `xcodebuild test` is not stable.
|
||||
final class SelftestRemovalTests: XCTestCase {
|
||||
|
||||
/// Fingerprints that uniquely identify the dead-code block.
|
||||
private static let forbiddenSubstrings: [String] = [
|
||||
"[Selftest]",
|
||||
"routeExplorer.searchSchedule",
|
||||
"Task.detached"
|
||||
]
|
||||
|
||||
func test_rootView_doesNotContainSelftestDeadCode() throws {
|
||||
guard let source = Self.loadRootViewSource() else {
|
||||
// We couldn't locate the file from the test bundle's vantage
|
||||
// point. Don't silently pass — surface it as a skip so the
|
||||
// dev knows to do a manual check.
|
||||
// manual check: open Flights/Views/RootView.swift and confirm
|
||||
// none of `[Selftest]`, `routeExplorer.searchSchedule`, or
|
||||
// `Task.detached` appear in it.
|
||||
throw XCTSkip("Could not locate RootView.swift from the test bundle; manual check required.")
|
||||
}
|
||||
|
||||
for needle in Self.forbiddenSubstrings {
|
||||
XCTAssertFalse(
|
||||
source.contains(needle),
|
||||
"RootView.swift still contains forbidden dead-code fingerprint: \(needle)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: - File location
|
||||
|
||||
/// Try several strategies to find `RootView.swift` on disk.
|
||||
/// Order: explicit env var → walking up from the test bundle → a known
|
||||
/// absolute project path → walking up from #file.
|
||||
private static func loadRootViewSource() -> String? {
|
||||
let fm = FileManager.default
|
||||
var candidates: [String] = []
|
||||
|
||||
// 1. Env override (useful for CI or weird scheme configs).
|
||||
if let envRoot = ProcessInfo.processInfo.environment["FLIGHTS_PROJECT_ROOT"] {
|
||||
candidates.append((envRoot as NSString).appendingPathComponent("Flights/Views/RootView.swift"))
|
||||
}
|
||||
|
||||
// 2. Walk up from the test bundle until we find a sibling `Flights` dir.
|
||||
let bundleURL = Bundle(for: SelftestRemovalTests.self).bundleURL
|
||||
var dir = bundleURL.deletingLastPathComponent()
|
||||
for _ in 0..<8 {
|
||||
let guess = dir.appendingPathComponent("Flights/Views/RootView.swift").path
|
||||
candidates.append(guess)
|
||||
dir = dir.deletingLastPathComponent()
|
||||
}
|
||||
|
||||
// 3. Known absolute path on this dev machine (best-effort fallback).
|
||||
candidates.append("/Users/m4mini/Desktop/code/Flights/Flights/Views/RootView.swift")
|
||||
|
||||
// 4. Walk up from this source file's location.
|
||||
let thisFile = URL(fileURLWithPath: #filePath)
|
||||
var srcDir = thisFile.deletingLastPathComponent()
|
||||
for _ in 0..<6 {
|
||||
let guess = srcDir.appendingPathComponent("Flights/Views/RootView.swift").path
|
||||
candidates.append(guess)
|
||||
srcDir = srcDir.deletingLastPathComponent()
|
||||
}
|
||||
|
||||
for path in candidates where fm.fileExists(atPath: path) {
|
||||
if let contents = try? String(contentsOfFile: path, encoding: .utf8) {
|
||||
return contents
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user