mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-10-30 14:13:50 +00:00
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>
This commit is contained in:
co-authored by
DeepSeek Harness
parent
d63d04c114
commit
e7116a5ebd
@@ -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(
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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()
|
||||
|
||||
+10
-25
@@ -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) 不改变导出行")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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, "每次观察经窄缝取目标事实")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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() {
|
||||
|
||||
-14
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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"])
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user