From 646d0190118d30548d25c581a5fbd68eac39b4a9 Mon Sep 17 00:00:00 2001 From: Piotr Piotrowski Date: Thu, 1 Feb 2024 16:27:46 +0100 Subject: [PATCH] Format code with swift-format Signed-off-by: Piotr Piotrowski --- Package.swift | 29 ++--- Sources/Benchmark/main.swift | 6 +- Sources/BenchmarkPubSub/main.swift | 14 ++- Sources/BenchmarkSub/main.swift | 12 +- .../NatsSwift/Extensions/Data+Parser.swift | 34 ++++-- .../Extensions/String+Utilities.swift | 7 +- Sources/NatsSwift/NatsClient/NatsClient.swift | 26 +++-- .../NatsClient/NatsClientOptions.swift | 2 +- Sources/NatsSwift/NatsConnection.swift | 81 ++++++++----- Sources/NatsSwift/NatsError.swift | 2 - Sources/NatsSwift/NatsHeaders.swift | 10 +- Sources/NatsSwift/NatsJwtUtils.swift | 11 +- Sources/NatsSwift/NatsProto.swift | 108 +++++++++++------- Sources/NatsSwift/NatsSubscription.swift | 4 +- Tests/LinuxMain.swift | 4 +- .../Integration/ConnectionTests.swift | 67 +++++------ .../Integration/MessageWithHeadersTests.swift | 5 +- Tests/NatsSwiftTests/NatsServer.swift | 50 ++++---- Tests/NatsSwiftTests/Unit/HeadersTests.swift | 30 ++--- Tests/NatsSwiftTests/Unit/JwtTests.swift | 13 ++- Tests/NatsSwiftTests/Unit/ParserTests.swift | 82 +++++++++---- 21 files changed, 362 insertions(+), 235 deletions(-) diff --git a/Package.swift b/Package.swift index e0832d2..3667dca 100644 --- a/Package.swift +++ b/Package.swift @@ -13,26 +13,27 @@ let package = Package( dependencies: [ .package(url: "https://github.com/apple/swift-nio.git", from: "2.0.0"), .package(url: "https://github.com/apple/swift-log.git", from: "1.4.2"), - .package(url: "https://github.com/nats-io/nkeys.swift.git", from: "0.1.1") + .package(url: "https://github.com/nats-io/nkeys.swift.git", from: "0.1.1"), ], targets: [ - .target(name: "NatsSwift", dependencies: [ - .product(name: "NIO", package: "swift-nio"), - .product(name: "Logging", package: "swift-log"), - .product(name: "NIOFoundationCompat", package: "swift-nio"), - .product(name: "NKeys", package: "nkeys.swift") - ]), + .target( + name: "NatsSwift", + dependencies: [ + .product(name: "NIO", package: "swift-nio"), + .product(name: "Logging", package: "swift-log"), + .product(name: "NIOFoundationCompat", package: "swift-nio"), + .product(name: "NKeys", package: "nkeys.swift"), + ]), .testTarget( - name: "NatsSwiftTests", - dependencies: ["NatsSwift"], - resources: [ - .process("Integration/Resources") - ] + name: "NatsSwiftTests", + dependencies: ["NatsSwift"], + resources: [ + .process("Integration/Resources") + ] ), .executableTarget(name: "Benchmark", dependencies: ["NatsSwift"]), .executableTarget(name: "BenchmarkPubSub", dependencies: ["NatsSwift"]), - .executableTarget(name: "BenchmarkSub", dependencies: ["NatsSwift"]) + .executableTarget(name: "BenchmarkSub", dependencies: ["NatsSwift"]), ] ) - diff --git a/Sources/Benchmark/main.swift b/Sources/Benchmark/main.swift index be51247..e180e48 100644 --- a/Sources/Benchmark/main.swift +++ b/Sources/Benchmark/main.swift @@ -1,5 +1,5 @@ -import NatsSwift import Foundation +import NatsSwift let nats = ClientOptions() .url(URL(string: "nats://localhost:4222")!) @@ -22,6 +22,6 @@ for _ in 0.. [Data] { + func split(separator: Data, maxSplits: Int = .max, omittingEmptySubsequences: Bool = true) + -> [Data] + { var chunks: [Data] = [] var start = startIndex var end = startIndex @@ -77,7 +79,9 @@ extension Data { var lineData: Data if let range = self[startIndex...].range(of: Data.crlf) { let lineEndIndex = range.lowerBound - nextLineStartIndex = self.index(range.upperBound, offsetBy: 0, limitedBy: self.endIndex) ?? self.endIndex + nextLineStartIndex = + self.index(range.upperBound, offsetBy: 0, limitedBy: self.endIndex) + ?? self.endIndex lineData = self[startIndex.. msg.headersLength { payload = Data() } var headers = HeaderMap() - + // if the whole msg length (including training crlf) is longer // than the remaining chunk, break and return the remainder if payloadEndIndex + Data.crlf.count > endIndex { @@ -132,7 +139,7 @@ extension Data { break } - let headersData = self[headersStartIndex.. String { let uuid = String.uuid() return uuid[0...7] } - + func trimWhitespacesAndApostrophes() -> String { return self.trimmingCharacters(in: String.charactersToTrim) } @@ -21,7 +22,7 @@ extension String { return UUID().uuidString.trimmingCharacters(in: .punctuationCharacters) } - subscript (bounds: CountableClosedRange) -> String { + subscript(bounds: CountableClosedRange) -> String { let start = index(startIndex, offsetBy: bounds.lowerBound) let end = index(startIndex, offsetBy: bounds.upperBound) return String(self[start...end]) diff --git a/Sources/NatsSwift/NatsClient/NatsClient.swift b/Sources/NatsSwift/NatsClient/NatsClient.swift index e7905c9..30a4bc6 100755 --- a/Sources/NatsSwift/NatsClient/NatsClient.swift +++ b/Sources/NatsSwift/NatsClient/NatsClient.swift @@ -3,12 +3,11 @@ // NatsSwift // +import Dispatch import Foundation +import Logging import NIO import NIOFoundationCompat -import Dispatch - -import Logging var logger = Logger(label: "NatsSwift") @@ -69,12 +68,13 @@ public class Client { } extension Client { - public func connect() async throws { + public func connect() async throws { //TODO(jrm): reafactor for reconnection and review error handling. //TODO(jrm): handle response logger.debug("connect") guard let connectionHandler = self.connectionHandler else { - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "empty connection handler"]) + throw NSError( + domain: "nats_swift", code: 1, userInfo: ["message": "empty connection handler"]) } try await connectionHandler.connect() } @@ -82,15 +82,19 @@ extension Client { public func close() async throws { logger.debug("close") guard let connectionHandler = self.connectionHandler else { - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "empty connection handler"]) + throw NSError( + domain: "nats_swift", code: 1, userInfo: ["message": "empty connection handler"]) } try await connectionHandler.close() } - public func publish(_ payload: Data, subject: String, reply: String? = nil, headers: HeaderMap? = nil) throws { + public func publish( + _ payload: Data, subject: String, reply: String? = nil, headers: HeaderMap? = nil + ) throws { logger.debug("publish") guard let connectionHandler = self.connectionHandler else { - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "empty connection handler"]) + throw NSError( + domain: "nats_swift", code: 1, userInfo: ["message": "empty connection handler"]) } try connectionHandler.write(operation: ClientOp.Publish((subject, reply, payload, headers))) } @@ -98,7 +102,8 @@ extension Client { public func flush() async throws { logger.debug("flush") guard let connectionHandler = self.connectionHandler else { - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "empty connection handler"]) + throw NSError( + domain: "nats_swift", code: 1, userInfo: ["message": "empty connection handler"]) } connectionHandler.channel?.flush() } @@ -106,7 +111,8 @@ extension Client { public func subscribe(to subject: String) async throws -> Subscription { logger.info("subscribe to subject \(subject)") guard let connectionHandler = self.connectionHandler else { - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "empty connection handler"]) + throw NSError( + domain: "nats_swift", code: 1, userInfo: ["message": "empty connection handler"]) } return try await connectionHandler.subscribe(subject) diff --git a/Sources/NatsSwift/NatsClient/NatsClientOptions.swift b/Sources/NatsSwift/NatsClient/NatsClientOptions.swift index bb71574..1341556 100644 --- a/Sources/NatsSwift/NatsClient/NatsClientOptions.swift +++ b/Sources/NatsSwift/NatsClient/NatsClientOptions.swift @@ -3,10 +3,10 @@ // NatsSwift // +import Dispatch import Foundation import NIO import NIOFoundationCompat -import Dispatch public class ClientOptions { private var urls: [URL] = [] diff --git a/Sources/NatsSwift/NatsConnection.swift b/Sources/NatsSwift/NatsConnection.swift index 19fa519..c55b99f 100644 --- a/Sources/NatsSwift/NatsConnection.swift +++ b/Sources/NatsSwift/NatsConnection.swift @@ -3,11 +3,11 @@ // NatsSwoft // +import Atomics +import Dispatch import Foundation import NIO import NIOFoundationCompat -import Dispatch -import Atomics import NKeys class ConnectionHandler: ChannelInboundHandler { @@ -26,7 +26,7 @@ class ConnectionHandler: ChannelInboundHandler { typealias InboundIn = ByteBuffer internal var state: NatsState = .Pending - internal var subscriptions: [ UInt64: Subscription ] + internal var subscriptions: [UInt64: Subscription] internal var subscriptionCounter = ManagedAtomic(0) internal var serverInfo: ServerInfo? internal var auth: Auth? @@ -34,7 +34,6 @@ class ConnectionHandler: ChannelInboundHandler { private var pingTask: RepeatedTask? private var outstandingPings = ManagedAtomic(0) - func channelRead(context: ChannelHandlerContext, data: NIOAny) { logger.debug("channel read") var byteBuffer = self.unwrapInboundIn(data) @@ -108,11 +107,13 @@ class ConnectionHandler: ChannelInboundHandler { let normalizedError = err.normalizedError // on some errors, force reconnect - if normalizedError == "stale connection" || normalizedError == "maximum connections exceeded" { + if normalizedError == "stale connection" + || normalizedError == "maximum connections exceeded" + { inputBuffer.clear() context.fireErrorCaught(err) } - // TODO(pp): handle auth errors here + // TODO(pp): handle auth errors here case let .Message(msg): self.handleIncomingMessage(msg) case let .HMessage(msg): @@ -126,12 +127,15 @@ class ConnectionHandler: ChannelInboundHandler { } inputBuffer.clear() } - init(inputBuffer: ByteBuffer, urls: [URL], reconnectWait: TimeInterval, maxReconnects: Int?, pingInterval: TimeInterval, auth: Auth?) { + init( + inputBuffer: ByteBuffer, urls: [URL], reconnectWait: TimeInterval, maxReconnects: Int?, + pingInterval: TimeInterval, auth: Auth? + ) { self.inputBuffer = self.allocator.buffer(capacity: 1024) self.urls = urls self.group = MultiThreadedEventLoopGroup(numberOfThreads: System.coreCount) self.inputBuffer = allocator.buffer(capacity: 1024) - self.subscriptions = [UInt64:Subscription]() + self.subscriptions = [UInt64: Subscription]() self.reconnectWait = UInt64(reconnectWait * 1_000_000_000) self.maxReconnects = maxReconnects self.auth = auth @@ -151,7 +155,10 @@ class ConnectionHandler: ChannelInboundHandler { Task.detached { do { let bootstrap = ClientBootstrap(group: self.group) - .channelOption(ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), value: 1) + .channelOption( + ChannelOptions.socket(SocketOptionLevel(SOL_SOCKET), SO_REUSEADDR), + value: 1 + ) .channelInitializer { channel in //Fixme(jrm): do not ignore error from addHandler future. channel.pipeline.addHandler(self).whenComplete { result in @@ -165,7 +172,8 @@ class ConnectionHandler: ChannelInboundHandler { return channel.eventLoop.makeSucceededFuture(()) }.connectTimeout(.seconds(5)) guard let url = self.urls.first, let host = url.host, let port = url.port else { - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "no url"]) + throw NSError( + domain: "nats_swift", code: 1, userInfo: ["message": "no url"]) } self.channel = try await bootstrap.connect(host: host, port: port).get() } catch { @@ -177,23 +185,32 @@ class ConnectionHandler: ChannelInboundHandler { self.serverInfo = info // TODO(jrm): Add rest of auth here. - var initialConnect = ConnectInfo(verbose: false, pedantic: false, userJwt: nil, nkey: "", name: "", echo: true, lang: self.lang, version: self.version, natsProtocol: .dynamic, tlsRequired: false, user: self.auth?.user ?? "", pass: self.auth?.password ?? "", authToken: self.auth?.token ?? "", headers: true, noResponders: true) + var initialConnect = ConnectInfo( + verbose: false, pedantic: false, userJwt: nil, nkey: "", name: "", echo: true, + lang: self.lang, version: self.version, natsProtocol: .dynamic, tlsRequired: false, + user: self.auth?.user ?? "", pass: self.auth?.password ?? "", + authToken: self.auth?.token ?? "", headers: true, noResponders: true) if let auth = self.auth { - if let credentialsPath = auth.credentialsPath { + if let credentialsPath = auth.credentialsPath { let credentials = try await URLSession.shared.data(from: credentialsPath).0 guard let jwt = JwtUtils.parseDecoratedJWT(contents: credentials) else { - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "failed to extract JWT from credentials file"]) + throw NSError( + domain: "nats_swift", code: 1, + userInfo: ["message": "failed to extract JWT from credentials file"]) } - guard let nkey = JwtUtils.parseDecoratedNKey(contents: credentials) else { - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "failed to extract NKEY from credentials file"]) + guard let nkey = JwtUtils.parseDecoratedNKey(contents: credentials) else { + throw NSError( + domain: "nats_swift", code: 1, + userInfo: ["message": "failed to extract NKEY from credentials file"]) } - guard let nonce = self.serverInfo?.nonce else { - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "missing nonce"]) + guard let nonce = self.serverInfo?.nonce else { + throw NSError( + domain: "nats_swift", code: 1, userInfo: ["message": "missing nonce"]) } let keypair = try KeyPair(seed: String(data: nkey, encoding: .utf8)!) let nonceData = nonce.data(using: .utf8)! - let sig = try keypair.sign(input: nonceData) + let sig = try keypair.sign(input: nonceData) let base64sig = sig.base64EncodedURLSafeNotPadded() initialConnect.signature = base64sig initialConnect.userJwt = String(data: jwt, encoding: .utf8)! @@ -217,10 +234,12 @@ class ConnectionHandler: ChannelInboundHandler { throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "empty channel"]) } // Schedule the task to send a PING periodically - let pingInterval = TimeAmount.nanoseconds(Int64(self.pingInterval*1_000_000_000)) - self.pingTask = channel.eventLoop.scheduleRepeatedTask(initialDelay: pingInterval, delay: pingInterval) { [weak self] task in - self?.sendPing() - } + let pingInterval = TimeAmount.nanoseconds(Int64(self.pingInterval * 1_000_000_000)) + self.pingTask = channel.eventLoop.scheduleRepeatedTask( + initialDelay: pingInterval, delay: pingInterval + ) { [weak self] task in + self?.sendPing() + } logger.debug("connection established") } @@ -236,7 +255,8 @@ class ConnectionHandler: ChannelInboundHandler { } private func sendPing() { - let pingsOut = self.outstandingPings.wrappingIncrementThenLoad(ordering: AtomicUpdateOrdering.relaxed) + let pingsOut = self.outstandingPings.wrappingIncrementThenLoad( + ordering: AtomicUpdateOrdering.relaxed) if pingsOut > 2 { handleDisconnect() return @@ -290,7 +310,7 @@ class ConnectionHandler: ChannelInboundHandler { } promise.futureResult.whenComplete { result in do { - try result.get() + try result.get() } catch { logger.error("Error closing connection: \(error)") } @@ -323,14 +343,18 @@ class ConnectionHandler: ChannelInboundHandler { } func handleIncomingMessage(_ message: MessageInbound) { - let natsMsg = NatsMessage(payload: message.payload, subject: message.subject, replySubject: message.reply, length: message.length, headers: nil, status: nil) + let natsMsg = NatsMessage( + payload: message.payload, subject: message.subject, replySubject: message.reply, + length: message.length, headers: nil, status: nil) if let sub = self.subscriptions[message.sid] { sub.receiveMessage(natsMsg) } } func handleIncomingHMessage(_ message: HMessageInbound) { - let natsMsg = NatsMessage(payload: message.payload, subject: message.subject, replySubject: message.reply, length: message.length, headers: message.headers, status: nil) + let natsMsg = NatsMessage( + payload: message.payload, subject: message.subject, replySubject: message.reply, + length: message.length, headers: message.headers, status: nil) if let sub = self.subscriptions[message.sid] { sub.receiveMessage(natsMsg) } @@ -344,7 +368,7 @@ class ConnectionHandler: ChannelInboundHandler { try self.writeMessage(payload) } - func writeMessage(_ message: ByteBuffer) throws { + func writeMessage(_ message: ByteBuffer) throws { _ = channel?.write(message) if channel?.isWritable ?? true { channel?.flush() @@ -352,7 +376,8 @@ class ConnectionHandler: ChannelInboundHandler { } func subscribe(_ subject: String) async throws -> Subscription { - let sid = self.subscriptionCounter.wrappingIncrementThenLoad(ordering: AtomicUpdateOrdering.relaxed) + let sid = self.subscriptionCounter.wrappingIncrementThenLoad( + ordering: AtomicUpdateOrdering.relaxed) try write(operation: ClientOp.Subscribe((sid, subject, nil))) let sub = Subscription(subject: subject) self.subscriptions[sid] = sub diff --git a/Sources/NatsSwift/NatsError.swift b/Sources/NatsSwift/NatsError.swift index 2ddc658..7e0846d 100644 --- a/Sources/NatsSwift/NatsError.swift +++ b/Sources/NatsSwift/NatsError.swift @@ -38,5 +38,3 @@ struct NatsTimeoutError: NatsError { self.description = description } } - - diff --git a/Sources/NatsSwift/NatsHeaders.swift b/Sources/NatsSwift/NatsHeaders.swift index b637782..9647fdb 100644 --- a/Sources/NatsSwift/NatsHeaders.swift +++ b/Sources/NatsSwift/NatsHeaders.swift @@ -1,6 +1,5 @@ import Foundation - // Represents NATS header field value in Swift. public struct HeaderValue: Equatable, CustomStringConvertible { private var inner: String @@ -32,7 +31,8 @@ public enum ParseHeaderNameError: Error, CustomStringConvertible { public var description: String { switch self { case .invalidCharacter: - return "Invalid header name (name cannot contain non-ascii alphanumeric characters other than '-')" + return + "Invalid header name (name cannot contain non-ascii alphanumeric characters other than '-')" } } } @@ -42,8 +42,8 @@ public struct HeaderName: Equatable, Hashable, CustomStringConvertible { private var inner: String public init(_ value: String) throws { - if value.contains(where: { $0 == ":" || $0.asciiValue! < 33 || $0.asciiValue! > 126 }) { - throw ParseHeaderNameError.invalidCharacter + if value.contains(where: { $0 == ":" || $0.asciiValue! < 33 || $0.asciiValue! > 126 }) { + throw ParseHeaderNameError.invalidCharacter } self.inner = value } @@ -121,5 +121,3 @@ extension HeaderMap { } } } - - diff --git a/Sources/NatsSwift/NatsJwtUtils.swift b/Sources/NatsSwift/NatsJwtUtils.swift index 67cbded..e14b847 100644 --- a/Sources/NatsSwift/NatsJwtUtils.swift +++ b/Sources/NatsSwift/NatsJwtUtils.swift @@ -4,7 +4,10 @@ class JwtUtils { // This regular expression is equivalent to the one used in Rust. static let userConfigRE: NSRegularExpression = { do { - return try NSRegularExpression(pattern: "\\s*(?:(?:-{3,}.*-{3,}\\r?\\n)([\\w\\-.=]+)(?:\\r?\\n-{3,}.*-{3,}\\r?\\n))", options: []) + return try NSRegularExpression( + pattern: + "\\s*(?:(?:-{3,}.*-{3,}\\r?\\n)([\\w\\-.=]+)(?:\\r?\\n-{3,}.*-{3,}\\r?\\n))", + options: []) } catch { fatalError("Invalid regular expression: \(error)") } @@ -12,7 +15,8 @@ class JwtUtils { /// Parses a credentials file and returns its user JWT. static func parseDecoratedJWT(contents: String) -> String? { - let matches = userConfigRE.matches(in: contents, options: [], range: NSRange(contents.startIndex..., in: contents)) + let matches = userConfigRE.matches( + in: contents, options: [], range: NSRange(contents.startIndex..., in: contents)) if let match = matches.first, let range = Range(match.range(at: 1), in: contents) { return String(contents[range]) } @@ -31,7 +35,8 @@ class JwtUtils { /// Parses a credentials file and returns its nkey. static func parseDecoratedNKey(contents: String) -> String? { - let matches = userConfigRE.matches(in: contents, options: [], range: NSRange(contents.startIndex..., in: contents)) + let matches = userConfigRE.matches( + in: contents, options: [], range: NSRange(contents.startIndex..., in: contents)) if matches.count > 1, let range = Range(matches[1].range(at: 1), in: contents) { return String(contents[range]) } diff --git a/Sources/NatsSwift/NatsProto.swift b/Sources/NatsSwift/NatsProto.swift index 9461be3..9c79402 100644 --- a/Sources/NatsSwift/NatsProto.swift +++ b/Sources/NatsSwift/NatsProto.swift @@ -7,25 +7,28 @@ import Foundation import NIO internal enum NatsOperation: String { - case connect = "CONNECT" - case subscribe = "SUB" - case unsubscribe = "UNSUB" - case publish = "PUB" - case hpublish = "HPUB" - case message = "MSG" - case hmessage = "HMSG" - case info = "INFO" - case ok = "+OK" - case error = "-ERR" - case ping = "PING" - case pong = "PONG" + case connect = "CONNECT" + case subscribe = "SUB" + case unsubscribe = "UNSUB" + case publish = "PUB" + case hpublish = "HPUB" + case message = "MSG" + case hmessage = "HMSG" + case info = "INFO" + case ok = "+OK" + case error = "-ERR" + case ping = "PING" + case pong = "PONG" var rawBytes: [UInt8] { return Array(self.rawValue.utf8) } static func allOperations() -> [NatsOperation] { - return [.connect, .subscribe, .unsubscribe, .publish, .message, .hmessage, .info, .ok, .error, .ping, .pong] + return [ + .connect, .subscribe, .unsubscribe, .publish, .message, .hmessage, .info, .ok, .error, + .ping, .pong, + ] } } @@ -37,10 +40,12 @@ enum ServerOp { case Error(NatsConnectionError) case Message(MessageInbound) case HMessage(HMessageInbound) - + static func parse(from message: Data) throws -> ServerOp { guard message.count > 2 else { - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "unable to parse inbound message: \(message)"]) + throw NSError( + domain: "nats_swift", code: 1, + userInfo: ["message": "unable to parse inbound message: \(message)"]) } let msgType = message.getMessageType() switch msgType { @@ -62,7 +67,9 @@ enum ServerOp { case .pong: return Pong default: - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "Unknown server op: \(message)"]) + throw NSError( + domain: "nats_swift", code: 1, + userInfo: ["message": "Unknown server op: \(message)"]) } } } @@ -81,16 +88,19 @@ internal struct HMessageInbound: Equatable { // Parse the operation syntax: HMSG [reply-to] internal static func parse(data: Data) throws -> HMessageInbound { - let protoComponents = data + let protoComponents = + data .dropFirst(NatsOperation.hmessage.rawValue.count) // Assuming the message starts with "HMSG " .split(separator: space) .filter { !$0.isEmpty } - - let parseArgs: ((Data, Data, Data?,Data, Data) throws -> HMessageInbound) = { subjectData, sidData, replyData, lengthHeaders, lengthData in + let parseArgs: ((Data, Data, Data?, Data, Data) throws -> HMessageInbound) = { + subjectData, sidData, replyData, lengthHeaders, lengthData in let subject = String(decoding: subjectData, as: UTF8.self) guard let sid = UInt64(String(decoding: sidData, as: UTF8.self)) else { - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "unable to parse subscription ID as number"]) + throw NSError( + domain: "nats_swift", code: 1, + userInfo: ["message": "unable to parse subscription ID as number"]) } var replySubject: String? = nil if let replyData = replyData { @@ -98,17 +108,25 @@ internal struct HMessageInbound: Equatable { } let headersLength = Int(String(decoding: lengthHeaders, as: UTF8.self)) ?? 0 let length = Int(String(decoding: lengthData, as: UTF8.self)) ?? 0 - return HMessageInbound(subject: subject, sid: sid, reply: replySubject, payload: nil, headers: HeaderMap(), headersLength: headersLength, length: length) + return HMessageInbound( + subject: subject, sid: sid, reply: replySubject, payload: nil, headers: HeaderMap(), + headersLength: headersLength, length: length) } var msg: HMessageInbound switch protoComponents.count { case 4: - msg = try parseArgs(protoComponents[0], protoComponents[1], nil, protoComponents[2], protoComponents[3]) + msg = try parseArgs( + protoComponents[0], protoComponents[1], nil, protoComponents[2], + protoComponents[3]) case 5: - msg = try parseArgs(protoComponents[0], protoComponents[1], protoComponents[2], protoComponents[3], protoComponents[4]) + msg = try parseArgs( + protoComponents[0], protoComponents[1], protoComponents[2], protoComponents[3], + protoComponents[4]) default: - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "unable to parse inbound message header"]) + throw NSError( + domain: "nats_swift", code: 1, + userInfo: ["message": "unable to parse inbound message header"]) } return msg } @@ -126,23 +144,27 @@ internal struct MessageInbound: Equatable { // Parse the operation syntax: MSG [reply-to] internal static func parse(data: Data) throws -> MessageInbound { - let protoComponents = data + let protoComponents = + data .dropFirst(NatsOperation.message.rawValue.count) // Assuming the message starts with "MSG " .split(separator: space) .filter { !$0.isEmpty } - - let parseArgs: ((Data, Data, Data?, Data) throws -> MessageInbound) = { subjectData, sidData, replyData, lengthData in + let parseArgs: ((Data, Data, Data?, Data) throws -> MessageInbound) = { + subjectData, sidData, replyData, lengthData in let subject = String(decoding: subjectData, as: UTF8.self) guard let sid = UInt64(String(decoding: sidData, as: UTF8.self)) else { - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "unable to parse subscription ID as number"]) + throw NSError( + domain: "nats_swift", code: 1, + userInfo: ["message": "unable to parse subscription ID as number"]) } var replySubject: String? = nil if let replyData = replyData { replySubject = String(decoding: replyData, as: UTF8.self) } let length = Int(String(decoding: lengthData, as: UTF8.self)) ?? 0 - return MessageInbound(subject: subject, sid: sid, reply: replySubject, payload: nil, length: length) + return MessageInbound( + subject: subject, sid: sid, reply: replySubject, payload: nil, length: length) } var msg: MessageInbound @@ -150,15 +172,17 @@ internal struct MessageInbound: Equatable { case 3: msg = try parseArgs(protoComponents[0], protoComponents[1], nil, protoComponents[2]) case 4: - msg = try parseArgs(protoComponents[0], protoComponents[1], protoComponents[2], protoComponents[3]) + msg = try parseArgs( + protoComponents[0], protoComponents[1], protoComponents[2], protoComponents[3]) default: - throw NSError(domain: "nats_swift", code: 1, userInfo: ["message": "unable to parse inbound message header"]) + throw NSError( + domain: "nats_swift", code: 1, + userInfo: ["message": "unable to parse inbound message header"]) } return msg } } - /// Struct representing server information in NATS. struct ServerInfo: Codable, Equatable { /// The unique identifier of the NATS server. @@ -222,7 +246,7 @@ struct ServerInfo: Codable, Equatable { } enum ClientOp { - case Publish((subject: String, reply: String?, payload: Data?, headers: HeaderMap? )) + case Publish((subject: String, reply: String?, payload: Data?, headers: HeaderMap?)) case Subscribe((sid: UInt64, subject: String, queue: String?)) case Unsubscribe((sid: UInt64, max: UInt64?)) case Connect(ConnectInfo) @@ -234,8 +258,10 @@ enum ClientOp { switch self { case let .Publish((subject, reply, payload, headers)): if let payload = payload { - buffer = allocator.buffer(capacity: payload.count + subject.utf8.count + NatsOperation.publish.rawValue.count + 12) - if headers != nil { + buffer = allocator.buffer( + capacity: payload.count + subject.utf8.count + + NatsOperation.publish.rawValue.count + 12) + if headers != nil { buffer.writeData(NatsOperation.hpublish.rawBytes) } else { buffer.writeData(NatsOperation.publish.rawBytes) @@ -253,12 +279,13 @@ enum ClientOp { buffer.writeString("\(headersLen) \(totalLen)\r\n") buffer.writeData(headers) } else { - buffer.writeString("\(payload.count)\r\n") + buffer.writeString("\(payload.count)\r\n") } buffer.writeData(payload) buffer.writeString("\r\n") } else { - buffer = allocator.buffer(capacity: subject.utf8.count + NatsOperation.publish.rawValue.count + 12) + buffer = allocator.buffer( + capacity: subject.utf8.count + NatsOperation.publish.rawValue.count + 12) buffer.writeData(NatsOperation.publish.rawBytes) buffer.writeString(" ") buffer.writeString(subject) @@ -271,7 +298,8 @@ enum ClientOp { case let .Subscribe((sid, subject, queue)): buffer = allocator.buffer(capacity: 0) if let queue { - buffer.writeString("\(NatsOperation.subscribe.rawValue) \(subject) \(queue) \(sid)\r\n") + buffer.writeString( + "\(NatsOperation.subscribe.rawValue) \(subject) \(queue) \(sid)\r\n") } else { buffer.writeString("\(NatsOperation.subscribe.rawValue) \(subject) \(sid)\r\n") } @@ -285,7 +313,7 @@ enum ClientOp { } case let .Connect(info): let json = try JSONEncoder().encode(info) - buffer = allocator.buffer(capacity: json.count+5) + buffer = allocator.buffer(capacity: json.count + 5) buffer.writeString("\(NatsOperation.connect.rawValue) ") buffer.writeData(json) buffer.writeString("\r\n") @@ -347,7 +375,7 @@ struct ConnectInfo: Encodable { case pedantic case userJwt = "jwt" case nkey - case signature = "sig" // Custom key name for JSON + case signature = "sig" // Custom key name for JSON case name case echo case lang diff --git a/Sources/NatsSwift/NatsSubscription.swift b/Sources/NatsSwift/NatsSubscription.swift index b51bb2d..4efed62 100644 --- a/Sources/NatsSwift/NatsSubscription.swift +++ b/Sources/NatsSwift/NatsSubscription.swift @@ -17,7 +17,7 @@ public class Subscription: AsyncSequence { private let lock = NSLock() public let subject: String - private static let defaultMaxPending: UInt64 = 512*1024 + private static let defaultMaxPending: UInt64 = 512 * 1024 convenience init(subject: String) { self.init(subject: subject, maxPending: Subscription.defaultMaxPending) @@ -65,7 +65,7 @@ public class Subscription: AsyncSequence { await subscription.nextMessage() } } - + private func nextMessage() async -> Element? { await withCheckedContinuation { continuation in lock.withLock { diff --git a/Tests/LinuxMain.swift b/Tests/LinuxMain.swift index 2b3f553..91c044e 100644 --- a/Tests/LinuxMain.swift +++ b/Tests/LinuxMain.swift @@ -3,11 +3,11 @@ // NatsSwift // - import XCTest + @testable import NatsSwiftTests XCTMain([ testCase(StringExtensionTests.allTests), - testCase(NatsMessageTests.allTests) + testCase(NatsMessageTests.allTests), ]) diff --git a/Tests/NatsSwiftTests/Integration/ConnectionTests.swift b/Tests/NatsSwiftTests/Integration/ConnectionTests.swift index 0db8fd5..7acb0e7 100755 --- a/Tests/NatsSwiftTests/Integration/ConnectionTests.swift +++ b/Tests/NatsSwiftTests/Integration/ConnectionTests.swift @@ -3,9 +3,10 @@ // NatsSwiftTests // -import XCTest -import NIO import Logging +import NIO +import XCTest + @testable import NatsSwift class CoreNatsTests: XCTestCase { @@ -17,7 +18,7 @@ class CoreNatsTests: XCTestCase { ("testConnect", testConnect), ("testReconnect", testReconnect), ("testUsernameAndPassword", testUsernameAndPassword), - ("testTokenAuth", testTokenAuth) + ("testTokenAuth", testTokenAuth), ] var natsServer = NatsServer() @@ -69,20 +70,20 @@ class CoreNatsTests: XCTestCase { } await fulfillment(of: [expectation], timeout: 5.0) await sub.complete() - } - - func testSubscribe() async throws { - natsServer.start() - logger.logLevel = .debug - let client = ClientOptions().url(URL(string: natsServer.clientURL)!).build() - try await client.connect() - let sub = try await client.subscribe(to: "test") - try client.publish("msg".data(using: .utf8)!, subject: "test") - let iter = sub.makeAsyncIterator() - let message = await iter.next() - print( "payload: \(String(data:message!.payload!, encoding: .utf8)!)") - XCTAssertEqual(message?.payload, "msg".data(using: .utf8)!) - } + } + + func testSubscribe() async throws { + natsServer.start() + logger.logLevel = .debug + let client = ClientOptions().url(URL(string: natsServer.clientURL)!).build() + try await client.connect() + let sub = try await client.subscribe(to: "test") + try client.publish("msg".data(using: .utf8)!, subject: "test") + let iter = sub.makeAsyncIterator() + let message = await iter.next() + print("payload: \(String(data:message!.payload!, encoding: .utf8)!)") + XCTAssertEqual(message?.payload, "msg".data(using: .utf8)!) + } func testConnect() async throws { natsServer.start() @@ -106,7 +107,6 @@ class CoreNatsTests: XCTestCase { try await client.connect() - // Payload to publish let payload = "hello".data(using: .utf8)! @@ -140,7 +140,7 @@ class CoreNatsTests: XCTestCase { try client.publish(payload, subject: "foo") } } - + for await _ in sub { messagesReceived += 1 if messagesReceived == 20 { @@ -159,11 +159,12 @@ class CoreNatsTests: XCTestCase { // Navigate up to the Tests directory let testsDir = currentFile.deletingLastPathComponent().deletingLastPathComponent() // Construct the path to the resource - let resourceURL = testsDir + let resourceURL = + testsDir .appendingPathComponent("Integration/Resources/creds.conf", isDirectory: false) natsServer.start(cfg: resourceURL.path) let client = ClientOptions() - .url(URL(string:natsServer.clientURL)!) + .url(URL(string: natsServer.clientURL)!) .username_and_password("derek", "s3cr3t") .maxReconnects(5) .build() @@ -173,10 +174,9 @@ class CoreNatsTests: XCTestCase { _ = try await client.subscribe(to: "test") XCTAssertNotNil(client, "Client should not be nil") - // Test if client with bad credentials throws an error let bad_creds_client = ClientOptions() - .url(URL(string:natsServer.clientURL)!) + .url(URL(string: natsServer.clientURL)!) .username_and_password("derek", "badpassword") .maxReconnects(5) .build() @@ -196,11 +196,12 @@ class CoreNatsTests: XCTestCase { // Navigate up to the Tests directory let testsDir = currentFile.deletingLastPathComponent().deletingLastPathComponent() // Construct the path to the resource - let resourceURL = testsDir + let resourceURL = + testsDir .appendingPathComponent("Integration/Resources/token.conf", isDirectory: false) natsServer.start(cfg: resourceURL.path) let client = ClientOptions() - .url(URL(string:natsServer.clientURL)!) + .url(URL(string: natsServer.clientURL)!) .token("s3cr3t") .maxReconnects(5) .build() @@ -210,10 +211,9 @@ class CoreNatsTests: XCTestCase { _ = try await client.subscribe(to: "test") XCTAssertNotNil(client, "Client should not be nil") - // Test if client with bad credentials throws an error let bad_creds_client = ClientOptions() - .url(URL(string:natsServer.clientURL)!) + .url(URL(string: natsServer.clientURL)!) .token("badtoken") .maxReconnects(5) .build() @@ -232,19 +232,22 @@ class CoreNatsTests: XCTestCase { // Navigate up to the Tests directory let testsDir = currentFile.deletingLastPathComponent().deletingLastPathComponent() // Construct the path to the resource - let resourceURL = testsDir + let resourceURL = + testsDir .appendingPathComponent("Integration/Resources/jwt.conf", isDirectory: false) natsServer.start(cfg: resourceURL.path) - let credsURL = testsDir.appendingPathComponent("Integration/Resources/TestUser.creds", isDirectory: false) + let credsURL = testsDir.appendingPathComponent( + "Integration/Resources/TestUser.creds", isDirectory: false) - let client = ClientOptions().url(URL(string:natsServer.clientURL)!).credentials_file(credsURL).build() + let client = ClientOptions().url(URL(string: natsServer.clientURL)!).credentials_file( + credsURL + ).build() try await client.connect() let subscribe = try await client.subscribe(to: "foo").makeAsyncIterator() - try client.publish("data".data(using: .utf8)!, subject: "foo") + try client.publish("data".data(using: .utf8)!, subject: "foo") let message = await subscribe.next() print("message: \(message!.subject)") } } - diff --git a/Tests/NatsSwiftTests/Integration/MessageWithHeadersTests.swift b/Tests/NatsSwiftTests/Integration/MessageWithHeadersTests.swift index bfe20af..18bf1e5 100644 --- a/Tests/NatsSwiftTests/Integration/MessageWithHeadersTests.swift +++ b/Tests/NatsSwiftTests/Integration/MessageWithHeadersTests.swift @@ -1,6 +1,7 @@ import Foundation import Logging import XCTest + @testable import NatsSwift class TestMessageWithHeadersTests: XCTestCase { @@ -16,7 +17,7 @@ class TestMessageWithHeadersTests: XCTestCase { natsServer.stop() } - func testMessageWithHeaders() async throws { + func testMessageWithHeaders() async throws { natsServer.start() logger.logLevel = .debug @@ -33,7 +34,7 @@ class TestMessageWithHeadersTests: XCTestCase { try client.publish("hello".data(using: .utf8)!, subject: "foo", reply: nil, headers: hm) let iter = sub.makeAsyncIterator() - let msg = await iter.next() + let msg = await iter.next() XCTAssertEqual(msg!.headers, hm) } diff --git a/Tests/NatsSwiftTests/NatsServer.swift b/Tests/NatsSwiftTests/NatsServer.swift index cc4ab56..868fba7 100644 --- a/Tests/NatsSwiftTests/NatsServer.swift +++ b/Tests/NatsSwiftTests/NatsServer.swift @@ -16,17 +16,18 @@ class NatsServer { return "" } } - + private var process: Process? private var natsServerPort: Int? private var tlsEnabled = false - + // TODO: When implementing JetStream, creating and deleting store dir should be handled in start/stop methods func start(port: Int = -1, cfg: String? = nil, file: StaticString = #file, line: UInt = #line) { - XCTAssertNil(self.process, "nats-server is already running on port \(port)", file: file, line: line) + XCTAssertNil( + self.process, "nats-server is already running on port \(port)", file: file, line: line) let process = Process() let pipe = Pipe() - + process.executableURL = URL(fileURLWithPath: "/usr/bin/env") process.arguments = ["nats-server", "-p", "\(port)"] if let cfg { @@ -34,14 +35,14 @@ class NatsServer { } process.standardError = pipe process.standardOutput = pipe - + let outputHandle = pipe.fileHandleForReading let semaphore = DispatchSemaphore(value: 0) var lineCount = 0 let maxLines = 100 var serverPort: Int? var serverError: String? - + outputHandle.readabilityHandler = { fileHandle in let data = fileHandle.availableData if let line = String(data: data, encoding: .utf8) { @@ -59,34 +60,37 @@ class NatsServer { } } } - - XCTAssertNoThrow(try process.run(), "error starting nats-server on port \(port)", file: file, line: line) - + + XCTAssertNoThrow( + try process.run(), "error starting nats-server on port \(port)", file: file, line: line) + let result = semaphore.wait(timeout: .now() + .seconds(10)) - - XCTAssertFalse(result == .timedOut, "timeout waiting for server to be ready", file: file, line: line) - XCTAssertNil(serverError, "error starting nats-server: \(serverError!)", file: file, line: line) - + + XCTAssertFalse( + result == .timedOut, "timeout waiting for server to be ready", file: file, line: line) + XCTAssertNil( + serverError, "error starting nats-server: \(serverError!)", file: file, line: line) + self.process = process self.natsServerPort = serverPort } - + func stop(file: StaticString = #file, line: UInt = #line) { XCTAssertNotNil(self.process, "nats-server is not running", file: file, line: line) - + self.process?.terminate() process?.waitUntilExit() process = nil natsServerPort = port tlsEnabled = false } - + private func extractPort(from string: String) -> Int? { let pattern = "Listening for client connections on [^:]+:(\\d+)" - + let regex = try! NSRegularExpression(pattern: pattern) let nsrange = NSRange(string.startIndex.. String? { if logLine.contains("nats-server: No such file or directory") { return "nats-server not found - make sure nats-server can be found in PATH" @@ -111,12 +115,12 @@ class NatsServer { return String(message).trimmingCharacters(in: .whitespaces) } - + private func isTLS(from logLine: String) -> Bool { return logLine.contains("TLS required for client connections") } - - deinit{ + + deinit { stop() } } diff --git a/Tests/NatsSwiftTests/Unit/HeadersTests.swift b/Tests/NatsSwiftTests/Unit/HeadersTests.swift index c77fbe5..5ee8732 100644 --- a/Tests/NatsSwiftTests/Unit/HeadersTests.swift +++ b/Tests/NatsSwiftTests/Unit/HeadersTests.swift @@ -1,5 +1,5 @@ - import XCTest + @testable import NatsSwift class HeadersTests: XCTestCase { @@ -12,7 +12,7 @@ class HeadersTests: XCTestCase { ("testValidHeaderName", testValidHeaderName), ("testDollarHeaderName", testDollarHeaderName), ("testInvalidHeaderName", testInvalidHeaderName), - ("testInvalidHeaderNameWithSpecialCharacters", testInvalidHeaderNameWithSpecialCharacters) + ("testInvalidHeaderNameWithSpecialCharacters", testInvalidHeaderNameWithSpecialCharacters), ] @@ -57,22 +57,22 @@ class HeadersTests: XCTestCase { XCTAssertThrowsError(try HeaderName("Invalid:Header:Name")) } -func testSubscript() { - var hm = HeaderMap() + func testSubscript() { + var hm = HeaderMap() - // Test setting a value - hm[try! HeaderName("foo")] = HeaderValue("bar") - XCTAssertEqual(hm[try! HeaderName("foo")], HeaderValue("bar")) + // Test setting a value + hm[try! HeaderName("foo")] = HeaderValue("bar") + XCTAssertEqual(hm[try! HeaderName("foo")], HeaderValue("bar")) - // Test updating existing value - hm[try! HeaderName("foo")] = HeaderValue("baz") - XCTAssertEqual(hm[try! HeaderName("foo")], HeaderValue("baz")) + // Test updating existing value + hm[try! HeaderName("foo")] = HeaderValue("baz") + XCTAssertEqual(hm[try! HeaderName("foo")], HeaderValue("baz")) - // Test retrieving non-existing value (should be nil or default) - XCTAssertNil(hm[try! HeaderName("non-existing")]) + // Test retrieving non-existing value (should be nil or default) + XCTAssertNil(hm[try! HeaderName("non-existing")]) - // Test removal of a value - hm[try! HeaderName("foo")] = nil - XCTAssertNil(hm[try! HeaderName("foo")]) + // Test removal of a value + hm[try! HeaderName("foo")] = nil + XCTAssertNil(hm[try! HeaderName("foo")]) } } diff --git a/Tests/NatsSwiftTests/Unit/JwtTests.swift b/Tests/NatsSwiftTests/Unit/JwtTests.swift index c380d6d..936e1a5 100644 --- a/Tests/NatsSwiftTests/Unit/JwtTests.swift +++ b/Tests/NatsSwiftTests/Unit/JwtTests.swift @@ -1,11 +1,12 @@ -import XCTest import Foundation +import XCTest + @testable import NatsSwift class JwtTests: XCTestCase { static var allTests = [ - ("testParseCredentialsFile", testParseCredentialsFile), + ("testParseCredentialsFile", testParseCredentialsFile) ] func testParseCredentialsFile() async throws { @@ -18,12 +19,12 @@ class JwtTests: XCTestCase { let nkey = String(data: JwtUtils.parseDecoratedNKey(contents: credsData)!, encoding: .utf8) let expectedNkey = "SUACH75SWCM5D2JMJM6EKLR2WDARVGZT4QC6LX3AGHSWOMVAKERABBBRWM" - XCTAssertEqual(nkey,expectedNkey) + XCTAssertEqual(nkey, expectedNkey) let jwt = String(data: JwtUtils.parseDecoratedJWT(contents: credsData)!, encoding: .utf8) - let expectedJWT = "eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJMN1dBT1hJU0tPSUZNM1QyNEhMQ09ENzJRT1czQkNVWEdETjRKVU1SSUtHTlQ3RzdZVFRRIiwiaWF0IjoxNjUxNzkwOTgyLCJpc3MiOiJBRFRRUzdaQ0ZWSk5XNTcyNkdPWVhXNVRTQ1pGTklRU0hLMlpHWVVCQ0Q1RDc3T1ROTE9PS1pPWiIsIm5hbWUiOiJUZXN0VXNlciIsInN1YiI6IlVBRkhHNkZVRDJVVTRTREZWQUZVTDVMREZPMlhNNFdZTTc2VU5YVFBKWUpLN0VFTVlSQkhUMlZFIiwibmF0cyI6eyJwdWIiOnt9LCJzdWIiOnt9LCJzdWJzIjotMSwiZGF0YSI6LTEsInBheWxvYWQiOi0xLCJ0eXBlIjoidXNlciIsInZlcnNpb24iOjJ9fQ.bp2-Jsy33l4ayF7Ku1MNdJby4WiMKUrG-rSVYGBusAtV3xP4EdCa-zhSNUaBVIL3uYPPCQYCEoM1pCUdOnoJBg" + let expectedJWT = + "eyJ0eXAiOiJKV1QiLCJhbGciOiJlZDI1NTE5LW5rZXkifQ.eyJqdGkiOiJMN1dBT1hJU0tPSUZNM1QyNEhMQ09ENzJRT1czQkNVWEdETjRKVU1SSUtHTlQ3RzdZVFRRIiwiaWF0IjoxNjUxNzkwOTgyLCJpc3MiOiJBRFRRUzdaQ0ZWSk5XNTcyNkdPWVhXNVRTQ1pGTklRU0hLMlpHWVVCQ0Q1RDc3T1ROTE9PS1pPWiIsIm5hbWUiOiJUZXN0VXNlciIsInN1YiI6IlVBRkhHNkZVRDJVVTRTREZWQUZVTDVMREZPMlhNNFdZTTc2VU5YVFBKWUpLN0VFTVlSQkhUMlZFIiwibmF0cyI6eyJwdWIiOnt9LCJzdWIiOnt9LCJzdWJzIjotMSwiZGF0YSI6LTEsInBheWxvYWQiOi0xLCJ0eXBlIjoidXNlciIsInZlcnNpb24iOjJ9fQ.bp2-Jsy33l4ayF7Ku1MNdJby4WiMKUrG-rSVYGBusAtV3xP4EdCa-zhSNUaBVIL3uYPPCQYCEoM1pCUdOnoJBg" - XCTAssertEqual(jwt,expectedJWT) + XCTAssertEqual(jwt, expectedJWT) } } - diff --git a/Tests/NatsSwiftTests/Unit/ParserTests.swift b/Tests/NatsSwiftTests/Unit/ParserTests.swift index 2a096bc..bc0904c 100644 --- a/Tests/NatsSwiftTests/Unit/ParserTests.swift +++ b/Tests/NatsSwiftTests/Unit/ParserTests.swift @@ -4,6 +4,7 @@ // import XCTest + @testable import NatsSwift class ParserTests: XCTestCase { @@ -36,9 +37,12 @@ class ParserTests: XCTestCase { name: "Single chunk, different operations", givenChunks: ["MSG foo 1 5\r\nhello\r\n+OK\r\nPONG\r\n"], expectedOps: [ - .Message(MessageInbound(subject: "foo", sid: 1, payload: "hello".data(using: .utf8)!, length: 5)), + .Message( + MessageInbound( + subject: "foo", sid: 1, payload: "hello".data(using: .utf8)!, length: 5) + ), .Ok, - .Pong + .Pong, ] ), TestCase( @@ -49,16 +53,37 @@ class ParserTests: XCTestCase { "oo 1 5\r\nhello\r\nMSG foo 1 5\r\nworld", "\r\nMSG foo 1 5\r\nhello\r", "\nMSG foo 1 5\r\nworld\r\nMSG foo 1 5\r\n", - "hello\r\n" + "hello\r\n", ], expectedOps: [ - .Message(MessageInbound(subject: "foo", sid: 1, payload: "hello".data(using: .utf8)!, length: 5)), - .Message(MessageInbound(subject: "foo", sid: 1, payload: "world".data(using: .utf8)!, length: 5)), - .Message(MessageInbound(subject: "foo", sid: 1, payload: "hello".data(using: .utf8)!, length: 5)), - .Message(MessageInbound(subject: "foo", sid: 1, payload: "world".data(using: .utf8)!, length: 5)), - .Message(MessageInbound(subject: "foo", sid: 1, payload: "hello".data(using: .utf8)!, length: 5)), - .Message(MessageInbound(subject: "foo", sid: 1, payload: "world".data(using: .utf8)!, length: 5)), - .Message(MessageInbound(subject: "foo", sid: 1, payload: "hello".data(using: .utf8)!, length: 5)), + .Message( + MessageInbound( + subject: "foo", sid: 1, payload: "hello".data(using: .utf8)!, length: 5) + ), + .Message( + MessageInbound( + subject: "foo", sid: 1, payload: "world".data(using: .utf8)!, length: 5) + ), + .Message( + MessageInbound( + subject: "foo", sid: 1, payload: "hello".data(using: .utf8)!, length: 5) + ), + .Message( + MessageInbound( + subject: "foo", sid: 1, payload: "world".data(using: .utf8)!, length: 5) + ), + .Message( + MessageInbound( + subject: "foo", sid: 1, payload: "hello".data(using: .utf8)!, length: 5) + ), + .Message( + MessageInbound( + subject: "foo", sid: 1, payload: "world".data(using: .utf8)!, length: 5) + ), + .Message( + MessageInbound( + subject: "foo", sid: 1, payload: "hello".data(using: .utf8)!, length: 5) + ), ] ), TestCase( @@ -68,23 +93,34 @@ class ParserTests: XCTestCase { "", "", "rld\r\nMSG f", - "oo 1 5\r\nhello\r\n" + "oo 1 5\r\nhello\r\n", ], expectedOps: [ - .Message(MessageInbound(subject: "foo", sid: 1, payload: "hello".data(using: .utf8)!, length: 5)), - .Message(MessageInbound(subject: "foo", sid: 1, payload: "world".data(using: .utf8)!, length: 5)), - .Message(MessageInbound(subject: "foo", sid: 1, payload: "hello".data(using: .utf8)!, length: 5)), + .Message( + MessageInbound( + subject: "foo", sid: 1, payload: "hello".data(using: .utf8)!, length: 5) + ), + .Message( + MessageInbound( + subject: "foo", sid: 1, payload: "world".data(using: .utf8)!, length: 5) + ), + .Message( + MessageInbound( + subject: "foo", sid: 1, payload: "hello".data(using: .utf8)!, length: 5) + ), ] ), TestCase( name: "With crlf in payload", givenChunks: [ - "MSG foo 1 7\r\nhe\r\nllo\r\n", + "MSG foo 1 7\r\nhe\r\nllo\r\n" ], expectedOps: [ - .Message(MessageInbound(subject: "foo", sid: 1, payload: Data("he\r\nllo".utf8), length: 7)) + .Message( + MessageInbound( + subject: "foo", sid: 1, payload: Data("he\r\nllo".utf8), length: 7)) ] - ) + ), ] for (tn, tc) in testCases.enumerated() { @@ -104,7 +140,8 @@ class ParserTests: XCTestCase { for (i, op) in ops.enumerated() { switch op { case .Ok: - if case .Ok = tc.expectedOps[i] {} else { + if case .Ok = tc.expectedOps[i] { + } else { XCTFail(fail(tn, tc.name)) } case .Info(let info): @@ -115,15 +152,18 @@ class ParserTests: XCTestCase { } case .Ping: - if case .Ping = tc.expectedOps[i] {} else { + if case .Ping = tc.expectedOps[i] { + } else { XCTFail(fail(tn, tc.name)) } case .Pong: - if case .Pong = tc.expectedOps[i] {} else { + if case .Pong = tc.expectedOps[i] { + } else { XCTFail(fail(tn, tc.name)) } case .Error(_): - if case .Error(_) = tc.expectedOps[i] {} else { + if case .Error(_) = tc.expectedOps[i] { + } else { XCTFail(fail(tn, tc.name)) } case .Message(let msg):