From e7116a5ebd34462abfd79ef4add254229ebecfef Mon Sep 17 00:00:00 2001 From: Qiu Yuzhou Date: Wed, 21 Oct 2026 01:46:29 +0800 Subject: [PATCH] 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 --- .../ActivationStateFileStoreTests.swift | 11 ------ .../ActivationStateMachineTests.swift | 14 -------- .../Tests/Catalog/CatalogFileStoreTests.swift | 14 +------- ...WorkflowIntegrationTests+SystemProxy.swift | 35 ++++++------------- .../ProxyControlWorkflowTests.swift | 8 ++--- .../ProxyModeAvailabilityTests.swift | 10 ------ ...eControllerTests+AgentAndSystemProxy.swift | 14 -------- ...oxyRuntimeControllerTests+GlobalMode.swift | 33 ----------------- .../Rules/GeneratedRuleSnapshotTests.swift | 15 +------- .../Tests/Runtime/RuntimeDocumentTests.swift | 14 -------- 10 files changed, 16 insertions(+), 152 deletions(-) diff --git a/ShadowsocksX-NG2/Tests/Activation/ActivationStateFileStoreTests.swift b/ShadowsocksX-NG2/Tests/Activation/ActivationStateFileStoreTests.swift index 31a72db3..c10b8b83 100644 --- a/ShadowsocksX-NG2/Tests/Activation/ActivationStateFileStoreTests.swift +++ b/ShadowsocksX-NG2/Tests/Activation/ActivationStateFileStoreTests.swift @@ -60,17 +60,6 @@ final class ActivationStateFileStoreTests: XCTestCase { XCTAssertNil(try store.loadActiveTargetID(), "清除即整文件替换,不再残留旧目标") } - // MARK: 权限基线 - - func testSaveCreatesDirectory0700AndFile0600() throws { - try store.save(activeTargetID: NodeID(rawValue: "s1")) - - let fileAttributes = try FileManager.default.attributesOfItem(atPath: store.fileURL.path) - XCTAssertEqual(fileAttributes[.posixPermissions] as? Int, 0o600, "文件权限 0600") - let dirAttributes = try FileManager.default.attributesOfItem(atPath: workDir.path) - XCTAssertEqual(dirAttributes[.posixPermissions] as? Int, 0o700, "目录权限 0700") - } - func testDefaultFileURLMatchesRuntimeDirectoryContract() { XCTAssertTrue( ActivationStateFileStore.defaultFileURL().path.hasSuffix( diff --git a/ShadowsocksX-NG2/Tests/Activation/ActivationStateMachineTests.swift b/ShadowsocksX-NG2/Tests/Activation/ActivationStateMachineTests.swift index 3061ed93..01ba7809 100644 --- a/ShadowsocksX-NG2/Tests/Activation/ActivationStateMachineTests.swift +++ b/ShadowsocksX-NG2/Tests/Activation/ActivationStateMachineTests.swift @@ -99,20 +99,6 @@ final class ActivationStateMachineTests: XCTestCase { XCTAssertNil(machine.activeTargetID) } - func testActivateGroupWhoseLeavesAreAllInvalidIsRejected() throws { - var catalog = ConfigurationCatalog() - let group = try catalog.addGroup("组") - let leaf = try ActivationFixture.addPlainServer( - "a", to: group, in: &catalog) - var fields = try ActivationFixture.serverFields(of: leaf, in: catalog) - fields.encryptionMethod = "future-cipher" - try catalog.updateServer(leaf, with: fields) - - ActivationFixture.assertThrows(.targetExpandsToNothing(group)) { - try activate(group, in: catalog) - } - } - func testActivateServerWithUnprovidedPluginIsRejectedNamingProgram() throws { var catalog = ConfigurationCatalog() let leaf = try ActivationFixture.addPlainServer("a", in: &catalog) diff --git a/ShadowsocksX-NG2/Tests/Catalog/CatalogFileStoreTests.swift b/ShadowsocksX-NG2/Tests/Catalog/CatalogFileStoreTests.swift index bc4036b9..dbc3b833 100644 --- a/ShadowsocksX-NG2/Tests/Catalog/CatalogFileStoreTests.swift +++ b/ShadowsocksX-NG2/Tests/Catalog/CatalogFileStoreTests.swift @@ -51,19 +51,7 @@ final class CatalogFileStoreTests: XCTestCase { XCTAssertFalse(try store.load().catalog.contains(first), "旧内容被整体替换而非追加") } - // MARK: 权限与敏感信息 - - func testSaveCreatesDirectory0700AndFile0600() throws { - var catalog = ConfigurationCatalog() - _ = try catalog.addTestServer("leaf") - - try store.save(CatalogDocument(catalog: catalog)) - - let fileAttributes = try FileManager.default.attributesOfItem(atPath: store.fileURL.path) - XCTAssertEqual(fileAttributes[.posixPermissions] as? Int, 0o600, "文件权限 0600") - let dirAttributes = try FileManager.default.attributesOfItem(atPath: workDir.path) - XCTAssertEqual(dirAttributes[.posixPermissions] as? Int, 0o700, "目录权限 0700") - } + // MARK: 敏感信息 func testPersistedFileContainsNoPlaintextSecrets() throws { var catalog = ConfigurationCatalog() diff --git a/ShadowsocksX-NG2/Tests/ProxyControl/ProxyControlWorkflowIntegrationTests+SystemProxy.swift b/ShadowsocksX-NG2/Tests/ProxyControl/ProxyControlWorkflowIntegrationTests+SystemProxy.swift index 70acc820..84882152 100644 --- a/ShadowsocksX-NG2/Tests/ProxyControl/ProxyControlWorkflowIntegrationTests+SystemProxy.swift +++ b/ShadowsocksX-NG2/Tests/ProxyControl/ProxyControlWorkflowIntegrationTests+SystemProxy.swift @@ -124,31 +124,16 @@ extension ProxyControlWorkflowIntegrationTests { "export http_proxy=http://127.0.0.1:11087;export https_proxy=http://127.0.0.1:11087;") } - func testHTTPExportCapabilityUsesLoopbackAddressForAllIPv4Interfaces() { - var listen = ProxyListeningConfiguration() - listen.listenerMode = .allIPv4Interfaces - listen.httpPort = 8080 - XCTAssertEqual( - HTTPExportCapability(listen: listen).copyableLine, - "export http_proxy=http://127.0.0.1:8080;export https_proxy=http://127.0.0.1:8080;") - } + /// 导出行恒指向本地监听回环 127.0.0.1,与 listenerMode 无关(ADR-0034)。 + func testHTTPExportCapabilityAlwaysUsesIPv4LoopbackRegardlessOfListenerMode() { + for mode in ListenerMode.allCases { + var listen = ProxyListeningConfiguration(listenerMode: mode) + listen.httpPort = 8080 - func testHTTPExportCapabilityUsesIPv4LoopbackForDualStackMode() { - var listen = ProxyListeningConfiguration() - listen.listenerMode = .allIPv4AndIPv6Interfaces - listen.httpPort = 8080 - - XCTAssertEqual( - HTTPExportCapability(listen: listen).copyableLine, - "export http_proxy=http://127.0.0.1:8080;export https_proxy=http://127.0.0.1:8080;") - } - - func testHTTPExportCapabilityBracketsIPv6LoopbackOnlyForIPv6OnlyMode() { - var listen = ProxyListeningConfiguration() - listen.listenerMode = .allIPv6Interfaces - listen.httpPort = 8080 - XCTAssertEqual( - HTTPExportCapability(listen: listen).copyableLine, - "export http_proxy=http://127.0.0.1:8080;export https_proxy=http://127.0.0.1:8080;") + XCTAssertEqual( + HTTPExportCapability(listen: listen).copyableLine, + "export http_proxy=http://127.0.0.1:8080;export https_proxy=http://127.0.0.1:8080;", + "\(mode) 不改变导出行") + } } } diff --git a/ShadowsocksX-NG2/Tests/ProxyControl/ProxyControlWorkflowTests.swift b/ShadowsocksX-NG2/Tests/ProxyControl/ProxyControlWorkflowTests.swift index 4286ae13..e5c747ab 100644 --- a/ShadowsocksX-NG2/Tests/ProxyControl/ProxyControlWorkflowTests.swift +++ b/ShadowsocksX-NG2/Tests/ProxyControl/ProxyControlWorkflowTests.swift @@ -318,7 +318,10 @@ final class ProxyControlWorkflowTests: XCTestCase { // MARK: - 命令边界 - func testCommandsTouchOnlyRuntimeAndTargetFactsAdapters() async { + /// 四个命令各自只触达自己的运行时缝:模式命令不派发 agent/系统代理命令, + /// 重同步不派发开关命令;目录激活、订阅刷新、设置保存与诊断导出不在本 + /// module 的依赖面上(类型层面不存在这些入口)。 + func testCommandsTouchOnlyTheirOwnRuntimeAdapter() async { await workflow.setAgentEnabled(true) await workflow.setSystemProxyEnabled(true) await workflow.setProxyMode(.global) @@ -328,8 +331,5 @@ final class ProxyControlWorkflowTests: XCTestCase { XCTAssertEqual(runtime.systemProxyCommands, [true]) XCTAssertEqual(runtime.modeCommands, [.global]) XCTAssertEqual(runtime.resyncCount, 1) - // 目标缝只被 snapshot 组装读取;目录激活、订阅刷新、设置保存与诊断导出 - // 不在本 module 的依赖面上(类型层面不存在这些入口)。 - XCTAssertFalse(targetFacts.queriedTargetIDs.isEmpty, "每次观察经窄缝取目标事实") } } diff --git a/ShadowsocksX-NG2/Tests/ProxyControl/ProxyModeAvailabilityTests.swift b/ShadowsocksX-NG2/Tests/ProxyControl/ProxyModeAvailabilityTests.swift index e94b4474..43b1960b 100644 --- a/ShadowsocksX-NG2/Tests/ProxyControl/ProxyModeAvailabilityTests.swift +++ b/ShadowsocksX-NG2/Tests/ProxyControl/ProxyModeAvailabilityTests.swift @@ -27,16 +27,6 @@ final class ProxyModeAvailabilityTests: XCTestCase { FixedLocalProxyRanges.systemProxyExceptions) } - func testIPv6LANModeKeepsLocalSystemProxyEndpoints() { - let listen = ProxyListeningConfiguration(listenerMode: .allIPv6Interfaces) - let document = SslocalRuntimeDocument(servers: [], listen: listen) - - let configuration = ProxyMode.direct.systemProxyConfiguration(for: document) - - XCTAssertEqual(configuration.socks, .init(host: "127.0.0.1", port: listen.socksPort)) - XCTAssertEqual(configuration.http, .init(host: "127.0.0.1", port: listen.httpPort)) - } - /// 全局模式:系统 SOCKS/HTTP 投影 + 固定本地例外,与直连共用 /// 同一安全范围;公网路由由 ACL 决定,不由系统例外决定。 func testGlobalModeUsesLocalSOCKSAndIncludesFixedBypasses() { diff --git a/ShadowsocksX-NG2/Tests/ProxyRuntime/ProxyRuntimeControllerTests+AgentAndSystemProxy.swift b/ShadowsocksX-NG2/Tests/ProxyRuntime/ProxyRuntimeControllerTests+AgentAndSystemProxy.swift index bc50fdb0..6d14723d 100644 --- a/ShadowsocksX-NG2/Tests/ProxyRuntime/ProxyRuntimeControllerTests+AgentAndSystemProxy.swift +++ b/ShadowsocksX-NG2/Tests/ProxyRuntime/ProxyRuntimeControllerTests+AgentAndSystemProxy.swift @@ -146,20 +146,6 @@ final class ProxyRuntimeAgentAndSystemProxyTests: ProxyRuntimeControllerTests { FileManager.default.fileExists(atPath: runtime.contract.path), "运行时契约保留") } - func testDisableClearFailureIsPresentedAndObservationStillStops() async throws { - let seeded = try makeSeededCatalog() - let controller = makeController(probe: ProxyRuntimeFixture.FakeProbe.reachable()) - try await controller.activate(seeded.server) - await controller.setAgentEnabled(true) - await controller.setSystemProxyEnabled(true) - systemProxy.clearError = SystemProxyError.commitFailed("busy") - - await controller.setSystemProxyEnabled(false) - - XCTAssertEqual(controller.systemProxyState, .clearFailed(.operation(.commitFailed))) - XCTAssertFalse(systemProxyNetworkChangeMonitor.isObserving, "清理失败后也结束临时观察") - } - /// 系统代理意图持久化:GUI 重启后从恢复快照读回。 func testSystemProxyIntentPersistsAcrossGUIRestart() async throws { let seeded = try makeSeededCatalog() diff --git a/ShadowsocksX-NG2/Tests/ProxyRuntime/ProxyRuntimeControllerTests+GlobalMode.swift b/ShadowsocksX-NG2/Tests/ProxyRuntime/ProxyRuntimeControllerTests+GlobalMode.swift index 82086943..84f776af 100644 --- a/ShadowsocksX-NG2/Tests/ProxyRuntime/ProxyRuntimeControllerTests+GlobalMode.swift +++ b/ShadowsocksX-NG2/Tests/ProxyRuntime/ProxyRuntimeControllerTests+GlobalMode.swift @@ -96,37 +96,4 @@ final class ProxyRuntimeGlobalModeTests: ProxyRuntimeControllerTests { systemProxy.applied.last?.http, .init(host: "127.0.0.1", port: ActivationFixture.listen.httpPort)) } - - func testFailedGlobalInstanceKeepsNewModeAndStopsService() 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), - launchHealthTimeoutSeconds: 0.05) - try await controller.activate(seeded.server) - XCTAssertEqual(controller.systemProxyState, .applied) - let runtimeStore = RuntimeFileStore(fileURL: runtime.contract) - let previousDocument = try XCTUnwrap(runtimeStore.loadDocument()) - let previousApplicationCount = systemProxy.applied.count - - agent.onRegister = { [runtimeStore, previousDocument] in - guard let requested = runtimeStore.loadDocument() else { return } - let accepted = requested.aclRuntime == nil ? requested : previousDocument - try? runtimeStore.writeRuntimeReceipt(for: accepted, processID: 42) - } - - await controller.setProxyMode(.global) - - XCTAssertEqual(controller.proxyMode, .global) - XCTAssertEqual(controller.settings.preferredMode, .global) - XCTAssertEqual(controller.controlPreferences.preferredMode, .global) - XCTAssertEqual(runtimeStore.loadDocument()?.aclRuntime?.summary, "global") - XCTAssertEqual(controller.state, .serviceFailed(.agent)) - XCTAssertNotNil(runtimeStore.readFailure()) - XCTAssertEqual(systemProxy.applied.count, previousApplicationCount) - XCTAssertGreaterThan(systemProxy.clearCount, 0) - } } diff --git a/ShadowsocksX-NG2/Tests/Rules/GeneratedRuleSnapshotTests.swift b/ShadowsocksX-NG2/Tests/Rules/GeneratedRuleSnapshotTests.swift index 5b708def..32e389ba 100644 --- a/ShadowsocksX-NG2/Tests/Rules/GeneratedRuleSnapshotTests.swift +++ b/ShadowsocksX-NG2/Tests/Rules/GeneratedRuleSnapshotTests.swift @@ -83,7 +83,7 @@ final class GeneratedRuleSnapshotTests: XCTestCase { XCTAssertTrue(rules.allSatisfy { Set($0.keys) == ["action", "match"] }) } - func testSnapshotStoreRoundTripAndVersionGuard() throws { + func testSnapshotStoreRoundTripAndMissingFileGuard() throws { let snapshot = try generatedSnapshot("geolocation-cn") let dir = FileManager.default.temporaryDirectory .appendingPathComponent("ssxng-rule-snapshot-\(UUID().uuidString)", isDirectory: true) @@ -99,19 +99,6 @@ final class GeneratedRuleSnapshotTests: XCTestCase { let loaded = try store.load() XCTAssertEqual(loaded.rules.map(\.match), snapshot.rules.map(\.match)) XCTAssertEqual(loaded.metadata, snapshot.metadata) - - // schema / converter 版本不匹配使加载失败。 - let rawObject = try JSONSerialization.jsonObject(with: Data(contentsOf: url)) - guard var raw = rawObject as? [String: Any] else { - return XCTFail("snapshot JSON root should be an object") - } - raw["schemaVersion"] = 99 - try JSONSerialization.data(withJSONObject: raw).write(to: url) - XCTAssertThrowsError(try store.load()) { error in - XCTAssertEqual( - error as? RuleSnapshotError, - .schemaVersionMismatch(found: 99, expected: RuleSnapshotStore.currentSchemaVersion)) - } } } diff --git a/ShadowsocksX-NG2/Tests/Runtime/RuntimeDocumentTests.swift b/ShadowsocksX-NG2/Tests/Runtime/RuntimeDocumentTests.swift index f7e04764..94d54ac3 100644 --- a/ShadowsocksX-NG2/Tests/Runtime/RuntimeDocumentTests.swift +++ b/ShadowsocksX-NG2/Tests/Runtime/RuntimeDocumentTests.swift @@ -77,13 +77,6 @@ final class RuntimeDocumentTests: XCTestCase { // MARK: 监听设置透传 - func testListenSettingsUseTCPAndUDPForSOCKSAndTCPOnlyForHTTP() { - let listen = ProxyListeningConfiguration() - - XCTAssertEqual(listen.mode, "tcp_and_udp") - XCTAssertEqual(listen.locals.map(\.mode), ["tcp_and_udp", "tcp_only"]) - } - func testRuntimeDocumentLeavesTimeoutAndLoggingAtUpstreamDefaults() throws { let document = SslocalRuntimeDocument( servers: [ @@ -174,11 +167,4 @@ final class RuntimeDocumentTests: XCTestCase { XCTAssertEqual(document.socksLocal?.probeHost, "127.0.0.1") } } - - func testHTTPInboundIsAlwaysPresentAlongsideSOCKS() { - let listen = ProxyListeningConfiguration( - listenerMode: .disabled, socksPort: 2086, httpPort: 2087) - - XCTAssertEqual(listen.locals.map(\.inboundProtocol), ["socks", "http"]) - } }