Files
ShadowsocksX-NG/ShadowsocksX-NG2/Tests/ProxyRuntime/ProxyRuntimeControllerTests+GlobalMode.swift
Qiu YuzhouandDeepSeek Harness e7116a5ebd test: merge duplicated cases into the stronger one
Every case here restates a rule another test already pins with the same or a
wider input class:

- the activation group case is byte-identical to its neighbour, minus an extra
  assertion; the rule snapshot version guard is re-driven with a second value
  of the same schemaVersion check; the global-mode deployment-failure case is
  a mode-literal copy of the strictly stronger direct-mode one; the clear-
  failure case duplicates the deferred-retry sibling
- two RuntimeDocument projection cases only re-map locals, already asserted as
  full array equality; the IPv6-LAN endpoint case repeats the direct-mode case
  (the derivation reads only ports)
- the three HTTP export cases varied only listenerMode while the derivation
  ignores it and one name contradicted its own expectation; one loop over all
  listener modes now states the real rule
- the 0700/0600 permission baseline is one AtomicFileWriter behavior and stays
  pinned through RuntimeFileStoreTests, which also covers self-heal and atomic
  replace

The command-boundary case keeps its mixed-scenario isolation check (four
commands, four independent adapter counters) and drops only its unfalsifiable
queriedTargetIDs claim.

Co-Authored-By: DeepSeek Harness <noreply@deepseek.com>
2026-10-21 01:46:29 +08:00

100 lines
4.6 KiB
Swift

import Combine
import XCTest
@testable import ShadowsocksX_NG2
/// 全局模式以 ACL 实现:proxy_all + 固定本地绕过;无活动服务器
/// 时 agent 仍监听、系统代理保持待应用;ACL 变化完整重启,失败保留最新意图。
final class ProxyRuntimeGlobalModeTests: ProxyRuntimeControllerTests {
override func makeDefaultRuleSnapshots() -> BuiltinRuleSnapshots {
BuiltinRuleSnapshots(loader: ProxyRuntimeFixture.controlFlowRuleSnapshot)
}
func testGlobalModeDeploysProxyAllACLAndProjectsSOCKSProxyWithFixedExceptions() async throws {
let seeded = try makeSeededCatalog()
let listeningStore = InMemoryProxyListeningStore()
let controller = makeController(
probe: ProxyRuntimeFixture.FakeProbe.reachable(),
listeningStore: listeningStore,
settings: ProxyRuntimeSettings(
listen: ActivationFixture.listen, agentEnabled: true, systemProxyEnabled: true))
try await controller.activate(seeded.server)
XCTAssertEqual(controller.systemProxyState, .applied)
let unregisterCount = agent.unregisterCount
let runtimeStore = RuntimeFileStore(fileURL: runtime.contract)
await controller.setProxyMode(.global)
XCTAssertEqual(controller.proxyMode, .global)
XCTAssertEqual(controller.settings.preferredMode, .global)
XCTAssertEqual(controller.controlPreferences.preferredMode, .global)
XCTAssertEqual(controller.state, .running)
XCTAssertEqual(
agent.unregisterCount, unregisterCount + 1, "ACL 变化触发完整 agent 重启")
let document = try XCTUnwrap(runtimeStore.loadDocument())
XCTAssertEqual(document.aclRuntime?.summary, "global")
let expectedACL = ProxyACLDocument.global(at: runtimeStore.aclFileURL)
XCTAssertTrue(expectedACL.content.hasPrefix("[proxy_all]\n[bypass_list]\n"))
XCTAssertEqual(
try Data(contentsOf: runtimeStore.aclFileURL),
Data(expectedACL.content.utf8),
"链接解析后的内容是 global 变体")
XCTAssertEqual(
systemProxy.applied.last?.socks,
.init(host: "127.0.0.1", port: ActivationFixture.listen.socksPort))
XCTAssertEqual(
systemProxy.applied.last?.http,
.init(host: "127.0.0.1", port: ActivationFixture.listen.httpPort))
XCTAssertTrue(
Set(FixedLocalProxyRanges.systemProxyExceptions).isSubset(
of: Set(systemProxy.applied.last?.exceptions ?? [])),
"系统例外包含固定本地范围")
XCTAssertEqual(controller.systemProxyState, .applied)
}
func testGlobalModeWithoutServerKeepsListeningAndPendingUntilTargetRecovers() async throws {
// 目录先播种、控制器再创建:bootstrap 快照读取器持有已提交目录,但尚无
// 活动目标——正是「全局模式 + 无可用出口」的起点。
let seeded = try makeSeededCatalog()
let listeningStore = InMemoryProxyListeningStore()
let controller = makeController(
probe: ProxyRuntimeFixture.FakeProbe.reachable(),
listeningStore: listeningStore,
settings: ProxyRuntimeSettings(
listen: ActivationFixture.listen, agentEnabled: true, systemProxyEnabled: true))
await controller.resyncOnLaunch()
XCTAssertEqual(controller.state, .running, "无活动目标时 agent 仍监听")
XCTAssertNil(controller.activeTargetID)
let unregisterCount = agent.unregisterCount
await controller.setProxyMode(.global)
XCTAssertEqual(controller.proxyMode, .global)
XCTAssertEqual(controller.state, .running, "全局模式空服务器列表仍提供本地监听")
XCTAssertEqual(
agent.unregisterCount, unregisterCount + 1, "ACL 变化触发完整 agent 重启")
let runtimeStore = RuntimeFileStore(fileURL: runtime.contract)
let document = try XCTUnwrap(runtimeStore.loadDocument())
XCTAssertTrue(document.servers.isEmpty, "无活动目标时服务器列表为空")
XCTAssertEqual(document.aclRuntime?.summary, "global")
XCTAssertEqual(
controller.systemProxyState, .paused,
"无可用代理出口时系统代理意图保持待应用")
XCTAssertTrue(systemProxy.applied.isEmpty, "不得把系统代理指向无出口入口")
// 服务器有效且端点健康后自动应用已开启的系统代理意图。
try await controller.activate(seeded.server)
XCTAssertEqual(controller.state, .running)
XCTAssertEqual(
controller.systemProxyState, .applied,
"服务器有效且健康后自动收敛待应用意图")
XCTAssertEqual(
systemProxy.applied.last?.socks,
.init(host: "127.0.0.1", port: ActivationFixture.listen.socksPort))
XCTAssertEqual(
systemProxy.applied.last?.http,
.init(host: "127.0.0.1", port: ActivationFixture.listen.httpPort))
}
}