From fbff23bd258d2a9817e18db4d97602885ceb710d Mon Sep 17 00:00:00 2001 From: Mark Lilback Date: Thu, 17 Aug 2017 16:00:11 -0400 Subject: [PATCH 01/11] added Connection.makeListenDispatchSource func that uses a DispatchSource to monitor for notifications instead of sleep --- Sources/PostgreSQL/Connection.swift | 31 +++++++++++++ Tests/PostgreSQLTests/PostgreSQLTests.swift | 51 +++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/Sources/PostgreSQL/Connection.swift b/Sources/PostgreSQL/Connection.swift index a95dda8..be22bd8 100644 --- a/Sources/PostgreSQL/Connection.swift +++ b/Sources/PostgreSQL/Connection.swift @@ -171,6 +171,37 @@ public final class Connection: ConnInfoInitializable { } } + /// Creates a dispatch read source for this connection that will call `callback` on `queue` when a notification is received + /// + /// - Parameter channel: the channel to register for + /// - Parameter queue: the queue to create the DispatchSource on + /// - Parameter callback: the callback + /// - Parameter note: The notification received from the database + /// - Parameter err: Any error while reading the notification. If not nil, the source will have been canceled + /// - Returns: the dispatch socket to activate + /// - Throws: if fails to get the socket for the connection + public func makeListenDispatchSource(toChannel channel: String, queue: DispatchQueue, callback: @escaping (_ note: Notification?, _ err: Error?) -> Void) throws -> DispatchSourceRead { + guard let sock = Optional.some(PQsocket(self.cConnection)), sock >= 0 + else { throw PostgreSQLError(code: .ioError, reason: "failed to get socket for connection") } + let src = DispatchSource.makeReadSource(fileDescriptor: sock, queue: queue) + src.setEventHandler { [unowned self] in + do { + try self.validateConnection() + PQconsumeInput(self.cConnection) + while let pgNotify = PQnotifies(self.cConnection) { + let notification = Notification(pgNotify: pgNotify.pointee) + callback(notification, nil) + PQfreemem(pgNotify) + } + } catch { + callback(nil, error) + src.cancel() + } + } + try self.execute("LISTEN \(channel)") + return src + } + /// Registers as a listener on a specific notification channel. /// /// - Parameters: diff --git a/Tests/PostgreSQLTests/PostgreSQLTests.swift b/Tests/PostgreSQLTests/PostgreSQLTests.swift index 55d35a2..0bed4ba 100644 --- a/Tests/PostgreSQLTests/PostgreSQLTests.swift +++ b/Tests/PostgreSQLTests/PostgreSQLTests.swift @@ -779,6 +779,31 @@ class PostgreSQLTests: XCTestCase { waitForExpectations(timeout: 5) } + func testDispatchNotification() throws { + let conn1 = try postgreSQL.makeConnection() + let conn2 = try postgreSQL.makeConnection() + + let testExpectation = expectation(description: "Receive notification") + + let queue = DispatchQueue.global() + var source: DispatchSourceRead? + source = try! conn1.makeListenDispatchSource(toChannel: "test_channel1", queue: queue) { (notification, error) in + XCTAssertEqual(notification?.channel, "test_channel1") + XCTAssertNil(notification?.payload) + XCTAssertNil(error) + + testExpectation.fulfill() + source?.cancel() + } + source?.resume() + + sleep(1) + + try conn2.notify(channel: "test_channel1", payload: nil) + + waitForExpectations(timeout: 5) + } + func testNotificationWithPayload() throws { let conn1 = try postgreSQL.makeConnection() let conn2 = try postgreSQL.makeConnection() @@ -801,6 +826,32 @@ class PostgreSQLTests: XCTestCase { waitForExpectations(timeout: 5) } + func testDispatchNotificationWithPayload() throws { + let conn1 = try postgreSQL.makeConnection() + let conn2 = try postgreSQL.makeConnection() + + let testExpectation = expectation(description: "Receive notification with payload") + + let queue = DispatchQueue.global() + var source: DispatchSourceRead? + source = try! conn1.makeListenDispatchSource(toChannel: "test_channel2", queue: queue) { (notification, error) in + XCTAssertEqual(notification?.channel, "test_channel1") + XCTAssertEqual(notification?.payload, "test_payload") + XCTAssertNil(notification?.payload) + XCTAssertNil(error) + + testExpectation.fulfill() + source?.cancel() + } + source?.resume() + + sleep(1) + + try conn2.notify(channel: "test_channel2", payload: "test_payload") + + waitForExpectations(timeout: 5) + } + func testQueryToNode() throws { let conn = try postgreSQL.makeConnection() From df578b74d5bb88f02cfeb3f6f2a064b1c35f3372 Mon Sep 17 00:00:00 2001 From: Mark Lilback Date: Fri, 18 Aug 2017 18:31:20 -0400 Subject: [PATCH 02/11] added paramertized init for Connection.Notification --- Sources/PostgreSQL/Connection.swift | 9 +++++++++ Tests/PostgreSQLTests/PostgreSQLTests.swift | 2 ++ 2 files changed, 11 insertions(+) diff --git a/Sources/PostgreSQL/Connection.swift b/Sources/PostgreSQL/Connection.swift index be22bd8..0ead33c 100644 --- a/Sources/PostgreSQL/Connection.swift +++ b/Sources/PostgreSQL/Connection.swift @@ -152,6 +152,15 @@ public final class Connection: ConnInfoInitializable { public let channel: String public let payload: String? + /// initializer usable without knowledge of CPostgreSQL + /// required to allow unit testing of classes using Notifications + public init(pid: Int, channel: String, payload: String?) { + self.pid = pid + self.channel = channel + self.payload = payload + } + + /// internal initializer init(pgNotify: PGnotify) { channel = String(cString: pgNotify.relname) pid = Int(pgNotify.be_pid) diff --git a/Tests/PostgreSQLTests/PostgreSQLTests.swift b/Tests/PostgreSQLTests/PostgreSQLTests.swift index 0bed4ba..e291f6a 100644 --- a/Tests/PostgreSQLTests/PostgreSQLTests.swift +++ b/Tests/PostgreSQLTests/PostgreSQLTests.swift @@ -30,6 +30,8 @@ class PostgreSQLTests: XCTestCase { ("testUnsupportedObject", testUnsupportedObject), ("testNotification", testNotification), ("testNotificationWithPayload", testNotificationWithPayload), + ("testDispatchNotification", testDispatchNotification), + ("testDispatchNotificationWithPayload", testDispatchNotificationWithPayload), ("testQueryToNode", testQueryToNode) ] From 0837a5088ab732ef5187b1b26ad2664f68e54507 Mon Sep 17 00:00:00 2001 From: Mark Lilback Date: Fri, 15 Sep 2017 17:05:26 -0400 Subject: [PATCH 03/11] removed compiler warning about unused variable --- Sources/PostgreSQL/Bind/Bind+Node.swift | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Sources/PostgreSQL/Bind/Bind+Node.swift b/Sources/PostgreSQL/Bind/Bind+Node.swift index 5ffee0e..9faf3f6 100644 --- a/Sources/PostgreSQL/Bind/Bind+Node.swift +++ b/Sources/PostgreSQL/Bind/Bind+Node.swift @@ -25,7 +25,7 @@ extension Bind { case .array(let supportedArrayType): return Bind.parse(type: supportedArrayType, configuration: configuration, value: value, length: length) - case .unsupported(let oid): + case .unsupported(_): // Unsupported Oid type for PostgreSQL binding. // Fallback to simply passing on the bytes From d5ac0a4738ac6dc79b22eb2dbc55fa3c255fedc4 Mon Sep 17 00:00:00 2001 From: Mark Lilback Date: Fri, 15 Sep 2017 17:40:21 -0400 Subject: [PATCH 04/11] fixed broken unit tests --- Tests/PostgreSQLTests/PostgreSQLTests.swift | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Tests/PostgreSQLTests/PostgreSQLTests.swift b/Tests/PostgreSQLTests/PostgreSQLTests.swift index e291f6a..d923205 100644 --- a/Tests/PostgreSQLTests/PostgreSQLTests.swift +++ b/Tests/PostgreSQLTests/PostgreSQLTests.swift @@ -837,9 +837,9 @@ class PostgreSQLTests: XCTestCase { let queue = DispatchQueue.global() var source: DispatchSourceRead? source = try! conn1.makeListenDispatchSource(toChannel: "test_channel2", queue: queue) { (notification, error) in - XCTAssertEqual(notification?.channel, "test_channel1") + XCTAssertEqual(notification?.channel, "test_channel2") XCTAssertEqual(notification?.payload, "test_payload") - XCTAssertNil(notification?.payload) + XCTAssertNotNil(notification?.payload) XCTAssertNil(error) testExpectation.fulfill() From ec2116a7c879a693dd7a72385aa0a13da41d3ba2 Mon Sep 17 00:00:00 2001 From: Mark Lilback Date: Fri, 15 Sep 2017 17:46:19 -0400 Subject: [PATCH 05/11] added import for Dispatch --- Tests/PostgreSQLTests/PostgreSQLTests.swift | 1 + 1 file changed, 1 insertion(+) diff --git a/Tests/PostgreSQLTests/PostgreSQLTests.swift b/Tests/PostgreSQLTests/PostgreSQLTests.swift index d923205..a6447d6 100644 --- a/Tests/PostgreSQLTests/PostgreSQLTests.swift +++ b/Tests/PostgreSQLTests/PostgreSQLTests.swift @@ -1,6 +1,7 @@ import XCTest @testable import PostgreSQL import Foundation +import Dispatch class PostgreSQLTests: XCTestCase { static let allTests = [ From 9ccf4a2c202734ebd2e2832d0d51f78abd53a3b1 Mon Sep 17 00:00:00 2001 From: Mark Lilback Date: Wed, 27 Sep 2017 14:45:11 -0400 Subject: [PATCH 06/11] reversed df578b74 as it was unnecessary for unit tests --- Sources/PostgreSQL/Connection.swift | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Sources/PostgreSQL/Connection.swift b/Sources/PostgreSQL/Connection.swift index 0ead33c..d8b54df 100644 --- a/Sources/PostgreSQL/Connection.swift +++ b/Sources/PostgreSQL/Connection.swift @@ -152,14 +152,6 @@ public final class Connection: ConnInfoInitializable { public let channel: String public let payload: String? - /// initializer usable without knowledge of CPostgreSQL - /// required to allow unit testing of classes using Notifications - public init(pid: Int, channel: String, payload: String?) { - self.pid = pid - self.channel = channel - self.payload = payload - } - /// internal initializer init(pgNotify: PGnotify) { channel = String(cString: pgNotify.relname) From e2491cabea14d550fd1c2a1c0a125e186effe837 Mon Sep 17 00:00:00 2001 From: Mark Lilback Date: Wed, 27 Sep 2017 15:23:10 -0400 Subject: [PATCH 07/11] renamed makeListenDispatchSource() to listen(). Removed abbreviations from method documentation. --- Sources/PostgreSQL/Connection.swift | 6 +++--- Tests/PostgreSQLTests/PostgreSQLTests.swift | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/Sources/PostgreSQL/Connection.swift b/Sources/PostgreSQL/Connection.swift index d8b54df..1d1399f 100644 --- a/Sources/PostgreSQL/Connection.swift +++ b/Sources/PostgreSQL/Connection.swift @@ -177,13 +177,13 @@ public final class Connection: ConnInfoInitializable { /// - Parameter channel: the channel to register for /// - Parameter queue: the queue to create the DispatchSource on /// - Parameter callback: the callback - /// - Parameter note: The notification received from the database - /// - Parameter err: Any error while reading the notification. If not nil, the source will have been canceled + /// - Parameter notification: The notification received from the database + /// - Parameter error: Any error while reading the notification. If not nil, the source will have been canceled /// - Returns: the dispatch socket to activate /// - Throws: if fails to get the socket for the connection - public func makeListenDispatchSource(toChannel channel: String, queue: DispatchQueue, callback: @escaping (_ note: Notification?, _ err: Error?) -> Void) throws -> DispatchSourceRead { guard let sock = Optional.some(PQsocket(self.cConnection)), sock >= 0 else { throw PostgreSQLError(code: .ioError, reason: "failed to get socket for connection") } + public func listen(toChannel channel: String, queue: DispatchQueue, callback: @escaping (_ notification: Notification?, _ error: Error?) -> Void) throws -> DispatchSourceRead { let src = DispatchSource.makeReadSource(fileDescriptor: sock, queue: queue) src.setEventHandler { [unowned self] in do { diff --git a/Tests/PostgreSQLTests/PostgreSQLTests.swift b/Tests/PostgreSQLTests/PostgreSQLTests.swift index a6447d6..723146e 100644 --- a/Tests/PostgreSQLTests/PostgreSQLTests.swift +++ b/Tests/PostgreSQLTests/PostgreSQLTests.swift @@ -790,7 +790,7 @@ class PostgreSQLTests: XCTestCase { let queue = DispatchQueue.global() var source: DispatchSourceRead? - source = try! conn1.makeListenDispatchSource(toChannel: "test_channel1", queue: queue) { (notification, error) in + source = try! conn1.listen(toChannel: "test_channel1", queue: queue) { (notification, error) in XCTAssertEqual(notification?.channel, "test_channel1") XCTAssertNil(notification?.payload) XCTAssertNil(error) @@ -837,7 +837,7 @@ class PostgreSQLTests: XCTestCase { let queue = DispatchQueue.global() var source: DispatchSourceRead? - source = try! conn1.makeListenDispatchSource(toChannel: "test_channel2", queue: queue) { (notification, error) in + source = try! conn1.listen(toChannel: "test_channel2", queue: queue) { (notification, error) in XCTAssertEqual(notification?.channel, "test_channel2") XCTAssertEqual(notification?.payload, "test_payload") XCTAssertNotNil(notification?.payload) From e85f737bfaba8ee4c91464457c7223cd15a1bacf Mon Sep 17 00:00:00 2001 From: Mark Lilback Date: Wed, 27 Sep 2017 15:27:23 -0400 Subject: [PATCH 08/11] simplified initial guard to not make an optional out of a non-optional. Changed callback to use weak instead of unowned. --- Sources/PostgreSQL/Connection.swift | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/Sources/PostgreSQL/Connection.swift b/Sources/PostgreSQL/Connection.swift index 1d1399f..ae7cecf 100644 --- a/Sources/PostgreSQL/Connection.swift +++ b/Sources/PostgreSQL/Connection.swift @@ -181,15 +181,18 @@ public final class Connection: ConnInfoInitializable { /// - Parameter error: Any error while reading the notification. If not nil, the source will have been canceled /// - Returns: the dispatch socket to activate /// - Throws: if fails to get the socket for the connection - guard let sock = Optional.some(PQsocket(self.cConnection)), sock >= 0 - else { throw PostgreSQLError(code: .ioError, reason: "failed to get socket for connection") } public func listen(toChannel channel: String, queue: DispatchQueue, callback: @escaping (_ notification: Notification?, _ error: Error?) -> Void) throws -> DispatchSourceRead { + let sock = PQsocket(self.cConnection) + guard sock >= 0 else { + throw PostgreSQLError(code: .ioError, reason: "failed to get socket for connection") + } let src = DispatchSource.makeReadSource(fileDescriptor: sock, queue: queue) - src.setEventHandler { [unowned self] in + src.setEventHandler { [weak self] in + guard let strongSelf = self else { return } do { - try self.validateConnection() - PQconsumeInput(self.cConnection) - while let pgNotify = PQnotifies(self.cConnection) { + try strongSelf.validateConnection() + PQconsumeInput(strongSelf.cConnection) + while let pgNotify = PQnotifies(strongSelf.cConnection) { let notification = Notification(pgNotify: pgNotify.pointee) callback(notification, nil) PQfreemem(pgNotify) From 85128d97c397adea640ad78fa44aca077e4fcffc Mon Sep 17 00:00:00 2001 From: Mark Lilback Date: Sun, 8 Oct 2017 07:05:47 -0400 Subject: [PATCH 09/11] cConnection changed from let to var so it can be set to nil when invalid. reset() behaves no longer calls validateConnection(), now throws if connection was not properly reset. close() no longer marked throws as it doesn't actually throw anymore. listen() version using sleep deprecated. --- Sources/PostgreSQL/Connection.swift | 73 ++++++++++++--------- Sources/PostgreSQL/Error.swift | 2 +- Tests/PostgreSQLTests/ConnectionTests.swift | 5 +- Tests/PostgreSQLTests/PostgreSQLTests.swift | 17 ++++- 4 files changed, 61 insertions(+), 36 deletions(-) diff --git a/Sources/PostgreSQL/Connection.swift b/Sources/PostgreSQL/Connection.swift index ae7cecf..a347aa2 100644 --- a/Sources/PostgreSQL/Connection.swift +++ b/Sources/PostgreSQL/Connection.swift @@ -10,8 +10,11 @@ public final class Connection: ConnInfoInitializable { // MARK: - CConnection public typealias CConnection = OpaquePointer - - public let cConnection: CConnection + + @available(*, deprecated: 2.2, message: "needs to be optional or could cause runtime crash passing invalid reference to C") + public var cConnection: CConnection { return pgConnection! } + + public private(set) var pgConnection: CConnection? // MARK: - Init @@ -27,14 +30,14 @@ public final class Connection: ConnInfoInitializable { string = "host='\(hostname)' port='\(port)' dbname='\(database)' user='\(user)' password='\(password)' client_encoding='UTF8'" } - cConnection = PQconnectdb(string) + pgConnection = PQconnectdb(string) try validateConnection() } // MARK: - Deinit deinit { - try? close() + close() } // MARK: - Execute @@ -68,7 +71,7 @@ public final class Connection: ConnInfoInitializable { } let resultPointer: Result.Pointer? = PQexecParams( - cConnection, + pgConnection, query, Int32(binds.count), types, @@ -85,27 +88,35 @@ public final class Connection: ConnInfoInitializable { // MARK: - Connection Status public var isConnected: Bool { - return PQstatus(cConnection) == CONNECTION_OK + return pgConnection != nil && PQstatus(pgConnection) == CONNECTION_OK } public var status: ConnStatusType { - return PQstatus(cConnection) + guard pgConnection != nil else { return CONNECTION_BAD } + return PQstatus(pgConnection) } private func validateConnection() throws { + guard pgConnection != nil else { + throw PostgreSQLError(code: .connectionDoesNotExist, connection: self) + } guard isConnected else { throw PostgreSQLError(code: .connectionFailure, connection: self) } } public func reset() throws { - try validateConnection() - PQreset(cConnection) + guard let connection = pgConnection else { return } + PQreset(connection) + guard status == CONNECTION_OK else { + throw PostgreSQLError(code: .connectionFailure, connection: self) + } } - public func close() throws { - try validateConnection() - PQfinish(cConnection) + public func close() { + guard pgConnection != nil else { return } + PQfinish(pgConnection) + pgConnection = nil } // MARK: - Transaction @@ -161,8 +172,7 @@ public final class Connection: ConnInfoInitializable { let string = String(cString: pgNotify.extra) if !string.isEmpty { payload = string - } - else { + } else { payload = nil } } @@ -182,24 +192,22 @@ public final class Connection: ConnInfoInitializable { /// - Returns: the dispatch socket to activate /// - Throws: if fails to get the socket for the connection public func listen(toChannel channel: String, queue: DispatchQueue, callback: @escaping (_ notification: Notification?, _ error: Error?) -> Void) throws -> DispatchSourceRead { - let sock = PQsocket(self.cConnection) - guard sock >= 0 else { + let sock = PQsocket(self.pgConnection) + guard sock >= 0 else { throw PostgreSQLError(code: .ioError, reason: "failed to get socket for connection") - } + } let src = DispatchSource.makeReadSource(fileDescriptor: sock, queue: queue) src.setEventHandler { [weak self] in - guard let strongSelf = self else { return } - do { - try strongSelf.validateConnection() - PQconsumeInput(strongSelf.cConnection) - while let pgNotify = PQnotifies(strongSelf.cConnection) { - let notification = Notification(pgNotify: pgNotify.pointee) - callback(notification, nil) - PQfreemem(pgNotify) - } - } catch { - callback(nil, error) - src.cancel() + guard let strongSelf = self else { return } + guard strongSelf.pgConnection != nil else { + callback(nil, PostgreSQLError(code: .connectionDoesNotExist, reason: "connection does not exist")) + return + } + PQconsumeInput(strongSelf.pgConnection) + while let pgNotify = PQnotifies(strongSelf.pgConnection) { + let notification = Notification(pgNotify: pgNotify.pointee) + callback(notification, nil) + PQfreemem(pgNotify) } } try self.execute("LISTEN \(channel)") @@ -212,6 +220,7 @@ public final class Connection: ConnInfoInitializable { /// - channel: The channel to register for. /// - queue: The queue to perform the listening on. /// - callback: Callback containing any received notification or error and a boolean which can be set to true to stop listening. + @available(*, deprecated: 2.2, message: "replaced with version using DispatchSource") public func listen(toChannel channel: String, on queue: DispatchQueue = DispatchQueue.global(), callback: @escaping (Notification?, Error?, inout Bool) -> Void) { queue.async { var stop: Bool = false @@ -225,9 +234,9 @@ public final class Connection: ConnInfoInitializable { // Sleep to avoid looping continuously on cpu sleep(1) - PQconsumeInput(self.cConnection) + PQconsumeInput(self.pgConnection) - while !stop, let pgNotify = PQnotifies(self.cConnection) { + while !stop, let pgNotify = PQnotifies(self.pgConnection) { let notification = Notification(pgNotify: pgNotify.pointee) callback(notification, nil, &stop) @@ -269,7 +278,7 @@ public final class Connection: ConnInfoInitializable { } private func getBooleanParameterStatus(key: String, `default` defaultValue: Bool = false) -> Bool { - guard let value = PQparameterStatus(cConnection, "integer_datetimes") else { + guard let value = PQparameterStatus(pgConnection, "integer_datetimes") else { return defaultValue } return String(cString: value) == "on" diff --git a/Sources/PostgreSQL/Error.swift b/Sources/PostgreSQL/Error.swift index 82e29d8..1fbbbb3 100644 --- a/Sources/PostgreSQL/Error.swift +++ b/Sources/PostgreSQL/Error.swift @@ -305,7 +305,7 @@ extension PostgreSQLError { extension PostgreSQLError { public init(code: Code, connection: Connection) { let reason: String - if let error = PQerrorMessage(connection.cConnection) { + if let error = PQerrorMessage(connection.pgConnection) { reason = String(cString: error) } else { diff --git a/Tests/PostgreSQLTests/ConnectionTests.swift b/Tests/PostgreSQLTests/ConnectionTests.swift index cf2e3f9..65ab471 100644 --- a/Tests/PostgreSQLTests/ConnectionTests.swift +++ b/Tests/PostgreSQLTests/ConnectionTests.swift @@ -18,11 +18,12 @@ class ConnectionTests: XCTestCase { let conn = try postgreSQL.makeConnection() let connection = try postgreSQL.makeConnection() - XCTAssert(conn.status == CONNECTION_OK) + let status = conn.status + XCTAssert(status == CONNECTION_OK) XCTAssertTrue(connection.isConnected) try connection.reset() - try connection.close() + connection.close() XCTAssertFalse(connection.isConnected) } diff --git a/Tests/PostgreSQLTests/PostgreSQLTests.swift b/Tests/PostgreSQLTests/PostgreSQLTests.swift index 723146e..9efa2c7 100644 --- a/Tests/PostgreSQLTests/PostgreSQLTests.swift +++ b/Tests/PostgreSQLTests/PostgreSQLTests.swift @@ -32,6 +32,7 @@ class PostgreSQLTests: XCTestCase { ("testNotification", testNotification), ("testNotificationWithPayload", testNotificationWithPayload), ("testDispatchNotification", testDispatchNotification), + ("testDispatchNotificationInvalidConnection", testDispatchNotificationInvalidConnection), ("testDispatchNotificationWithPayload", testDispatchNotificationWithPayload), ("testQueryToNode", testQueryToNode) ] @@ -806,7 +807,21 @@ class PostgreSQLTests: XCTestCase { waitForExpectations(timeout: 5) } - + + func testDispatchNotificationInvalidConnection() throws { + let conn1 = try postgreSQL.makeConnection() + conn1.close() + do { + _ = try conn1.listen(toChannel: "test_channel1", queue: .global()) { (notification, error) in + XCTFail("callback should never be called") + } + XCTFail("exception should have been thrown because connection was not open") + } catch { + guard let pgerror = error as? PostgreSQLError else { XCTFail("incorrect error type"); return } + XCTAssertEqual(pgerror.code, .ioError) + } + } + func testNotificationWithPayload() throws { let conn1 = try postgreSQL.makeConnection() let conn2 = try postgreSQL.makeConnection() From 455a3bce3e5c4d6d7a4947c1b9761b6b49e05ebf Mon Sep 17 00:00:00 2001 From: Mark Lilback Date: Mon, 9 Oct 2017 13:15:59 -0400 Subject: [PATCH 10/11] added unit test for validateConnection throwing an error --- Sources/PostgreSQL/Connection.swift | 2 +- Tests/PostgreSQLTests/ConnectionTests.swift | 14 ++++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/Sources/PostgreSQL/Connection.swift b/Sources/PostgreSQL/Connection.swift index a347aa2..9a18cb0 100644 --- a/Sources/PostgreSQL/Connection.swift +++ b/Sources/PostgreSQL/Connection.swift @@ -96,7 +96,7 @@ public final class Connection: ConnInfoInitializable { return PQstatus(pgConnection) } - private func validateConnection() throws { + func validateConnection() throws { guard pgConnection != nil else { throw PostgreSQLError(code: .connectionDoesNotExist, connection: self) } diff --git a/Tests/PostgreSQLTests/ConnectionTests.swift b/Tests/PostgreSQLTests/ConnectionTests.swift index 65ab471..5791469 100644 --- a/Tests/PostgreSQLTests/ConnectionTests.swift +++ b/Tests/PostgreSQLTests/ConnectionTests.swift @@ -9,6 +9,7 @@ class ConnectionTests: XCTestCase { ("testConnInfoRaw", testConnInfoRaw), ("testConnectionFailure", testConnectionFailure), ("testConnectionSuccess", testConnectionSuccess), + ("testInvalidConnection", testInvalidConnection), ] var postgreSQL: PostgreSQL.Database! @@ -87,4 +88,17 @@ class ConnectionTests: XCTestCase { XCTFail("Could not connect to database") } } + + func testInvalidConnection() throws { + postgreSQL = PostgreSQL.Database.makeTest() + let connection = try postgreSQL.makeConnection() + try connection.validateConnection() + connection.close() + do { + try connection.validateConnection() + XCTFail("connection was valid after close") + } catch { + // connection was invalid + } + } } From b127f46967d75d50d70c9649c2c7926d3edd9502 Mon Sep 17 00:00:00 2001 From: Mark Lilback Date: Tue, 12 Dec 2017 09:40:56 -0500 Subject: [PATCH 11/11] updated order of brew commands for travis --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index cd44e11..1d8ae75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -9,9 +9,9 @@ matrix: env: SCHEME="PostgreSQL-Package" before_install: + - brew update - gem install xcpretty - brew tap vapor/tap - - brew update - brew install vapor install: