Files
ShadowsocksX-NG/ShadowsocksX-NG2/Tests/Runtime/RuntimeDocumentTests.swift
T
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

171 lines
6.5 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import XCTest
@testable import ShadowsocksX_NG2
/// 运行时文档缝:派生 JSON 的字段契约与监听设置透传。
/// 上游字段名以 shadowsocks-rust v1.26.0 配置契约为准;无插件时整体省略插件字段。
final class RuntimeDocumentTests: XCTestCase {
private func encodedDictionary(_ document: SslocalRuntimeDocument) throws -> [String: Any] {
let data = try document.jsonData()
let object = try JSONSerialization.jsonObject(with: data)
return try XCTUnwrap(object as? [String: Any])
}
private func makeDocument(
plugin: String? = nil, pluginOpts: String? = nil
) -> SslocalRuntimeDocument {
SslocalRuntimeDocument(
servers: [
SslocalServerDocument(
id: "server-1",
server: "203.0.113.7",
serverPort: 8388,
password: "resolved-password",
method: "aes-256-gcm",
plugin: plugin,
pluginOpts: pluginOpts
)
], listen: ProxyListeningConfiguration()
)
}
// MARK: 字段契约
func testDocumentEncodesUpstreamContractKeys() throws {
let dictionary = try encodedDictionary(
makeDocument(plugin: "/bundle/plugins/x", pluginOpts: "obfs=http"))
let locals = try XCTUnwrap(dictionary["locals"] as? [[String: Any]])
XCTAssertEqual(locals.count, 2)
XCTAssertEqual(locals[0]["local_address"] as? String, "127.0.0.1")
XCTAssertEqual(locals[0]["local_port"] as? Int, 11086)
XCTAssertEqual(locals[0]["protocol"] as? String, "socks")
XCTAssertEqual(locals[0]["mode"] as? String, "tcp_and_udp")
XCTAssertEqual(locals[1]["local_port"] as? Int, 11087)
XCTAssertEqual(locals[1]["protocol"] as? String, "http")
XCTAssertNil(dictionary["x_shadowsocksx_ng_listen"])
XCTAssertNil(dictionary["x_shadowsocksx_ng_acl"])
XCTAssertNil(dictionary["ipv6_only"])
XCTAssertNil(dictionary["timeout"])
let servers = try XCTUnwrap(dictionary["servers"] as? [[String: Any]])
XCTAssertEqual(servers.count, 1)
let server = servers[0]
XCTAssertEqual(server["id"] as? String, "server-1", "叶子身份进入文档,供诊断与稳定标识")
XCTAssertFalse(
server.keys.contains("remarks"), "显示名是目录元数据,不进契约(只承载连接有效值)")
XCTAssertEqual(server["server"] as? String, "203.0.113.7")
XCTAssertEqual(server["server_port"] as? Int, 8388)
XCTAssertEqual(server["password"] as? String, "resolved-password")
XCTAssertEqual(server["method"] as? String, "aes-256-gcm")
XCTAssertEqual(server["plugin"] as? String, "/bundle/plugins/x")
XCTAssertEqual(server["plugin_opts"] as? String, "obfs=http")
}
func testPluginFieldsAreAbsentEntirelyWithoutPlugin() throws {
let dictionary = try encodedDictionary(makeDocument())
let server = try XCTUnwrap((dictionary["servers"] as? [[String: Any]])?[0])
XCTAssertFalse(server.keys.contains("plugin"), "选「无」时 plugin 字段整体省略(D10)")
XCTAssertFalse(server.keys.contains("plugin_opts"))
}
func testJSONDataIsStableAcrossEncodes() throws {
let document = makeDocument(plugin: "/bundle/plugins/x")
XCTAssertEqual(try document.jsonData(), try document.jsonData(), "编码确定性(sortedKeys)")
}
// MARK: 监听设置透传
func testRuntimeDocumentLeavesTimeoutAndLoggingAtUpstreamDefaults() throws {
let document = SslocalRuntimeDocument(
servers: [
SslocalServerDocument(
id: "server-1", server: "example.com", serverPort: 8388,
password: "pw", method: "aes-256-gcm", plugin: nil, pluginOpts: nil)
],
listen: ProxyListeningConfiguration())
let dictionary = try encodedDictionary(document)
XCTAssertFalse(dictionary.keys.contains("timeout"))
XCTAssertFalse(dictionary.keys.contains("verbose"))
}
func testACLIdentityStaysInMemoryAndOnlyPathIsEncoded() throws {
let acl = ProxyACLDocument.direct(
at: URL(fileURLWithPath: "/tmp/ssxng-tests/acl-active.ini"))
let document = SslocalRuntimeDocument(
servers: [], listen: ProxyListeningConfiguration(), acl: acl)
let dictionary = try encodedDictionary(document)
XCTAssertEqual(dictionary["acl"] as? String, acl.path)
XCTAssertNil(dictionary["x_shadowsocksx_ng_acl"])
let changedPath = document.replacingACL(
.direct(at: URL(fileURLWithPath: "/tmp/ssxng-tests/other.acl")))
XCTAssertNotEqual(document.listenFingerprint, changedPath.listenFingerprint)
}
func testLocalhostModeDerivesBothSslocalInbounds() {
let listen = ProxyListeningConfiguration(
listenerMode: .disabled,
socksPort: 1086,
httpPort: 1087)
XCTAssertEqual(listen.bindAddress, "127.0.0.1")
XCTAssertEqual(
listen.locals,
[
SslocalLocalDocument(
inboundProtocol: "socks", localAddress: "127.0.0.1", localPort: 1086,
mode: "tcp_and_udp"),
SslocalLocalDocument(
inboundProtocol: "http", localAddress: "127.0.0.1", localPort: 1087,
mode: "tcp_only"),
])
}
func testAllIPv4ModeUsesWildcardBindings() {
let listen = ProxyListeningConfiguration(
listenerMode: .allIPv4Interfaces,
socksPort: 1086,
httpPort: 1087)
XCTAssertEqual(listen.bindAddress, "127.0.0.1")
XCTAssertEqual(Set(listen.locals.map(\.localAddress)), ["127.0.0.1", "0.0.0.0"])
}
func testDualStackModeSetsIPv6OnlyFalseAndChangesListenerFingerprint() throws {
var listen = ProxyListeningConfiguration()
listen.listenerMode = .allIPv4AndIPv6Interfaces
let dualStack = SslocalRuntimeDocument(servers: [], listen: listen)
let dictionary = try encodedDictionary(dualStack)
XCTAssertEqual(dictionary["ipv6_only"] as? Bool, false)
XCTAssertEqual(dualStack.socksLocal?.probeHost, "127.0.0.1")
listen.listenerMode = .allIPv6Interfaces
let ipv6Only = SslocalRuntimeDocument(servers: [], listen: listen)
XCTAssertEqual(try encodedDictionary(ipv6Only)["ipv6_only"] as? Bool, true)
XCTAssertNotEqual(
dualStack.listenFingerprint, ipv6Only.listenFingerprint,
"IPv6_ONLY 差异必须使 wrapper 重启 sslocal,即使两者都绑定 ::")
}
func testListenerModeBindingsAndProxyLoopbackAddresses() {
XCTAssertEqual(
ListenerMode.allCases.map(\.bindingHint),
[
"127.0.0.1",
"0.0.0.0",
":: / IPV6_V6ONLY=false",
":: / IPV6_V6ONLY=true",
])
for mode in ListenerMode.allCases {
let document = SslocalRuntimeDocument(
servers: [], listen: ProxyListeningConfiguration(listenerMode: mode))
XCTAssertEqual(document.socksLocal?.probeHost, "127.0.0.1")
}
}
}