Skip to content

Commit

Permalink
Add nats-server wrapper for tests
Browse files Browse the repository at this point in the history
Signed-off-by: Piotr Piotrowski <piotr@synadia.com>
  • Loading branch information
piotrpio committed Dec 1, 2023
1 parent 261daa7 commit c54414c
Show file tree
Hide file tree
Showing 9 changed files with 208 additions and 45 deletions.
2 changes: 1 addition & 1 deletion Sources/NatsSwift/NatsClient/NatsClient.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public enum NatsEvent: String {
case error = "error"
case dropped = "dropped"
case reconnecting = "reconnecting"
case informed = "informed"
case informed = "informed"
static let all = [ connected, disconnected, response, error, dropped, reconnecting ]
}

Expand Down
24 changes: 19 additions & 5 deletions Tests/NatsSwiftTests/Integration/ConnectionTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,26 +14,37 @@ class ConnectionTests: XCTestCase {
("testClientServerSetWhenConnected", testClientServerSetWhenConnected),
("testClientBadConnection", testClientBadConnection)
]

var natsServer = NatsServer()

override func tearDown() {
super.tearDown()
natsServer.stop()
}

func testClientConnection() {
natsServer.start()

let client = NatsClient(TestSettings.natsUrl)
let client = NatsClient(natsServer.clientURL)

try? client.connect()

XCTAssertTrue(client.state == .connected, "Client did not connect")

}

func testClientServerSetWhenConnected() {
natsServer.start()

let client = NatsClient(TestSettings.natsUrl)
let client = NatsClient(natsServer.clientURL)

try? client.connect()
guard let _ = client.server else { XCTFail("Client did not connect to server correctly"); return }

}

func testClientBadConnection() {
natsServer.start()

let client = NatsClient("notnats.net")

Expand All @@ -43,16 +54,18 @@ class ConnectionTests: XCTestCase {
}

func testClientConnectionLogging() {
natsServer.start()

let client = NatsClient(TestSettings.natsUrl)
let client = NatsClient(natsServer.clientURL)
client.config.loglevel = .trace
try? client.connect()
XCTAssertTrue(client.state == .connected, "Client did not connect")

}

func testClientConnectDisconnect() {
let client = NatsClient(TestSettings.natsUrl)
natsServer.start()
let client = NatsClient(natsServer.clientURL)
client.config.loglevel = .trace

try? client.connect()
Expand All @@ -77,7 +90,8 @@ class ConnectionTests: XCTestCase {
}

func testClientReconnectWhenAlreadyConnected() {
let client = NatsClient(TestSettings.natsUrl)
natsServer.start()
let client = NatsClient(natsServer.clientURL)
client.config.loglevel = .trace

try? client.connect()
Expand Down
29 changes: 19 additions & 10 deletions Tests/NatsSwiftTests/Integration/EventBusTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@ import XCTest

class EventBusTests: XCTestCase {

func testClientConnectedEvent() {
var natsServer = NatsServer()

let client = NatsClient(TestSettings.natsUrl)
override func tearDown() {
super.tearDown()
natsServer.stop()
}

func testClientConnectedEvent() {
natsServer.start()
let client = NatsClient(natsServer.clientURL)

var isConnected = false
client.on(.connected) { _ in
isConnected = true
Expand All @@ -26,8 +33,9 @@ class EventBusTests: XCTestCase {
}

func testClientDisconnectedEvent() {
natsServer.start()
let client = NatsClient(natsServer.clientURL)

let client = NatsClient(TestSettings.natsUrl)
try? client.connect()

var isConnected = true
Expand All @@ -42,8 +50,9 @@ class EventBusTests: XCTestCase {
}

func testClientEventOff() {
natsServer.start()
let client = NatsClient(natsServer.clientURL)

let client = NatsClient(TestSettings.natsUrl)
try? client.connect()

var isConnected = true
Expand All @@ -60,9 +69,9 @@ class EventBusTests: XCTestCase {
}

func testClientEventMultiple() {

let client = NatsClient(TestSettings.natsUrl)

natsServer.start()
let client = NatsClient(natsServer.clientURL)
var counter = 0
client.on([.connected, .disconnected]) { _ in
counter += 1
Expand All @@ -75,9 +84,9 @@ class EventBusTests: XCTestCase {
}

func testClientEventAutoOff() {

let client = NatsClient(TestSettings.natsUrl)

natsServer.start()
let client = NatsClient(natsServer.clientURL)
var counter = 0
client.on([.connected, .disconnected], autoOff: true) { _ in
counter += 1
Expand Down
13 changes: 10 additions & 3 deletions Tests/NatsSwiftTests/Integration/LoopTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@ import XCTest
@testable import NatsSwift

class LoopTests: XCTestCase {
var natsServer = NatsServer()

func testReadSubscriptionInLoop() {
override func tearDown() {
super.tearDown()
natsServer.stop()
}

let clientPublish = NatsClient(TestSettings.natsUrl)
let clientSubscribe = NatsClient(TestSettings.natsUrl)
func testReadSubscriptionInLoop() {
natsServer.start()

let clientPublish = NatsClient(natsServer.clientURL)
let clientSubscribe = NatsClient(natsServer.clientURL)

try? clientPublish.connect()
try? clientSubscribe.connect()

Expand Down
18 changes: 12 additions & 6 deletions Tests/NatsSwiftTests/Integration/PublishTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,17 @@ import XCTest
@testable import NatsSwift

class PublishTests: XCTestCase {
var natsServer = NatsServer()

func testClientPublish() {

let client = NatsClient(TestSettings.natsUrl)
override func tearDown() {
super.tearDown()
natsServer.stop()
}

func testClientPublish() {
natsServer.start()
let client = NatsClient(natsServer.clientURL)

try? client.connect()

var responses = [String]()
Expand All @@ -35,9 +41,9 @@ class PublishTests: XCTestCase {
}

func testClientPublishSync() {

let client = NatsClient(TestSettings.natsUrl)

natsServer.start()
let client = NatsClient(natsServer.clientURL)
try? client.connect()

var responses = [String]()
Expand Down
17 changes: 13 additions & 4 deletions Tests/NatsSwiftTests/Integration/ServerInformationTest.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,17 @@ import XCTest
@testable import NatsSwift

class ServerInformationTest: XCTestCase {
var natsServer = NatsServer()

override func tearDown() {
super.tearDown()
natsServer.stop()
}

func testServerInformation() {
natsServer.start()
let client = NatsClient(natsServer.clientURL)

let client = NatsClient(TestSettings.natsUrl)
XCTAssertNil(client.serverInformation)

try? client.connect()
Expand All @@ -22,11 +29,13 @@ class ServerInformationTest: XCTestCase {
}

func testServerInformationPropertiesSet() {
let client = NatsClient(TestSettings.natsUrl)
natsServer.start()
let client = NatsClient(natsServer.clientURL)

try? client.connect()
XCTAssertEqual(client.serverInformation?.host, "0.0.0.0")
XCTAssertEqual(client.serverInformation?.port, 4222)

XCTAssertEqual(client.serverInformation?.port, natsServer.port!)
XCTAssert(client.serverInformation?.serverName.count ?? 0 > 0)
XCTAssert(client.serverInformation?.serverId.count ?? 0 > 0)
XCTAssert(client.serverInformation?.version.count ?? 0 >= 5)
Expand Down
18 changes: 12 additions & 6 deletions Tests/NatsSwiftTests/Integration/SubscribeTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,17 @@ import XCTest
@testable import NatsSwift

class SubscribeTests: XCTestCase {
var natsServer = NatsServer()

func testClientSubscription() {

let client = NatsClient(TestSettings.natsUrl)
override func tearDown() {
super.tearDown()
natsServer.stop()
}

func testClientSubscription() {
natsServer.start()
let client = NatsClient(natsServer.clientURL)

try? client.connect()

var subscribed = false
Expand All @@ -29,9 +35,9 @@ class SubscribeTests: XCTestCase {
}

func testClientSubscriptionSync() {

let client = NatsClient(TestSettings.natsUrl)

natsServer.start()
let client = NatsClient(natsServer.clientURL)
try? client.connect()

let handler: (NatsMessage) -> Void = { message in
Expand Down
122 changes: 122 additions & 0 deletions Tests/NatsSwiftTests/NatsServer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,122 @@
//
// NatsServer.swift
// NatsSwift
//

import Foundation
import XCTest

class NatsServer {
var port: UInt? { return natsServerPort }
var clientURL: String {
let scheme = tlsEnabled ? "tls://" : "nats://"
if let natsServerPort {
return "\(scheme)localhost:\(natsServerPort)"
} else {
return ""
}
}

private var process: Process?
private var natsServerPort: UInt?
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)
let process = Process()
let pipe = Pipe()

process.executableURL = URL(fileURLWithPath: "/usr/bin/env")
process.arguments = ["nats-server", "-p", "\(port)"]
if let cfg {
process.arguments?.append(contentsOf: ["-c", cfg])
}
process.standardError = pipe
process.standardOutput = pipe

let outputHandle = pipe.fileHandleForReading
let semaphore = DispatchSemaphore(value: 0)
var lineCount = 0
let maxLines = 100
var serverPort: UInt?
var serverError: String?

outputHandle.readabilityHandler = { fileHandle in
let data = fileHandle.availableData
if let line = String(data: data, encoding: .utf8) {
lineCount += 1

serverError = self.extracErrorMessage(from: line)
serverPort = self.extractPort(from: line)
if !self.tlsEnabled && self.isTLS(from: line) {
self.tlsEnabled = true
}
if serverPort != nil || serverError != nil || lineCount >= maxLines {
serverError = serverError
semaphore.signal()
outputHandle.readabilityHandler = nil
}
}
}

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)

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) -> UInt? {
let pattern = "Listening for client connections on [^:]+:(\\d+)"

let regex = try! NSRegularExpression(pattern: pattern)
let nsrange = NSRange(string.startIndex..<string.endIndex, in: string)

if let match = regex.firstMatch(in: string, options: [], range: nsrange) {
let portRange = match.range(at: 1)
if let swiftRange = Range(portRange, in: string) {
let portString = String(string[swiftRange])
return UInt(portString)
}
}

return nil
}

private func extracErrorMessage(from logLine: String) -> 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"
}
guard let range = logLine.range(of: "[FTL]") else {
return nil
}

let messageStartIndex = range.upperBound
let message = logLine[messageStartIndex...]

return String(message).trimmingCharacters(in: .whitespaces)
}

private func isTLS(from logLine: String) -> Bool {
return logLine.contains("TLS required for client connections")
}

deinit{
stop()
}
}

0 comments on commit c54414c

Please sign in to comment.