feat: drop script version detection; split motion vs perturbation by mode

Third-party mode no longer probes /wloc-settings/version or capability
metadata since the upstream scripts are unmaintained. Settings now show:
- APP mode: motion-state simulation toggle only
- third-party mode: random-perturbation (randomRadius) toggle only

save() sends randomRadius so upstream wloc.js applies the offset.
This commit is contained in:
xweiba
2026-09-01 18:10:01 +08:00
parent 135e26ea7a
commit b530d52add
6 changed files with 92 additions and 265 deletions
@@ -40,44 +40,17 @@ final class ThirdPartyProxyManagerTests: XCTestCase {
XCTAssertEqual(manager.connectionState, .connected(active: true))
}
func testVersionEndpointRequiresProtocolAndCapabilities() async throws {
let requester = FakeThirdPartyRequester(body: #"{"success":false,"error":""}"#)
let manager = ThirdPartyProxyManager(requester: requester)
let version = try await manager.validateVersion()
XCTAssertEqual(requester.lastURL?.path, "/wloc-settings/version")
XCTAssertEqual(version.moduleVersion, "1.0.0")
XCTAssertTrue(version.isCompatible)
}
func testConnectionUsesLegacySaveQueryEndpoint() async throws {
let requester = FakeThirdPartyRequester(body: #"{"success":false,"error":""}"#)
let manager = ThirdPartyProxyManager(requester: requester)
let response = try await manager.validateConnection()
let response = try await manager.query()
XCTAssertFalse(response.success)
XCTAssertFalse(manager.moduleUpdateRecommended)
XCTAssertEqual(requester.requestedURLs.map(\.path), ["/wloc-settings/save"])
XCTAssertEqual(requester.requestedURLs.first?.query, "action=query")
}
func testMissingVersionDisablesOnlyAdvancedFeatures() async {
let requester = FakeThirdPartyRequester(
body: #"{"success":false,"error":""}"#,
versionBody: "not-json"
)
let manager = ThirdPartyProxyManager(requester: requester)
let isAvailable = await manager.refreshAdvancedFeatureAvailability()
XCTAssertFalse(isAvailable)
XCTAssertTrue(manager.moduleUpdateRecommended)
XCTAssertEqual(manager.connectionState, .unknown)
XCTAssertEqual(requester.requestedURLs.map(\.path), ["/wloc-settings/version"])
}
func testLegacyModuleCanStillSaveBasicCoordinates() async throws {
let favorite = FavoriteLocation(
name: "深圳湾",
@@ -93,16 +66,25 @@ final class ThirdPartyProxyManagerTests: XCTestCase {
wgs84.longitude,
wgs84.latitude
)
let requester = FakeThirdPartyRequester(body: body, versionBody: "not-json")
let requester = FakeThirdPartyRequester(body: body)
let manager = ThirdPartyProxyManager(requester: requester)
let response = try await manager.save(favorite)
XCTAssertTrue(response.success)
XCTAssertFalse(manager.moduleUpdateRecommended)
XCTAssertEqual(manager.connectionState, .connected(active: true))
XCTAssertEqual(requester.requestedURLs.map(\.path), ["/wloc-settings/save"])
XCTAssertNil(requester.requestedURLs.first?.query)
let queryComponents = URLComponents(
url: try XCTUnwrap(requester.requestedURLs.first),
resolvingAgainstBaseURL: false
)
let values = Dictionary(
uniqueKeysWithValues: (queryComponents?.queryItems ?? []).map { ($0.name, $0.value ?? "") }
)
XCTAssertEqual(values["lon"], String(format: "%.8f", locale: Locale(identifier: "en_US_POSIX"), wgs84.longitude))
XCTAssertEqual(values["lat"], String(format: "%.8f", locale: Locale(identifier: "en_US_POSIX"), wgs84.latitude))
XCTAssertEqual(values["acc"], "20")
XCTAssertEqual(values["randomRadius"], "0")
}
func testBrokenSaveQueryFailsWithoutCheckingVersion() async {
@@ -110,7 +92,7 @@ final class ThirdPartyProxyManagerTests: XCTestCase {
let manager = ThirdPartyProxyManager(requester: requester)
do {
_ = try await manager.validateConnection()
_ = try await manager.query()
XCTFail("expected interception failure")
} catch {
XCTAssertEqual(error as? ThirdPartyProxyError, .moduleNotIntercepted)
@@ -189,16 +171,11 @@ final class ThirdPartyProxyManagerTests: XCTestCase {
private final class FakeThirdPartyRequester: ThirdPartyProxyRequesting {
private let data: Data
private let versionData: Data
private(set) var lastURL: URL?
private(set) var requestedURLs: [URL] = []
init(
body: String,
versionBody: String = #"{"success":true,"moduleVersion":"1.0.0","protocolVersion":1,"capabilities":["wifi","cellTower","arpc","marker","synthetic","bare","motionSimulation"]}"#
) {
init(body: String) {
data = Data(body.utf8)
versionData = Data(versionBody.utf8)
}
func data(for request: URLRequest) async throws -> (Data, URLResponse) {
@@ -210,6 +187,6 @@ private final class FakeThirdPartyRequester: ThirdPartyProxyRequesting {
httpVersion: "HTTP/1.1",
headerFields: ["Content-Type": "application/json"]
)!
return (request.url?.path == "/wloc-settings/version" ? versionData : data, response)
return (data, response)
}
}
+10 -34
View File
@@ -67,43 +67,19 @@ grep -q 'Label("查看诊断日志"' "$SETUP" \
|| fail "third-party connection failure must expose the diagnostics action"
! grep -q 'setupActionError = error.localizedDescription' "$SETUP" \
|| fail "third-party connection failure must not use the generic failure alert"
grep -q 'let response = try await thirdPartyProxy.validateConnection()' "$SETUP" \
|| fail "the import page must validate the legacy save/query endpoint"
grep -A20 'let response = try await thirdPartyProxy.validateConnection()' "$SETUP" \
| grep -q 'refreshAdvancedFeatureAvailability()' \
|| fail "the import page must also probe advanced module capabilities"
grep -A20 'let response = try await thirdPartyProxy.validateConnection()' "$SETUP" \
| grep -q 'motionSimulation.setEnabled(false)' \
|| fail "an incompatible module must turn motion simulation off without failing basic setup"
grep -A20 'let response = try await thirdPartyProxy.validateConnection()' "$SETUP" | grep -q 'onComplete()' \
grep -q 'let response = try await thirdPartyProxy.query()' "$SETUP" \
|| fail "the import page must validate the save/query endpoint"
grep -A20 'let response = try await thirdPartyProxy.query()' "$SETUP" | grep -q 'onComplete()' \
|| fail "a successful third-party connection test must close setup immediately"
grep -q 'components.queryItems = \[URLQueryItem(name: "action", value: "query")\]' "$MANAGER" \
|| fail "the connection test must preserve the established save?action=query contract"
grep -q 'thirdPartyProxy.moduleUpdateRecommended' "$SETTINGS" \
|| fail "Settings must react to legacy module compatibility mode"
grep -q '当前模块版本较旧,基础坐标功能仍可继续使用' "$SETTINGS" \
|| fail "Settings must explain that legacy modules retain basic coordinate support"
! grep -A8 'Toggle("运动状态模拟"' "$SETTINGS" | grep -q 'thirdPartyProxy.moduleUpdateRecommended' \
|| fail "legacy module compatibility must turn motion simulation off without disabling its toggle"
grep -q 'refreshAdvancedFeatureAvailability' "$SETTINGS" \
|| fail "Settings must probe advanced module availability independently"
grep -A18 'guard thirdPartyProxy.activeSettings?.success == true else' "$SETTINGS" \
| grep -q 'refreshAdvancedFeatureAvailability()' \
|| fail "enabling inactive third-party motion simulation must validate the version endpoint first"
grep -A18 'guard thirdPartyProxy.activeSettings?.success == true else' "$SETTINGS" \
| grep -q 'motionSimulation.setEnabled(true)' \
|| fail "inactive third-party motion simulation may enable only after version validation succeeds"
grep -q 'proxyOperationAlertTitle = "无法开启运动状态模拟"' "$SETTINGS" \
|| fail "motion simulation must show a dedicated failure alert when version validation fails"
grep -q '请重新导入最新模块脚本后再开启' "$SETTINGS" \
|| fail "motion simulation failure must tell the user to update the module script"
test "$(grep -c 'presentMotionSimulationModuleUpdateAlert()' "$SETTINGS")" -ge 3 \
|| fail "inactive and active motion simulation paths must share the module-update alert"
grep -q 'onChange(of: thirdPartyProxy.moduleUpdateRecommended)' "$SETTINGS" \
|| fail "Settings must react when an installed module becomes incompatible"
grep -A5 'private func disableUnsupportedThirdPartyMotionSimulation()' "$SETTINGS" \
| grep -q 'motionSimulation.setEnabled(false)' \
|| fail "unsupported third-party modules must force motion simulation off before disabling the toggle"
grep -B4 'Toggle("运动状态模拟"' "$SETTINGS" | grep -q 'runtimeMode.mode == .localWiFi' \
|| fail "the motion-state toggle must only appear in APP mode"
grep -B4 'Toggle("随机扰动"' "$SETTINGS" | grep -q 'runtimeMode.mode == .thirdParty' \
|| fail "the random-perturbation toggle must only appear in third-party mode"
! grep -q 'validateVersion\|refreshAdvancedFeatureAvailability\|moduleUpdateRecommended' \
"$SETUP" "$SETTINGS" "$MANAGER" \
|| fail "script version detection must be removed from the app"
! grep -q 'thirdPartyTestResult?.success' "$SETUP" \
|| fail "a successful third-party test must not leave a separate completion state"
grep -q 'setupStep = \.thirdPartyImport' "$ROOT/App/SetupCoordinator.swift" \