mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-11-08 18:24:51 +00:00
193 lines
7.6 KiB
Swift
193 lines
7.6 KiB
Swift
import Foundation
|
|
import Testing
|
|
|
|
@testable import ShadowsocksX_NG2
|
|
|
|
struct SubscriptionInformationParserTests {
|
|
@Test func subscriptionInformationUsesStandardRootAndNamespacedDateFields() throws {
|
|
let snapshot = try SubscriptionDocumentParser.parse(
|
|
Data(
|
|
#"""
|
|
{"version":1,
|
|
"servers":[],
|
|
"bytes_used":25,
|
|
"bytes_remaining":75,
|
|
"x_shadowsocksx_ng":{"schema_version":1,
|
|
"expires_at":"2026-10-04T08:30:00+08:00",
|
|
"traffic_reset_at":"2026-10-05T00:00:00.125Z"}}
|
|
"""#
|
|
.utf8),
|
|
subscriptionID: .fresh())
|
|
|
|
#expect(snapshot.information.bytesUsed == 25)
|
|
#expect(snapshot.information.bytesRemaining == 75)
|
|
#expect(snapshot.information.expiresAt == Date(timeIntervalSince1970: 1_791_073_800))
|
|
#expect(snapshot.information.trafficResetAt == Date(timeIntervalSince1970: 1_791_158_400.125))
|
|
}
|
|
@Test func rfc3339OffsetOutsideTimeZoneFactoryRangeIsAccepted() throws {
|
|
let snapshot = try SubscriptionDocumentParser.parse(
|
|
Data(
|
|
#"{"version":1,"servers":[],"x_shadowsocksx_ng":{"schema_version":1,"expires_at":"2026-10-04T23:59:00+23:59"}}"#
|
|
.utf8),
|
|
subscriptionID: .fresh())
|
|
#expect(snapshot.information.expiresAt == Date(timeIntervalSince1970: 1_791_072_000))
|
|
}
|
|
@Test(arguments: [
|
|
"true", "false", "\"25\"", "-1", "1.5", "1.0", "1e3", "1e999", "18446744073709551616", "null",
|
|
"[]",
|
|
"{}",
|
|
])
|
|
func invalidByteCountsDoNotDiscardValidInformation(_ literal: String) throws {
|
|
let snapshot = try parse(
|
|
#"""
|
|
"bytes_used":\#(literal),
|
|
"bytes_remaining":75,
|
|
"x_shadowsocksx_ng":{"schema_version":1,
|
|
"expires_at":"1970-01-01T00:00:00Z"}
|
|
"""#
|
|
)
|
|
#expect(snapshot.information.bytesUsed == nil)
|
|
#expect(snapshot.information.bytesRemaining == 75)
|
|
#expect(snapshot.information.expiresAt == Date(timeIntervalSince1970: 0))
|
|
}
|
|
|
|
@Test func fullUnsignedRangeAndZeroAreAccepted() throws {
|
|
let snapshot = try parse(#""bytes_used":18446744073709551615,"bytes_remaining":0"#)
|
|
#expect(snapshot.information.bytesUsed == UInt64.max)
|
|
#expect(snapshot.information.bytesRemaining == 0)
|
|
}
|
|
|
|
@Test(arguments: [
|
|
#""2026-02-30T12:30:00Z""#, #""2026-10-04T24:00:00Z""#,
|
|
#""2026-10-04T00:00:00""#, #""2026-10-04T00:00:00Zjunk""#,
|
|
#""2026-10-04""#, #""2026-10-04T23:59:60Z""#, #""not a date""#,
|
|
"1791072000", "null", "true", "{}", "[]",
|
|
])
|
|
func invalidDateDoesNotAffectOtherFields(_ literal: String) throws {
|
|
let snapshot = try parse(
|
|
#"""
|
|
"bytes_used":0,
|
|
"x_shadowsocksx_ng":{"schema_version":1,
|
|
"expires_at":\#(literal),
|
|
"traffic_reset_at":"2026-10-04T00:00:00Z"}
|
|
"""#
|
|
)
|
|
#expect(snapshot.information.expiresAt == nil)
|
|
#expect(snapshot.information.trafficResetAt == Date(timeIntervalSince1970: 1_791_072_000))
|
|
#expect(snapshot.information.bytesUsed == 0)
|
|
}
|
|
|
|
@Test func rfc3339CaseOffsetsAndLeapSecondPreserveInstants() throws {
|
|
let snapshot = try parse(
|
|
#"""
|
|
"x_shadowsocksx_ng":{"schema_version":1,
|
|
"expires_at":"2026-10-03t19:00:00-05:00",
|
|
"traffic_reset_at":"2016-12-31T23:59:60Z"}
|
|
"""#
|
|
)
|
|
#expect(snapshot.information.expiresAt == Date(timeIntervalSince1970: 1_791_072_000))
|
|
#expect(snapshot.information.trafficResetAt == Date(timeIntervalSince1970: 1_483_228_800))
|
|
}
|
|
|
|
@Test func informationAndGroupingValidityAreIndependent() throws {
|
|
let treeText = try #require(String(bytes: SubscriptionDocs.tree(), encoding: .utf8))
|
|
.trimmingCharacters(in: .whitespacesAndNewlines)
|
|
let validTree = try SubscriptionDocumentParser.parse(
|
|
Data(
|
|
(treeText.replacingOccurrences(
|
|
of: "\"schema_version\": 1", with: "\"schema_version\": 1,\"expires_at\":123"
|
|
).dropLast() + #","bytes_used":25}"#).utf8),
|
|
subscriptionID: .fresh())
|
|
#expect(validTree.root.name == "Example subscription")
|
|
#expect(validTree.information.bytesUsed == 25)
|
|
#expect(validTree.information.expiresAt == nil)
|
|
|
|
let invalidTree = try parse(#""bytes_remaining":75,"x_shadowsocksx_ng":{"schema_version":99}"#)
|
|
#expect(invalidTree.root.name.isEmpty)
|
|
#expect(invalidTree.information.bytesRemaining == 75)
|
|
}
|
|
|
|
@Test func onlyExpectedPathsAreReadAcrossNestedEscapedAndUnrepresentableValues() throws {
|
|
let snapshot = try SubscriptionDocumentParser.parse(
|
|
Data(
|
|
#"""
|
|
{"version":1,"servers":[{"server":"203.0.113.7","server_port":8388,
|
|
"password":"{\"bytes_used\":999},\\tail","method":"aes-256-gcm","bytes_remaining":999}],
|
|
"ignored":1e999,"other":{"expires_at":"1970-01-01T00:00:00Z"},
|
|
"bytes_\u0075sed":25,"bytes_remaining":75,"expires_at":"1970-01-01T00:00:00Z",
|
|
"x_shadowsocksx_ng":{"schema_version":1,"ignored":1e999,"bytes_used":999,
|
|
"expires_\u0061t":"2026-10-04T00:00:00Z","other":{"traffic_reset_at":"1970-01-01T00:00:00Z"}}}
|
|
"""#.utf8), subscriptionID: .fresh())
|
|
#expect(snapshot.information.bytesUsed == 25)
|
|
#expect(snapshot.information.bytesRemaining == 75)
|
|
#expect(snapshot.information.expiresAt == Date(timeIntervalSince1970: 1_791_072_000))
|
|
#expect(snapshot.information.trafficResetAt == nil)
|
|
#expect(snapshot.root.children.count == 1)
|
|
}
|
|
|
|
@Test func utf8BOMPreservesValidSubscriptionInformation() throws {
|
|
let data =
|
|
Data([0xEF, 0xBB, 0xBF])
|
|
+ Data(#"{"version":1,"servers":[],"bytes_used":25,"bytes_remaining":75}"#.utf8)
|
|
let snapshot = try SubscriptionDocumentParser.parse(data, subscriptionID: .fresh())
|
|
#expect(snapshot.information.bytesUsed == 25)
|
|
#expect(snapshot.information.bytesRemaining == 75)
|
|
}
|
|
|
|
@Test(arguments: [
|
|
"null", "[]", "true", "1", #""text""#, "{}",
|
|
#"{"schema_version":99}"#, #"{"schema_version":null}"#,
|
|
#"{"schema_version":true}"#, #"{"schema_version":"1"}"#,
|
|
#"{"schema_version":1e999}"#,
|
|
#"{"expires_at":"1970-01-01T00:00:00Z"}"#,
|
|
#"{"schema_version":99,"expires_at":"1970-01-01T00:00:00Z"}"#,
|
|
])
|
|
func invalidNamespaceOrVersionDoesNotReadDatesOrDiscardTraffic(_ literal: String) throws {
|
|
let snapshot = try parse(
|
|
#"""
|
|
"bytes_used":25,
|
|
"bytes_remaining":75,
|
|
"expires_at":"1970-01-01T00:00:00Z",
|
|
"traffic_reset_at":"1970-01-01T00:00:00Z",
|
|
"x_shadowsocksx_ng":\#(literal)
|
|
"""#
|
|
)
|
|
#expect(snapshot.information.bytesUsed == 25)
|
|
#expect(snapshot.information.bytesRemaining == 75)
|
|
#expect(snapshot.information.expiresAt == nil)
|
|
#expect(snapshot.information.trafficResetAt == nil)
|
|
}
|
|
|
|
@Test func rootDatesAreIgnoredWithoutNamespaceFallback() throws {
|
|
let snapshot = try parse(
|
|
#""expires_at":"1970-01-01T00:00:00Z","traffic_reset_at":"1970-01-01T00:00:00Z""#)
|
|
#expect(snapshot.information.isEmpty)
|
|
let invalidDate = try parse(
|
|
#""expires_at":"1970-01-01T00:00:00Z","x_shadowsocksx_ng":{"schema_version":1,"expires_at":null}"#
|
|
)
|
|
#expect(invalidDate.information.isEmpty)
|
|
}
|
|
|
|
@Test func invalidTreeAndPolicyDoNotDiscardValidDates() throws {
|
|
let snapshot = try parse(
|
|
#"""
|
|
"x_shadowsocksx_ng":{"schema_version":1,
|
|
"groups":false,
|
|
"auto_update":false,
|
|
"expires_at":"1970-01-01T00:00:00Z",
|
|
"traffic_reset_at":"1970-01-02T00:00:00Z"}
|
|
"""#
|
|
)
|
|
#expect(snapshot.information.expiresAt == Date(timeIntervalSince1970: 0))
|
|
#expect(snapshot.information.trafficResetAt == Date(timeIntervalSince1970: 86_400))
|
|
#expect(snapshot.root.children.isEmpty)
|
|
#expect(snapshot.autoUpdatePolicy == .invalid)
|
|
}
|
|
|
|
private func parse(_ fields: String) throws -> SubscriptionSnapshot {
|
|
try SubscriptionDocumentParser.parse(
|
|
Data("{\"version\":1,\"servers\":[],\(fields)}".utf8), subscriptionID: .fresh())
|
|
}
|
|
|
|
}
|