diff --git a/.github/workflows/api-docs.yml b/.github/workflows/api-docs.yml
index dc2e0634..3de1304c 100644
--- a/.github/workflows/api-docs.yml
+++ b/.github/workflows/api-docs.yml
@@ -3,6 +3,9 @@ on:
push:
branches:
- main
+permissions:
+ contents: read
+ id-token: write
jobs:
build-and-deploy:
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 704508ba..80d1a187 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -11,6 +11,8 @@ on:
- "*"
env:
LOG_LEVEL: info
+permissions:
+ contents: read
jobs:
linux-unit:
@@ -18,10 +20,10 @@ jobs:
fail-fast: false
matrix:
swift-image:
- - swift:5.10-jammy
- - swift:6.0-noble
+ - swift:6.0-jammy
- swift:6.1-noble
- - swiftlang/swift:nightly-main-jammy
+ - swift:6.2-noble
+ - swiftlang/swift:nightly-main-noble
container: ${{ matrix.swift-image }}
runs-on: ubuntu-latest
steps:
@@ -32,8 +34,10 @@ jobs:
[[ -z "${SWIFT_VERSION}" ]] && SWIFT_VERSION="$(cat /.swift_tag 2>/dev/null || true)"
printf 'OS: %s\nTag: %s\nVersion:\n' "${SWIFT_PLATFORM}-${RUNNER_ARCH}" "${SWIFT_VERSION}"
swift --version
+ - name: Install curl for Codecov
+ run: apt-get update -y -q && apt-get install -y curl
- name: Check out package
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Run unit tests with Thread Sanitizer
run: |
swift test --filter='^(PostgresNIOTests|ConnectionPoolModuleTests)' --sanitize=thread --enable-code-coverage
@@ -47,47 +51,31 @@ jobs:
fail-fast: false
matrix:
postgres-image:
- - postgres:17
- - postgres:15
- - postgres:13
+ - postgres:18
+ - postgres:16
+ - postgres:14
include:
- - postgres-image: postgres:17
+ - postgres-image: postgres:18
postgres-auth: scram-sha-256
- - postgres-image: postgres:15
+ - postgres-image: postgres:16
postgres-auth: md5
- - postgres-image: postgres:13
+ - postgres-image: postgres:14
postgres-auth: trust
container:
- image: swift:6.1-noble
+ image: swift:6.2-noble
volumes: [ 'pgrunshare:/var/run/postgresql' ]
runs-on: ubuntu-latest
env:
# Unfortunately, fluent-postgres-driver details leak through here
POSTGRES_DB: 'test_database'
- POSTGRES_DB_A: 'test_database'
- POSTGRES_DB_B: 'test_database'
POSTGRES_USER: 'test_username'
- POSTGRES_USER_A: 'test_username'
- POSTGRES_USER_B: 'test_username'
POSTGRES_PASSWORD: 'test_password'
- POSTGRES_PASSWORD_A: 'test_password'
- POSTGRES_PASSWORD_B: 'test_password'
POSTGRES_HOSTNAME: 'psql-a'
- POSTGRES_HOSTNAME_A: 'psql-a'
POSTGRES_HOSTNAME_B: 'psql-b'
POSTGRES_SOCKET: '/var/run/postgresql/.s.PGSQL.5432'
POSTGRES_HOST_AUTH_METHOD: ${{ matrix.postgres-auth }}
services:
- psql-a:
- image: ${{ matrix.postgres-image }}
- volumes: [ 'pgrunshare:/var/run/postgresql' ]
- env:
- POSTGRES_USER: 'test_username'
- POSTGRES_DB: 'test_database'
- POSTGRES_PASSWORD: 'test_password'
- POSTGRES_HOST_AUTH_METHOD: ${{ matrix.postgres-auth }}
- POSTGRES_INITDB_ARGS: --auth-host=${{ matrix.postgres-auth }}
- psql-b:
+ psql-a: &psql_service_spec
image: ${{ matrix.postgres-image }}
volumes: [ 'pgrunshare:/var/run/postgresql' ]
env:
@@ -96,6 +84,7 @@ jobs:
POSTGRES_PASSWORD: 'test_password'
POSTGRES_HOST_AUTH_METHOD: ${{ matrix.postgres-auth }}
POSTGRES_INITDB_ARGS: --auth-host=${{ matrix.postgres-auth }}
+ psql-b: *psql_service_spec
steps:
- name: Display OS and Swift versions
run: |
@@ -103,15 +92,15 @@ jobs:
[[ -z "${SWIFT_VERSION}" ]] && SWIFT_VERSION="$(cat /.swift_tag 2>/dev/null || true)"
printf 'OS: %s\nTag: %s\nVersion:\n' "${SWIFT_PLATFORM}-${RUNNER_ARCH}" "${SWIFT_VERSION}" && swift --version
- name: Check out package
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with: { path: 'postgres-nio' }
- name: Run integration tests
run: swift test --package-path postgres-nio --filter=^IntegrationTests
- name: Check out postgres-kit dependent
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with: { repository: 'vapor/postgres-kit', path: 'postgres-kit' }
- name: Check out fluent-postgres-driver dependent
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with: { repository: 'vapor/fluent-postgres-driver', path: 'fluent-postgres-driver' }
- name: Use local package in dependents
run: |
@@ -128,15 +117,18 @@ jobs:
matrix:
postgres-formula:
# Only test one version on macOS, let Linux do the rest
- - postgresql@16
+ - postgresql@17
postgres-auth:
# Only test one auth method on macOS, Linux tests will cover the others
- scram-sha-256
- xcode-version:
- - '~15'
+ macos-version:
+ - macos-15
+ - macos-26
include:
- - xcode-version: '~15'
- macos-version: 'macos-14'
+ - macos-version: macos-15
+ xcode-version: latest-stable
+ - macos-version: macos-26
+ xcode-version: latest-stable
runs-on: ${{ matrix.macos-version }}
env:
POSTGRES_HOSTNAME: 127.0.0.1
@@ -153,21 +145,19 @@ jobs:
xcode-version: ${{ matrix.xcode-version }}
- name: Install Postgres, setup DB and auth, and wait for server start
run: |
- export PATH="$(brew --prefix)/opt/${POSTGRES_FORMULA}/bin:$PATH" PGDATA=/tmp/vapor-postgres-test
- # ** BEGIN ** Work around bug in both Homebrew and GHA
- (brew upgrade python@3.11 || true) && (brew link --force --overwrite python@3.11 || true)
- (brew upgrade python@3.12 || true) && (brew link --force --overwrite python@3.12 || true)
- (brew upgrade || true)
- # ** END ** Work around bug in both Homebrew and GHA
- brew install --overwrite "${POSTGRES_FORMULA}"
- brew link --overwrite --force "${POSTGRES_FORMULA}"
+ export PGDATA=/tmp/vapor-postgres-test
+ brew install --overwrite "${POSTGRES_FORMULA}" && brew link --overwrite --force "${POSTGRES_FORMULA}"
initdb --locale=C --auth-host "${POSTGRES_AUTH_METHOD}" -U "${POSTGRES_USER}" --pwfile=<(echo "${POSTGRES_PASSWORD}")
pg_ctl start --wait
timeout-minutes: 15
- name: Checkout code
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
- name: Run all tests
- run: swift test
+ run: swift test --enable-code-coverage
+ - name: Submit code coverage
+ uses: vapor/swift-codecov-action@v0.3
+ with:
+ codecov_token: ${{ secrets.CODECOV_TOKEN }}
api-breakage:
if: github.event_name == 'pull_request'
@@ -175,7 +165,7 @@ jobs:
container: swift:noble
steps:
- name: Checkout
- uses: actions/checkout@v4
+ uses: actions/checkout@v5
with:
fetch-depth: 0
# https://github.com/actions/checkout/issues/766
@@ -183,22 +173,3 @@ jobs:
run: |
git config --global --add safe.directory "${GITHUB_WORKSPACE}"
swift package diagnose-api-breaking-changes origin/main
-
-# gh-codeql:
-# if: ${{ false }}
-# runs-on: ubuntu-latest
-# container: swift:noble
-# permissions: { actions: write, contents: read, security-events: write }
-# steps:
-# - name: Check out code
-# uses: actions/checkout@v4
-# - name: Mark repo safe in non-fake global config
-# run: git config --global --add safe.directory "${GITHUB_WORKSPACE}"
-# - name: Initialize CodeQL
-# uses: github/codeql-action/init@v3
-# with:
-# languages: swift
-# - name: Perform build
-# run: swift build
-# - name: Run CodeQL analyze
-# uses: github/codeql-action/analyze@v3
diff --git a/Package.swift b/Package.swift
index af2d07ae..ae0e8a5d 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,9 +1,15 @@
-// swift-tools-version:5.10
+// swift-tools-version:6.0
import PackageDescription
+#if compiler(>=6.1)
+let swiftSettings: [SwiftSetting] = []
+#else
let swiftSettings: [SwiftSetting] = [
- .enableUpcomingFeature("StrictConcurrency"),
+ // Sadly the 6.0 compiler concurrency checker finds false positives.
+ // To be able to compile, lets reduce the language version down to 5 for 6.0 only.
+ .swiftLanguageMode(.v5)
]
+#endif
let package = Package(
name: "postgres-nio",
@@ -24,7 +30,7 @@ let package = Package(
.package(url: "https://github.com/apple/swift-nio.git", from: "2.81.0"),
.package(url: "https://github.com/apple/swift-nio-transport-services.git", from: "1.19.0"),
.package(url: "https://github.com/apple/swift-nio-ssl.git", from: "2.25.0"),
- .package(url: "https://github.com/apple/swift-crypto.git", "3.9.0" ..< "4.0.0"),
+ .package(url: "https://github.com/apple/swift-crypto.git", "3.9.0" ..< "5.0.0"),
.package(url: "https://github.com/apple/swift-metrics.git", from: "2.4.1"),
.package(url: "https://github.com/apple/swift-log.git", from: "1.5.3"),
.package(url: "https://github.com/swift-server/swift-service-lifecycle.git", from: "2.5.0"),
diff --git a/README.md b/README.md
index 6d03b8da..fa4495e2 100644
--- a/README.md
+++ b/README.md
@@ -12,7 +12,7 @@
-
+
@@ -163,7 +163,7 @@ Please see [SECURITY.md] for details on the security process.
[Team Chat]: https://discord.gg/vapor
[MIT License]: LICENSE
[Continuous Integration]: https://github.com/vapor/postgres-nio/actions
-[Swift 5.10]: https://swift.org
+[Swift 6.0]: https://swift.org
[Security.md]: https://github.com/vapor/.github/blob/main/SECURITY.md
[`PostgresConnection`]: https://api.vapor.codes/postgresnio/documentation/postgresnio/postgresconnection
diff --git a/Sources/ConnectionPoolModule/ConnectionLease.swift b/Sources/ConnectionPoolModule/ConnectionLease.swift
new file mode 100644
index 00000000..77591a58
--- /dev/null
+++ b/Sources/ConnectionPoolModule/ConnectionLease.swift
@@ -0,0 +1,17 @@
+public struct ConnectionLease: Sendable {
+ public var connection: Connection
+
+ @usableFromInline
+ let _release: @Sendable (Connection) -> ()
+
+ @inlinable
+ public init(connection: Connection, release: @escaping @Sendable (Connection) -> Void) {
+ self.connection = connection
+ self._release = release
+ }
+
+ @inlinable
+ public func release() {
+ self._release(self.connection)
+ }
+}
diff --git a/Sources/ConnectionPoolModule/ConnectionPool.swift b/Sources/ConnectionPoolModule/ConnectionPool.swift
index b460b263..40d52a5a 100644
--- a/Sources/ConnectionPoolModule/ConnectionPool.swift
+++ b/Sources/ConnectionPoolModule/ConnectionPool.swift
@@ -88,7 +88,7 @@ public protocol ConnectionRequestProtocol: Sendable {
/// A function that is called with a connection or a
/// `PoolError`.
- func complete(with: Result)
+ func complete(with: Result, ConnectionPoolError>)
}
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
@@ -283,12 +283,15 @@ public final class ConnectionPool<
await self.run(in: &taskGroup)
}
} onCancel: {
- let actions = self.stateBox.withLockedValue { state in
- state.stateMachine.triggerForceShutdown()
- }
+ self.triggerForceShutdown()
+ }
+ }
- self.runStateMachineActions(actions)
+ public func triggerForceShutdown() {
+ let actions = self.stateBox.withLockedValue { state in
+ state.stateMachine.triggerForceShutdown()
}
+ self.runStateMachineActions(actions)
}
// MARK: - Private Methods -
@@ -402,8 +405,11 @@ public final class ConnectionPool<
/*private*/ func runRequestAction(_ action: StateMachine.RequestAction) {
switch action {
case .leaseConnection(let requests, let connection):
+ let lease = ConnectionLease(connection: connection) { connection in
+ self.releaseConnection(connection)
+ }
for request in requests {
- request.complete(with: .success(connection))
+ request.complete(with: .success(lease))
}
case .failRequest(let request, let error):
diff --git a/Sources/ConnectionPoolModule/ConnectionPoolError.swift b/Sources/ConnectionPoolModule/ConnectionPoolError.swift
index 1f1e1d2c..3abfe778 100644
--- a/Sources/ConnectionPoolModule/ConnectionPoolError.swift
+++ b/Sources/ConnectionPoolModule/ConnectionPoolError.swift
@@ -1,16 +1,25 @@
public struct ConnectionPoolError: Error, Hashable {
- enum Base: Error, Hashable {
+ @usableFromInline
+ enum Base: Error, Hashable, Sendable {
case requestCancelled
case poolShutdown
}
- private let base: Base
+ @usableFromInline
+ let base: Base
+ @inlinable
init(_ base: Base) { self.base = base }
/// The connection requests got cancelled
- public static let requestCancelled = ConnectionPoolError(.requestCancelled)
+ @inlinable
+ public static var requestCancelled: Self {
+ ConnectionPoolError(.requestCancelled)
+ }
/// The connection requests can't be fulfilled as the pool has already been shutdown
- public static let poolShutdown = ConnectionPoolError(.poolShutdown)
+ @inlinable
+ public static var poolShutdown: Self {
+ ConnectionPoolError(.poolShutdown)
+ }
}
diff --git a/Sources/ConnectionPoolModule/ConnectionRequest.swift b/Sources/ConnectionPoolModule/ConnectionRequest.swift
index 1d1c55da..d6654a27 100644
--- a/Sources/ConnectionPoolModule/ConnectionRequest.swift
+++ b/Sources/ConnectionPoolModule/ConnectionRequest.swift
@@ -5,18 +5,18 @@ public struct ConnectionRequest: ConnectionRequest
public var id: ID
@usableFromInline
- private(set) var continuation: CheckedContinuation
+ private(set) var continuation: CheckedContinuation, any Error>
@inlinable
init(
id: Int,
- continuation: CheckedContinuation
+ continuation: CheckedContinuation, any Error>
) {
self.id = id
self.continuation = continuation
}
- public func complete(with result: Result) {
+ public func complete(with result: Result, ConnectionPoolError>) {
self.continuation.resume(with: result)
}
}
@@ -46,7 +46,7 @@ extension ConnectionPool where Request == ConnectionRequest {
}
@inlinable
- public func leaseConnection() async throws -> Connection {
+ public func leaseConnection() async throws -> ConnectionLease {
let requestID = requestIDGenerator.next()
let connection = try await withTaskCancellationHandler {
@@ -54,7 +54,7 @@ extension ConnectionPool where Request == ConnectionRequest {
throw CancellationError()
}
- return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in
+ return try await withCheckedThrowingContinuation { (continuation: CheckedContinuation, Error>) in
let request = Request(
id: requestID,
continuation: continuation
@@ -71,8 +71,8 @@ extension ConnectionPool where Request == ConnectionRequest {
@inlinable
public func withConnection(_ closure: (Connection) async throws -> Result) async throws -> Result {
- let connection = try await self.leaseConnection()
- defer { self.releaseConnection(connection) }
- return try await closure(connection)
+ let lease = try await self.leaseConnection()
+ defer { lease.release() }
+ return try await closure(lease.connection)
}
}
diff --git a/Sources/ConnectionPoolModule/TinyFastSequence.swift b/Sources/ConnectionPoolModule/TinyFastSequence.swift
index dff8a30b..df140c98 100644
--- a/Sources/ConnectionPoolModule/TinyFastSequence.swift
+++ b/Sources/ConnectionPoolModule/TinyFastSequence.swift
@@ -29,6 +29,12 @@ struct TinyFastSequence: Sequence {
self.base = .none(reserveCapacity: 0)
case 1:
self.base = .one(collection.first!, reserveCapacity: 0)
+ case 2:
+ self.base = .two(
+ collection.first!,
+ collection[collection.index(after: collection.startIndex)],
+ reserveCapacity: 0
+ )
default:
if let collection = collection as? Array {
self.base = .n(collection)
@@ -46,7 +52,7 @@ struct TinyFastSequence: Sequence {
case 1:
self.base = .one(max2Sequence.first!, reserveCapacity: 0)
case 2:
- self.base = .n(Array(max2Sequence))
+ self.base = .two(max2Sequence.first!, max2Sequence.second!, reserveCapacity: 0)
default:
fatalError()
}
@@ -169,7 +175,7 @@ struct TinyFastSequence: Sequence {
case .n(let array):
if self.index < array.endIndex {
- defer { self.index += 1}
+ defer { self.index += 1 }
return array[self.index]
}
return nil
diff --git a/Sources/ConnectionPoolTestUtils/MockRequest.swift b/Sources/ConnectionPoolTestUtils/MockRequest.swift
index 5e4e2fc0..3dd8b0fb 100644
--- a/Sources/ConnectionPoolTestUtils/MockRequest.swift
+++ b/Sources/ConnectionPoolTestUtils/MockRequest.swift
@@ -1,8 +1,6 @@
import _ConnectionPoolModule
-public final class MockRequest: ConnectionRequestProtocol, Hashable, Sendable {
- public typealias Connection = MockConnection
-
+public final class MockRequest: ConnectionRequestProtocol, Hashable, Sendable {
public struct ID: Hashable, Sendable {
var objectID: ObjectIdentifier
@@ -11,7 +9,7 @@ public final class MockRequest: ConnectionRequestProtocol, Hashable, Sendable {
}
}
- public init() {}
+ public init(connectionType: Connection.Type = Connection.self) {}
public var id: ID { ID(self) }
@@ -23,7 +21,7 @@ public final class MockRequest: ConnectionRequestProtocol, Hashable, Sendable {
hasher.combine(self.id)
}
- public func complete(with: Result) {
+ public func complete(with: Result, ConnectionPoolError>) {
}
}
diff --git a/Sources/PostgresNIO/Connection/PostgresConnection.swift b/Sources/PostgresNIO/Connection/PostgresConnection.swift
index e267d8f9..fc48fa31 100644
--- a/Sources/PostgresNIO/Connection/PostgresConnection.swift
+++ b/Sources/PostgresNIO/Connection/PostgresConnection.swift
@@ -531,7 +531,6 @@ extension PostgresConnection {
}
}
- #if compiler(>=6.0)
/// Puts the connection into an open transaction state, for the provided `closure`'s lifetime.
///
/// The function starts a transaction by running a `BEGIN` query on the connection against the database. It then
@@ -552,9 +551,8 @@ extension PostgresConnection {
file: String = #file,
line: Int = #line,
isolation: isolated (any Actor)? = #isolation,
- // DO NOT FIX THE WHITESPACE IN THE NEXT LINE UNTIL 5.10 IS UNSUPPORTED
- // https://github.com/swiftlang/swift/issues/79285
- _ process: (PostgresConnection) async throws -> sending Result) async throws -> sending Result {
+ _ process: (PostgresConnection) async throws -> sending Result
+ ) async throws -> sending Result {
do {
try await self.query("BEGIN;", logger: logger)
} catch {
@@ -583,57 +581,6 @@ extension PostgresConnection {
throw transactionError
}
}
- #else
- /// Puts the connection into an open transaction state, for the provided `closure`'s lifetime.
- ///
- /// The function starts a transaction by running a `BEGIN` query on the connection against the database. It then
- /// lends the connection to the user provided closure. The user can then modify the database as they wish. If the user
- /// provided closure returns successfully, the function will attempt to commit the changes by running a `COMMIT`
- /// query against the database. If the user provided closure throws an error, the function will attempt to rollback the
- /// changes made within the closure.
- ///
- /// - Parameters:
- /// - logger: The `Logger` to log into for the transaction.
- /// - file: The file, the transaction was started in. Used for better error reporting.
- /// - line: The line, the transaction was started in. Used for better error reporting.
- /// - closure: The user provided code to modify the database. Use the provided connection to run queries.
- /// The connection must stay in the transaction mode. Otherwise this method will throw!
- /// - Returns: The closure's return value.
- public func withTransaction(
- logger: Logger,
- file: String = #file,
- line: Int = #line,
- _ process: (PostgresConnection) async throws -> Result
- ) async throws -> Result {
- do {
- try await self.query("BEGIN;", logger: logger)
- } catch {
- throw PostgresTransactionError(file: file, line: line, beginError: error)
- }
-
- var closureHasFinished: Bool = false
- do {
- let value = try await process(self)
- closureHasFinished = true
- try await self.query("COMMIT;", logger: logger)
- return value
- } catch {
- var transactionError = PostgresTransactionError(file: file, line: line)
- if !closureHasFinished {
- transactionError.closureError = error
- do {
- try await self.query("ROLLBACK;", logger: logger)
- } catch {
- transactionError.rollbackError = error
- }
- } else {
- transactionError.commitError = error
- }
-
- throw transactionError
- }
- }
- #endif
}
// MARK: EventLoopFuture interface
diff --git a/Sources/PostgresNIO/New/Connection State Machine/ConnectionStateMachine.swift b/Sources/PostgresNIO/New/Connection State Machine/ConnectionStateMachine.swift
index 9d264bcc..8560b948 100644
--- a/Sources/PostgresNIO/New/Connection State Machine/ConnectionStateMachine.swift
+++ b/Sources/PostgresNIO/New/Connection State Machine/ConnectionStateMachine.swift
@@ -752,6 +752,12 @@ struct ConnectionStateMachine {
return self.modify(with: action)
}
+ mutating func copyInResponseReceived(
+ _ copyInResponse: PostgresBackendMessage.CopyInResponse
+ ) -> ConnectionAction {
+ return self.closeConnectionAndCleanup(.unexpectedBackendMessage(.copyInResponse(copyInResponse)))
+ }
+
mutating func emptyQueryResponseReceived() -> ConnectionAction {
guard case .extendedQuery(var queryState, let connectionContext) = self.state, !queryState.isComplete else {
return self.closeConnectionAndCleanup(.unexpectedBackendMessage(.emptyQueryResponse))
diff --git a/Sources/PostgresNIO/New/Connection State Machine/ExtendedQueryStateMachine.swift b/Sources/PostgresNIO/New/Connection State Machine/ExtendedQueryStateMachine.swift
index 087a6c24..5708b6b9 100644
--- a/Sources/PostgresNIO/New/Connection State Machine/ExtendedQueryStateMachine.swift
+++ b/Sources/PostgresNIO/New/Connection State Machine/ExtendedQueryStateMachine.swift
@@ -91,7 +91,7 @@ struct ExtendedQueryStateMachine {
mutating func cancel() -> Action {
switch self.state {
case .initialized:
- preconditionFailure("Start must be called immediatly after the query was created")
+ preconditionFailure("Start must be called immediately after the query was created")
case .messagesSent(let queryContext),
.parseCompleteReceived(let queryContext),
@@ -322,6 +322,12 @@ struct ExtendedQueryStateMachine {
}
}
+ mutating func copyInResponseReceived(
+ _ copyInResponse: PostgresBackendMessage.CopyInResponse
+ ) -> Action {
+ return self.setAndFireError(.unexpectedBackendMessage(.copyInResponse(copyInResponse)))
+ }
+
mutating func emptyQueryResponseReceived() -> Action {
guard case .bindCompleteReceived(let queryContext) = self.state else {
return self.setAndFireError(.unexpectedBackendMessage(.emptyQueryResponse))
diff --git a/Sources/PostgresNIO/New/Data/String+PostgresCodable.swift b/Sources/PostgresNIO/New/Data/String+PostgresCodable.swift
index 6bd09e78..7e8376a7 100644
--- a/Sources/PostgresNIO/New/Data/String+PostgresCodable.swift
+++ b/Sources/PostgresNIO/New/Data/String+PostgresCodable.swift
@@ -29,6 +29,12 @@ extension String: PostgresDecodable {
context: PostgresDecodingContext
) throws {
switch (format, type) {
+ case (.binary, .jsonb):
+ // Discard the version byte
+ guard let version = buffer.readInteger(as: UInt8.self), version == 1 else {
+ throw PostgresDecodingError.Code.failure
+ }
+ self = buffer.readString(length: buffer.readableBytes)!
case (_, .varchar),
(_, .bpchar),
(_, .text),
diff --git a/Sources/PostgresNIO/New/Messages/CopyInMessage.swift b/Sources/PostgresNIO/New/Messages/CopyInMessage.swift
new file mode 100644
index 00000000..46dec648
--- /dev/null
+++ b/Sources/PostgresNIO/New/Messages/CopyInMessage.swift
@@ -0,0 +1,44 @@
+extension PostgresBackendMessage {
+ struct CopyInResponse: Hashable {
+ enum Format: Int8 {
+ case textual = 0
+ case binary = 1
+ }
+
+ let format: Format
+ let columnFormats: [Format]
+
+ static func decode(from buffer: inout ByteBuffer) throws -> Self {
+ guard let rawFormat = buffer.readInteger(endianness: .big, as: Int8.self) else {
+ throw PSQLPartialDecodingError.expectedAtLeastNRemainingBytes(1, actual: buffer.readableBytes)
+ }
+ guard let format = Format(rawValue: rawFormat) else {
+ throw PSQLPartialDecodingError.unexpectedValue(value: rawFormat)
+ }
+
+ guard let numColumns = buffer.readInteger(endianness: .big, as: Int16.self) else {
+ throw PSQLPartialDecodingError.expectedAtLeastNRemainingBytes(2, actual: buffer.readableBytes)
+ }
+ var columnFormatCodes: [Format] = []
+ columnFormatCodes.reserveCapacity(Int(numColumns))
+
+ for _ in 0.. Self {
+ return PSQLPartialDecodingError(
+ description: "Unknown message kind: \(messageID)",
+ file: file, line: line)
+ }
}
extension ByteBuffer {
diff --git a/Sources/PostgresNIO/New/PostgresChannelHandler.swift b/Sources/PostgresNIO/New/PostgresChannelHandler.swift
index 0a14849a..bc256203 100644
--- a/Sources/PostgresNIO/New/PostgresChannelHandler.swift
+++ b/Sources/PostgresNIO/New/PostgresChannelHandler.swift
@@ -136,6 +136,8 @@ final class PostgresChannelHandler: ChannelDuplexHandler {
action = self.state.closeCompletedReceived()
case .commandComplete(let commandTag):
action = self.state.commandCompletedReceived(commandTag)
+ case .copyInResponse(let copyInResponse):
+ action = self.state.copyInResponseReceived(copyInResponse)
case .dataRow(let dataRow):
action = self.state.dataRowReceived(dataRow)
case .emptyQueryResponse:
@@ -565,8 +567,13 @@ final class PostgresChannelHandler: ChannelDuplexHandler {
_ cleanup: ConnectionStateMachine.ConnectionAction.CleanUpContext,
context: ChannelHandlerContext
) {
- self.logger.debug("Cleaning up and closing connection.", metadata: [.error: "\(cleanup.error)"])
-
+ // Don't log a misleading error if the client closed the connection.
+ if cleanup.error.code == .clientClosedConnection {
+ self.logger.debug("Cleaning up and closing connection.")
+ } else {
+ self.logger.debug("Cleaning up and closing connection.", metadata: [.error: "\(cleanup.error)"])
+ }
+
// 1. fail all tasks
cleanup.tasks.forEach { task in
task.failWithError(cleanup.error)
diff --git a/Sources/PostgresNIO/New/PostgresFrontendMessageEncoder.swift b/Sources/PostgresNIO/New/PostgresFrontendMessageEncoder.swift
index 97805418..6ca4cc27 100644
--- a/Sources/PostgresNIO/New/PostgresFrontendMessageEncoder.swift
+++ b/Sources/PostgresNIO/New/PostgresFrontendMessageEncoder.swift
@@ -167,6 +167,28 @@ struct PostgresFrontendMessageEncoder {
self.buffer.writeMultipleIntegers(UInt32(8), Self.sslRequestCode)
}
+ /// Adds the `CopyData` message ID and `dataLength` to the message buffer but not the actual data.
+ ///
+ /// The caller of this function is expected to write the encoder's message buffer to the backend after calling this
+ /// function, followed by sending the actual data to the backend.
+ mutating func copyDataHeader(dataLength: UInt32) {
+ self.clearIfNeeded()
+ self.buffer.psqlWriteMultipleIntegers(id: .copyData, length: dataLength)
+ }
+
+ mutating func copyDone() {
+ self.clearIfNeeded()
+ self.buffer.psqlWriteMultipleIntegers(id: .copyDone, length: 0)
+ }
+
+ mutating func copyFail(message: String) {
+ self.clearIfNeeded()
+ var messageBuffer = ByteBuffer()
+ messageBuffer.writeNullTerminatedString(message)
+ self.buffer.psqlWriteMultipleIntegers(id: .copyFail, length: UInt32(messageBuffer.readableBytes))
+ self.buffer.writeImmutableBuffer(messageBuffer)
+ }
+
mutating func sync() {
self.clearIfNeeded()
self.buffer.psqlWriteMultipleIntegers(id: .sync, length: 0)
@@ -197,6 +219,9 @@ struct PostgresFrontendMessageEncoder {
private enum FrontendMessageID: UInt8, Hashable, Sendable {
case bind = 66 // B
case close = 67 // C
+ case copyData = 100 // d
+ case copyDone = 99 // c
+ case copyFail = 102 // f
case describe = 68 // D
case execute = 69 // E
case flush = 72 // H
diff --git a/Sources/PostgresNIO/New/VariadicGenerics.swift b/Sources/PostgresNIO/New/VariadicGenerics.swift
index 7931c90c..b284c7a2 100644
--- a/Sources/PostgresNIO/New/VariadicGenerics.swift
+++ b/Sources/PostgresNIO/New/VariadicGenerics.swift
@@ -116,7 +116,8 @@ extension PostgresRow {
extension AsyncSequence where Element == PostgresRow {
// --- snip TODO: Remove once bug is fixed, that disallows tuples of one
@inlinable
- public func decode(
+ @preconcurrency
+ public func decode(
_: Column.Type,
context: PostgresDecodingContext,
file: String = #fileID,
@@ -128,7 +129,8 @@ extension AsyncSequence where Element == PostgresRow {
}
@inlinable
- public func decode(
+ @preconcurrency
+ public func decode(
_: Column.Type,
file: String = #fileID,
line: Int = #line
@@ -137,7 +139,8 @@ extension AsyncSequence where Element == PostgresRow {
}
// --- snap TODO: Remove once bug is fixed, that disallows tuples of one
- public func decode(
+ @preconcurrency
+ public func decode(
_ columnType: (repeat each Column).Type,
context: PostgresDecodingContext,
file: String = #fileID,
@@ -148,7 +151,8 @@ extension AsyncSequence where Element == PostgresRow {
}
}
- public func decode(
+ @preconcurrency
+ public func decode(
_ columnType: (repeat each Column).Type,
file: String = #fileID,
line: Int = #line
diff --git a/Sources/PostgresNIO/Pool/PostgresClient.swift b/Sources/PostgresNIO/Pool/PostgresClient.swift
index d54e34eb..581b5113 100644
--- a/Sources/PostgresNIO/Pool/PostgresClient.swift
+++ b/Sources/PostgresNIO/Pool/PostgresClient.swift
@@ -301,14 +301,13 @@ public final class PostgresClient: Sendable, ServiceLifecycle.Service {
/// - Returns: The closure's return value.
@_disfavoredOverload
public func withConnection(_ closure: (PostgresConnection) async throws -> Result) async throws -> Result {
- let connection = try await self.leaseConnection()
+ let lease = try await self.leaseConnection()
- defer { self.pool.releaseConnection(connection) }
+ defer { lease.release() }
- return try await closure(connection)
+ return try await closure(lease.connection)
}
- #if compiler(>=6.0)
/// Lease a connection for the provided `closure`'s lifetime.
///
/// - Parameter closure: A closure that uses the passed `PostgresConnection`. The closure **must not** capture
@@ -316,14 +315,13 @@ public final class PostgresClient: Sendable, ServiceLifecycle.Service {
/// - Returns: The closure's return value.
public func withConnection(
isolation: isolated (any Actor)? = #isolation,
- // DO NOT FIX THE WHITESPACE IN THE NEXT LINE UNTIL 5.10 IS UNSUPPORTED
- // https://github.com/swiftlang/swift/issues/79285
- _ closure: (PostgresConnection) async throws -> sending Result) async throws -> sending Result {
- let connection = try await self.leaseConnection()
+ _ closure: (PostgresConnection) async throws -> sending Result
+ ) async throws -> sending Result {
+ let lease = try await self.leaseConnection()
- defer { self.pool.releaseConnection(connection) }
+ defer { lease.release() }
- return try await closure(connection)
+ return try await closure(lease.connection)
}
/// Lease a connection, which is in an open transaction state, for the provided `closure`'s lifetime.
@@ -346,41 +344,13 @@ public final class PostgresClient: Sendable, ServiceLifecycle.Service {
file: String = #file,
line: Int = #line,
isolation: isolated (any Actor)? = #isolation,
- // DO NOT FIX THE WHITESPACE IN THE NEXT LINE UNTIL 5.10 IS UNSUPPORTED
- // https://github.com/swiftlang/swift/issues/79285
- _ closure: (PostgresConnection) async throws -> sending Result) async throws -> sending Result {
- try await self.withConnection { connection in
- try await connection.withTransaction(logger: logger, file: file, line: line, closure)
+ _ closure: (PostgresConnection) async throws -> sending Result
+ ) async throws -> sending Result {
+ // for 6.0 to compile we need to explicitly forward the isolation.
+ try await self.withConnection(isolation: isolation) { connection in
+ try await connection.withTransaction(logger: logger, file: file, line: line, isolation: isolation, closure)
}
}
- #else
-
- /// Lease a connection, which is in an open transaction state, for the provided `closure`'s lifetime.
- ///
- /// The function leases a connection from the underlying connection pool and starts a transaction by running a `BEGIN`
- /// query on the leased connection against the database. It then lends the connection to the user provided closure.
- /// The user can then modify the database as they wish. If the user provided closure returns successfully, the function
- /// will attempt to commit the changes by running a `COMMIT` query against the database. If the user provided closure
- /// throws an error, the function will attempt to rollback the changes made within the closure.
- ///
- /// - Parameters:
- /// - logger: The `Logger` to log into for the transaction.
- /// - file: The file, the transaction was started in. Used for better error reporting.
- /// - line: The line, the transaction was started in. Used for better error reporting.
- /// - closure: The user provided code to modify the database. Use the provided connection to run queries.
- /// The connection must stay in the transaction mode. Otherwise this method will throw!
- /// - Returns: The closure's return value.
- public func withTransaction(
- logger: Logger,
- file: String = #file,
- line: Int = #line,
- _ closure: (PostgresConnection) async throws -> Result
- ) async throws -> Result {
- try await self.withConnection { connection in
- try await connection.withTransaction(logger: logger, file: file, line: line, closure)
- }
- }
- #endif
/// Run a query on the Postgres server the client is connected to.
///
@@ -404,7 +374,8 @@ public final class PostgresClient: Sendable, ServiceLifecycle.Service {
throw PSQLError(code: .tooManyParameters, query: query, file: file, line: line)
}
- let connection = try await self.leaseConnection()
+ let lease = try await self.leaseConnection()
+ let connection = lease.connection
var logger = logger
logger[postgresMetadataKey: .connectionID] = "\(connection.id)"
@@ -419,12 +390,12 @@ public final class PostgresClient: Sendable, ServiceLifecycle.Service {
connection.channel.write(HandlerTask.extendedQuery(context), promise: nil)
promise.futureResult.whenFailure { _ in
- self.pool.releaseConnection(connection)
+ lease.release()
}
return try await promise.futureResult.map {
$0.asyncSequence(onFinish: {
- self.pool.releaseConnection(connection)
+ lease.release()
})
}.get()
} catch var error as PSQLError {
@@ -446,7 +417,8 @@ public final class PostgresClient: Sendable, ServiceLifecycle.Service {
let logger = logger ?? Self.loggingDisabled
do {
- let connection = try await self.leaseConnection()
+ let lease = try await self.leaseConnection()
+ let connection = lease.connection
let promise = connection.channel.eventLoop.makePromise(of: PSQLRowStream.self)
let task = HandlerTask.executePreparedStatement(.init(
@@ -460,11 +432,11 @@ public final class PostgresClient: Sendable, ServiceLifecycle.Service {
connection.channel.write(task, promise: nil)
promise.futureResult.whenFailure { _ in
- self.pool.releaseConnection(connection)
+ lease.release()
}
return try await promise.futureResult
- .map { $0.asyncSequence(onFinish: { self.pool.releaseConnection(connection) }) }
+ .map { $0.asyncSequence(onFinish: { lease.release() }) }
.get()
.map { try preparedStatement.decodeRow($0) }
} catch var error as PSQLError {
@@ -504,7 +476,7 @@ public final class PostgresClient: Sendable, ServiceLifecycle.Service {
// MARK: - Private Methods -
- private func leaseConnection() async throws -> PostgresConnection {
+ private func leaseConnection() async throws -> ConnectionLease {
if !self.runningAtomic.load(ordering: .relaxed) {
self.backgroundLogger.warning("Trying to lease connection from `PostgresClient`, but `PostgresClient.run()` hasn't been called yet.")
}
diff --git a/Sources/PostgresNIO/Pool/PostgresClientMetrics.swift b/Sources/PostgresNIO/Pool/PostgresClientMetrics.swift
index aa8215db..62fa326a 100644
--- a/Sources/PostgresNIO/Pool/PostgresClientMetrics.swift
+++ b/Sources/PostgresNIO/Pool/PostgresClientMetrics.swift
@@ -19,7 +19,7 @@ final class PostgresClientMetrics: ConnectionPoolObservabilityDelegate {
/// A connection attempt failed with the given error. After some period of
/// time ``startedConnecting(id:)`` may be called again.
func connectFailed(id: ConnectionID, error: Error) {
- self.logger.debug("Connection creation failed", metadata: [
+ self.logger.info("Connection creation failed", metadata: [
.connectionID: "\(id)",
.error: "\(String(reflecting: error))"
])
diff --git a/Tests/ConnectionPoolModuleTests/ConnectionIDGeneratorTests.swift b/Tests/ConnectionPoolModuleTests/ConnectionIDGeneratorTests.swift
index fb0bfce1..23165746 100644
--- a/Tests/ConnectionPoolModuleTests/ConnectionIDGeneratorTests.swift
+++ b/Tests/ConnectionPoolModuleTests/ConnectionIDGeneratorTests.swift
@@ -1,13 +1,14 @@
import _ConnectionPoolModule
-import XCTest
+import Testing
-final class ConnectionIDGeneratorTests: XCTestCase {
- func testGenerateConnectionIDs() async {
+@Suite struct ConnectionIDGeneratorTests {
+
+ @Test func testGenerateConnectionIDs() async {
let idGenerator = ConnectionIDGenerator()
- XCTAssertEqual(idGenerator.next(), 0)
- XCTAssertEqual(idGenerator.next(), 1)
- XCTAssertEqual(idGenerator.next(), 2)
+ #expect(idGenerator.next() == 0)
+ #expect(idGenerator.next() == 1)
+ #expect(idGenerator.next() == 2)
await withTaskGroup(of: Void.self) { taskGroup in
for _ in 0..<1000 {
@@ -17,6 +18,6 @@ final class ConnectionIDGeneratorTests: XCTestCase {
}
}
- XCTAssertEqual(idGenerator.next(), 1003)
+ #expect(idGenerator.next() == 1003)
}
}
diff --git a/Tests/ConnectionPoolModuleTests/ConnectionPoolTests.swift b/Tests/ConnectionPoolModuleTests/ConnectionPoolTests.swift
index c745b4a0..f3664242 100644
--- a/Tests/ConnectionPoolModuleTests/ConnectionPoolTests.swift
+++ b/Tests/ConnectionPoolModuleTests/ConnectionPoolTests.swift
@@ -2,12 +2,13 @@
import _ConnectionPoolTestUtils
import Atomics
import NIOEmbedded
-import XCTest
+import Testing
-@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
-final class ConnectionPoolTests: XCTestCase {
- func test1000ConsecutiveRequestsOnSingleConnection() async {
+@Suite struct ConnectionPoolTests {
+
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func test1000ConsecutiveRequestsOnSingleConnection() async {
let factory = MockConnectionFactory()
var config = ConnectionPoolConfiguration()
@@ -34,37 +35,35 @@ final class ConnectionPoolTests: XCTestCase {
let createdConnection = await factory.nextConnectAttempt { _ in
return 1
}
- XCTAssertNotNil(createdConnection)
do {
for _ in 0..<1000 {
- async let connectionFuture = try await pool.leaseConnection()
- var leasedConnection: MockConnection?
- XCTAssertEqual(factory.pendingConnectionAttemptsCount, 0)
- leasedConnection = try await connectionFuture
- XCTAssertNotNil(leasedConnection)
- XCTAssert(createdConnection === leasedConnection)
-
- if let leasedConnection {
- pool.releaseConnection(leasedConnection)
- }
+ async let connectionFuture = pool.leaseConnection()
+ var connectionLease: ConnectionLease?
+ #expect(factory.pendingConnectionAttemptsCount == 0)
+ connectionLease = try await connectionFuture
+ #expect(connectionLease != nil)
+ #expect(createdConnection === connectionLease?.connection)
+
+ connectionLease?.release()
}
} catch {
- XCTFail("Unexpected error: \(error)")
+ Issue.record("Unexpected error: \(error)")
}
taskGroup.cancelAll()
- XCTAssertEqual(factory.pendingConnectionAttemptsCount, 0)
+ #expect(factory.pendingConnectionAttemptsCount == 0)
for connection in factory.runningConnections {
connection.closeIfClosing()
}
}
- XCTAssertEqual(factory.runningConnections.count, 0)
+ #expect(factory.runningConnections.count == 0)
}
- func testShutdownPoolWhileConnectionIsBeingCreated() async {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testShutdownPoolWhileConnectionIsBeingCreated() async {
let clock = MockClock()
let factory = MockConnectionFactory()
@@ -109,7 +108,8 @@ final class ConnectionPoolTests: XCTestCase {
struct ConnectionCreationError: Error {}
}
- func testShutdownPoolWhileConnectionIsBackingOff() async {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testShutdownPoolWhileConnectionIsBackingOff() async {
let clock = MockClock()
let factory = MockConnectionFactory()
@@ -144,7 +144,8 @@ final class ConnectionPoolTests: XCTestCase {
struct ConnectionCreationError: Error {}
}
- func testConnectionHardLimitIsRespected() async {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testConnectionHardLimitIsRespected() async {
let factory = MockConnectionFactory()
var mutableConfig = ConnectionPoolConfiguration()
@@ -173,21 +174,21 @@ final class ConnectionPoolTests: XCTestCase {
await withTaskGroup(of: Void.self) { taskGroup in
taskGroup.addTask_ {
await pool.run()
- XCTAssertFalse(hasFinished.compareExchange(expected: false, desired: true, ordering: .relaxed).original)
+ #expect(hasFinished.compareExchange(expected: false, desired: true, ordering: .relaxed).original == false)
}
taskGroup.addTask_ {
var usedConnectionIDs = Set()
for _ in 0..()
let keepAliveDuration = Duration.seconds(30)
@@ -250,16 +252,16 @@ final class ConnectionPoolTests: XCTestCase {
await pool.run()
}
- async let lease1ConnectionAsync = pool.leaseConnection()
+ async let connectionLeaseFuture = pool.leaseConnection()
let connection = await factory.nextConnectAttempt { connectionID in
return 1
}
- let lease1Connection = try await lease1ConnectionAsync
- XCTAssert(connection === lease1Connection)
+ let connectionLease = try await connectionLeaseFuture
+ #expect(connection === connectionLease.connection)
- pool.releaseConnection(lease1Connection)
+ connectionLease.release()
// keep alive 1
@@ -267,11 +269,11 @@ final class ConnectionPoolTests: XCTestCase {
var expectedInstants: Set = [.init(keepAliveDuration), .init(config.idleTimeout)]
let deadline1 = await clock.nextTimerScheduled()
print(deadline1)
- XCTAssertNotNil(expectedInstants.remove(deadline1))
+ #expect(expectedInstants.remove(deadline1) != nil)
let deadline2 = await clock.nextTimerScheduled()
print(deadline2)
- XCTAssertNotNil(expectedInstants.remove(deadline2))
- XCTAssert(expectedInstants.isEmpty)
+ #expect(expectedInstants.remove(deadline2) != nil)
+ #expect(expectedInstants.isEmpty == true)
// move clock forward to keep alive
let newTime = clock.now.advanced(by: keepAliveDuration)
@@ -280,14 +282,14 @@ final class ConnectionPoolTests: XCTestCase {
await keepAlive.nextKeepAlive { keepAliveConnection in
defer { print("keep alive 1 has run") }
- XCTAssertTrue(keepAliveConnection === lease1Connection)
+ #expect(keepAliveConnection === connectionLease.connection)
return true
}
// keep alive 2
let deadline3 = await clock.nextTimerScheduled()
- XCTAssertEqual(deadline3, clock.now.advanced(by: keepAliveDuration))
+ #expect(deadline3 == clock.now.advanced(by: keepAliveDuration))
print(deadline3)
// race keep alive vs timeout
@@ -301,7 +303,8 @@ final class ConnectionPoolTests: XCTestCase {
}
}
- func testKeepAliveOnClose() async throws {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testKeepAliveOnClose() async throws {
let clock = MockClock()
let factory = MockConnectionFactory()
let keepAliveDuration = Duration.seconds(20)
@@ -329,16 +332,16 @@ final class ConnectionPoolTests: XCTestCase {
await pool.run()
}
- async let lease1ConnectionAsync = pool.leaseConnection()
+ async let connectionLeaseFuture = pool.leaseConnection()
let connection = await factory.nextConnectAttempt { connectionID in
return 1
}
- let lease1Connection = try await lease1ConnectionAsync
- XCTAssert(connection === lease1Connection)
+ let connectionLease = try await connectionLeaseFuture
+ #expect(connection === connectionLease.connection)
- pool.releaseConnection(lease1Connection)
+ connectionLease.release()
// keep alive 1
@@ -346,38 +349,38 @@ final class ConnectionPoolTests: XCTestCase {
var expectedInstants: Set = [.init(keepAliveDuration), .init(config.idleTimeout)]
let deadline1 = await clock.nextTimerScheduled()
print(deadline1)
- XCTAssertNotNil(expectedInstants.remove(deadline1))
+ #expect(expectedInstants.remove(deadline1) != nil)
let deadline2 = await clock.nextTimerScheduled()
print(deadline2)
- XCTAssertNotNil(expectedInstants.remove(deadline2))
- XCTAssert(expectedInstants.isEmpty)
+ #expect(expectedInstants.remove(deadline2) != nil)
+ #expect(expectedInstants.isEmpty)
// move clock forward to keep alive
let newTime = clock.now.advanced(by: keepAliveDuration)
clock.advance(to: newTime)
await keepAlive.nextKeepAlive { keepAliveConnection in
- XCTAssertTrue(keepAliveConnection === lease1Connection)
+ #expect(keepAliveConnection === connectionLease.connection)
return true
}
// keep alive 2
let deadline3 = await clock.nextTimerScheduled()
- XCTAssertEqual(deadline3, clock.now.advanced(by: keepAliveDuration))
+ #expect(deadline3 == clock.now.advanced(by: keepAliveDuration))
clock.advance(to: clock.now.advanced(by: keepAliveDuration))
let failingKeepAliveDidRun = ManagedAtomic(false)
// the following keep alive should not cause a crash
_ = try? await keepAlive.nextKeepAlive { keepAliveConnection in
defer {
- XCTAssertFalse(failingKeepAliveDidRun
- .compareExchange(expected: false, desired: true, ordering: .relaxed).original)
+ #expect(failingKeepAliveDidRun
+ .compareExchange(expected: false, desired: true, ordering: .relaxed).original == false)
}
- XCTAssertTrue(keepAliveConnection === lease1Connection)
+ #expect(keepAliveConnection === connectionLease.connection)
keepAliveConnection.close()
throw CancellationError() // any error
} // will fail and it's expected
- XCTAssertTrue(failingKeepAliveDidRun.load(ordering: .relaxed))
+ #expect(failingKeepAliveDidRun.load(ordering: .relaxed) == true)
taskGroup.cancelAll()
@@ -387,7 +390,8 @@ final class ConnectionPoolTests: XCTestCase {
}
}
- func testKeepAliveWorksRacesAgainstShutdown() async throws {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testKeepAliveWorksRacesAgainstShutdown() async throws {
let clock = MockClock()
let factory = MockConnectionFactory()
let keepAliveDuration = Duration.seconds(30)
@@ -415,16 +419,16 @@ final class ConnectionPoolTests: XCTestCase {
await pool.run()
}
- async let lease1ConnectionAsync = pool.leaseConnection()
+ async let connectionLeaseFuture = pool.leaseConnection()
let connection = await factory.nextConnectAttempt { connectionID in
return 1
}
- let lease1Connection = try await lease1ConnectionAsync
- XCTAssert(connection === lease1Connection)
+ let connectionLease = try await connectionLeaseFuture
+ #expect(connection === connectionLease.connection)
- pool.releaseConnection(lease1Connection)
+ connectionLease.release()
// keep alive 1
@@ -432,17 +436,17 @@ final class ConnectionPoolTests: XCTestCase {
var expectedInstants: Set = [.init(keepAliveDuration), .init(config.idleTimeout)]
let deadline1 = await clock.nextTimerScheduled()
print(deadline1)
- XCTAssertNotNil(expectedInstants.remove(deadline1))
+ #expect(expectedInstants.remove(deadline1) != nil)
let deadline2 = await clock.nextTimerScheduled()
print(deadline2)
- XCTAssertNotNil(expectedInstants.remove(deadline2))
- XCTAssert(expectedInstants.isEmpty)
+ #expect(expectedInstants.remove(deadline2) != nil)
+ #expect(expectedInstants.isEmpty)
clock.advance(to: clock.now.advanced(by: keepAliveDuration))
await keepAlive.nextKeepAlive { keepAliveConnection in
defer { print("keep alive 1 has run") }
- XCTAssertTrue(keepAliveConnection === lease1Connection)
+ #expect(keepAliveConnection === connectionLease.connection)
return true
}
@@ -455,7 +459,8 @@ final class ConnectionPoolTests: XCTestCase {
}
}
- func testCancelConnectionRequestWorks() async throws {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testCancelConnectionRequestWorks() async throws {
let clock = MockClock()
let factory = MockConnectionFactory()
let keepAliveDuration = Duration.seconds(30)
@@ -503,9 +508,9 @@ final class ConnectionPoolTests: XCTestCase {
let taskResult = await leaseTask.result
switch taskResult {
case .success:
- XCTFail("Expected task failure")
+ Issue.record("Expected task failure")
case .failure(let failure):
- XCTAssertEqual(failure as? ConnectionPoolError, .requestCancelled)
+ #expect(failure as? ConnectionPoolError == .requestCancelled)
}
taskGroup.cancelAll()
@@ -515,7 +520,8 @@ final class ConnectionPoolTests: XCTestCase {
}
}
- func testLeasingMultipleConnectionsAtOnceWorks() async throws {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testLeasingMultipleConnectionsAtOnceWorks() async throws {
let clock = MockClock()
let factory = MockConnectionFactory()
let keepAliveDuration = Duration.seconds(30)
@@ -556,19 +562,19 @@ final class ConnectionPoolTests: XCTestCase {
// lease 4 connections at once
pool.leaseConnections(requests)
- var connections = [MockConnection]()
+ var connectionLeases = [ConnectionLease]()
for request in requests {
let connection = try await request.future.success
- connections.append(connection)
+ connectionLeases.append(connection)
}
// Ensure that we got 4 distinct connections
- XCTAssertEqual(Set(connections.lazy.map(\.id)).count, 4)
+ #expect(Set(connectionLeases.lazy.map(\.connection.id)).count == 4)
// release all 4 leased connections
- for connection in connections {
- pool.releaseConnection(connection)
+ for lease in connectionLeases {
+ lease.release()
}
// shutdown
@@ -579,7 +585,8 @@ final class ConnectionPoolTests: XCTestCase {
}
}
- func testLeasingConnectionAfterShutdownIsInvokedFails() async throws {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testLeasingConnectionAfterShutdownIsInvokedFails() async throws {
let clock = MockClock()
let factory = MockConnectionFactory()
let keepAliveDuration = Duration.seconds(30)
@@ -620,10 +627,10 @@ final class ConnectionPoolTests: XCTestCase {
do {
_ = try await pool.leaseConnection()
- XCTFail("Expected a failure")
+ Issue.record("Expected a failure")
} catch {
print("failed")
- XCTAssertEqual(error as? ConnectionPoolError, .poolShutdown)
+ #expect(error as? ConnectionPoolError == .poolShutdown)
}
print("will close connections: \(factory.runningConnections)")
@@ -634,7 +641,8 @@ final class ConnectionPoolTests: XCTestCase {
}
}
- func testLeasingConnectionsAfterShutdownIsInvokedFails() async throws {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testLeasingConnectionsAfterShutdownIsInvokedFails() async throws {
let clock = MockClock()
let factory = MockConnectionFactory()
let keepAliveDuration = Duration.seconds(30)
@@ -682,9 +690,9 @@ final class ConnectionPoolTests: XCTestCase {
for request in requests {
do {
_ = try await request.future.success
- XCTFail("Expected a failure")
+ Issue.record("Expected a failure")
} catch {
- XCTAssertEqual(error as? ConnectionPoolError, .poolShutdown)
+ #expect(error as? ConnectionPoolError == .poolShutdown)
}
}
@@ -695,7 +703,8 @@ final class ConnectionPoolTests: XCTestCase {
}
}
- func testLeasingMultipleStreamsFromOneConnectionWorks() async throws {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testLeasingMultipleStreamsFromOneConnectionWorks() async throws {
let clock = MockClock()
let factory = MockConnectionFactory()
let keepAliveDuration = Duration.seconds(30)
@@ -727,7 +736,7 @@ final class ConnectionPoolTests: XCTestCase {
// create 4 connection requests
let requests = (0..<10).map { ConnectionFuture(id: $0) }
pool.leaseConnections(requests)
- var connections = [MockConnection]()
+ var connectionLeases = [ConnectionLease]()
await factory.nextConnectAttempt { connectionID in
return 10
@@ -735,15 +744,15 @@ final class ConnectionPoolTests: XCTestCase {
for request in requests {
let connection = try await request.future.success
- connections.append(connection)
+ connectionLeases.append(connection)
}
// Ensure that all requests got the same connection
- XCTAssertEqual(Set(connections.lazy.map(\.id)).count, 1)
+ #expect(Set(connectionLeases.lazy.map(\.connection.id)).count == 1)
// release all 10 leased streams
- for connection in connections {
- pool.releaseConnection(connection)
+ for lease in connectionLeases {
+ lease.release()
}
for _ in 0..<9 {
@@ -760,7 +769,8 @@ final class ConnectionPoolTests: XCTestCase {
}
}
- func testIncreasingAvailableStreamsWorks() async throws {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testIncreasingAvailableStreamsWorks() async throws {
let clock = MockClock()
let factory = MockConnectionFactory()
let keepAliveDuration = Duration.seconds(30)
@@ -792,41 +802,41 @@ final class ConnectionPoolTests: XCTestCase {
// create 4 connection requests
var requests = (0..<21).map { ConnectionFuture(id: $0) }
pool.leaseConnections(requests)
- var connections = [MockConnection]()
+ var connectionLease = [ConnectionLease]()
await factory.nextConnectAttempt { connectionID in
return 1
}
- let connection = try await requests.first!.future.success
- connections.append(connection)
+ let lease = try await requests.first!.future.success
+ connectionLease.append(lease)
requests.removeFirst()
- pool.connectionReceivedNewMaxStreamSetting(connection, newMaxStreamSetting: 21)
+ pool.connectionReceivedNewMaxStreamSetting(lease.connection, newMaxStreamSetting: 21)
for (_, request) in requests.enumerated() {
let connection = try await request.future.success
- connections.append(connection)
+ connectionLease.append(connection)
}
// Ensure that all requests got the same connection
- XCTAssertEqual(Set(connections.lazy.map(\.id)).count, 1)
+ #expect(Set(connectionLease.lazy.map(\.connection.id)).count == 1)
requests = (22..<42).map { ConnectionFuture(id: $0) }
pool.leaseConnections(requests)
// release all 21 leased streams in a single call
- pool.releaseConnection(connection, streams: 21)
+ pool.releaseConnection(lease.connection, streams: 21)
// ensure all 20 new requests got fulfilled
for request in requests {
let connection = try await request.future.success
- connections.append(connection)
+ connectionLease.append(connection)
}
// release all 20 leased streams one by one
for _ in requests {
- pool.releaseConnection(connection, streams: 1)
+ pool.releaseConnection(lease.connection, streams: 1)
}
// shutdown
@@ -840,14 +850,14 @@ final class ConnectionPoolTests: XCTestCase {
struct ConnectionFuture: ConnectionRequestProtocol {
let id: Int
- let future: Future
+ let future: Future>
init(id: Int) {
self.id = id
- self.future = Future(of: MockConnection.self)
+ self.future = Future(of: ConnectionLease.self)
}
- func complete(with result: Result) {
+ func complete(with result: Result, ConnectionPoolError>) {
switch result {
case .success(let success):
self.future.yield(value: success)
diff --git a/Tests/ConnectionPoolModuleTests/ConnectionRequestTests.swift b/Tests/ConnectionPoolModuleTests/ConnectionRequestTests.swift
index 537efbd9..b4658df8 100644
--- a/Tests/ConnectionPoolModuleTests/ConnectionRequestTests.swift
+++ b/Tests/ConnectionPoolModuleTests/ConnectionRequestTests.swift
@@ -1,28 +1,29 @@
@testable import _ConnectionPoolModule
import _ConnectionPoolTestUtils
-import XCTest
+import Testing
-final class ConnectionRequestTests: XCTestCase {
+@Suite struct ConnectionRequestTests {
- func testHappyPath() async throws {
+ @Test func testHappyPath() async throws {
let mockConnection = MockConnection(id: 1)
- let connection = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in
+ let lease = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation, any Error>) in
let request = ConnectionRequest(id: 42, continuation: continuation)
- XCTAssertEqual(request.id, 42)
- continuation.resume(with: .success(mockConnection))
+ #expect(request.id == 42)
+ let lease = ConnectionLease(connection: mockConnection) { _ in }
+ continuation.resume(with: .success(lease))
}
- XCTAssert(connection === mockConnection)
+ #expect(lease.connection === mockConnection)
}
- func testSadPath() async throws {
+ @Test func testSadPath() async throws {
do {
_ = try await withCheckedThrowingContinuation { (continuation: CheckedContinuation) in
continuation.resume(with: .failure(ConnectionPoolError.requestCancelled))
}
- XCTFail("This point should not be reached")
+ Issue.record("This point should not be reached")
} catch {
- XCTAssertEqual(error as? ConnectionPoolError, .requestCancelled)
+ #expect(error as? ConnectionPoolError == .requestCancelled)
}
}
}
diff --git a/Tests/ConnectionPoolModuleTests/Max2SequenceTests.swift b/Tests/ConnectionPoolModuleTests/Max2SequenceTests.swift
index 081e867b..ce620cc3 100644
--- a/Tests/ConnectionPoolModuleTests/Max2SequenceTests.swift
+++ b/Tests/ConnectionPoolModuleTests/Max2SequenceTests.swift
@@ -1,60 +1,61 @@
@testable import _ConnectionPoolModule
-import XCTest
+import Testing
-final class Max2SequenceTests: XCTestCase {
- func testCountAndIsEmpty() async {
+@Suite struct Max2SequenceTests {
+
+ @Test func testCountAndIsEmpty() async {
var sequence = Max2Sequence()
- XCTAssertEqual(sequence.count, 0)
- XCTAssertEqual(sequence.isEmpty, true)
+ #expect(sequence.count == 0)
+ #expect(sequence.isEmpty == true)
sequence.append(1)
- XCTAssertEqual(sequence.count, 1)
- XCTAssertEqual(sequence.isEmpty, false)
+ #expect(sequence.count == 1)
+ #expect(sequence.isEmpty == false)
sequence.append(2)
- XCTAssertEqual(sequence.count, 2)
- XCTAssertEqual(sequence.isEmpty, false)
+ #expect(sequence.count == 2)
+ #expect(sequence.isEmpty == false)
}
- func testOptionalInitializer() {
+ @Test func testOptionalInitializer() {
let emptySequence = Max2Sequence(nil, nil)
- XCTAssertEqual(emptySequence.count, 0)
- XCTAssertEqual(emptySequence.isEmpty, true)
+ #expect(emptySequence.count == 0)
+ #expect(emptySequence.isEmpty == true)
var emptySequenceIterator = emptySequence.makeIterator()
- XCTAssertNil(emptySequenceIterator.next())
- XCTAssertNil(emptySequenceIterator.next())
- XCTAssertNil(emptySequenceIterator.next())
+ #expect(emptySequenceIterator.next() == nil)
+ #expect(emptySequenceIterator.next() == nil)
+ #expect(emptySequenceIterator.next() == nil)
let oneElemSequence1 = Max2Sequence(1, nil)
- XCTAssertEqual(oneElemSequence1.count, 1)
- XCTAssertEqual(oneElemSequence1.isEmpty, false)
+ #expect(oneElemSequence1.count == 1)
+ #expect(oneElemSequence1.isEmpty == false)
var oneElemSequence1Iterator = oneElemSequence1.makeIterator()
- XCTAssertEqual(oneElemSequence1Iterator.next(), 1)
- XCTAssertNil(oneElemSequence1Iterator.next())
- XCTAssertNil(oneElemSequence1Iterator.next())
+ #expect(oneElemSequence1Iterator.next() == 1)
+ #expect(oneElemSequence1Iterator.next() == nil)
+ #expect(oneElemSequence1Iterator.next() == nil)
let oneElemSequence2 = Max2Sequence(nil, 2)
- XCTAssertEqual(oneElemSequence2.count, 1)
- XCTAssertEqual(oneElemSequence2.isEmpty, false)
+ #expect(oneElemSequence2.count == 1)
+ #expect(oneElemSequence2.isEmpty == false)
var oneElemSequence2Iterator = oneElemSequence2.makeIterator()
- XCTAssertEqual(oneElemSequence2Iterator.next(), 2)
- XCTAssertNil(oneElemSequence2Iterator.next())
- XCTAssertNil(oneElemSequence2Iterator.next())
+ #expect(oneElemSequence2Iterator.next() == 2)
+ #expect(oneElemSequence2Iterator.next() == nil)
+ #expect(oneElemSequence2Iterator.next() == nil)
let twoElemSequence = Max2Sequence(1, 2)
- XCTAssertEqual(twoElemSequence.count, 2)
- XCTAssertEqual(twoElemSequence.isEmpty, false)
+ #expect(twoElemSequence.count == 2)
+ #expect(twoElemSequence.isEmpty == false)
var twoElemSequenceIterator = twoElemSequence.makeIterator()
- XCTAssertEqual(twoElemSequenceIterator.next(), 1)
- XCTAssertEqual(twoElemSequenceIterator.next(), 2)
- XCTAssertNil(twoElemSequenceIterator.next())
+ #expect(twoElemSequenceIterator.next() == 1)
+ #expect(twoElemSequenceIterator.next() == 2)
+ #expect(twoElemSequenceIterator.next() == nil)
}
func testMap() {
let twoElemSequence = Max2Sequence(1, 2).map({ "\($0)" })
- XCTAssertEqual(twoElemSequence.count, 2)
- XCTAssertEqual(twoElemSequence.isEmpty, false)
+ #expect(twoElemSequence.count == 2)
+ #expect(twoElemSequence.isEmpty == false)
var twoElemSequenceIterator = twoElemSequence.makeIterator()
- XCTAssertEqual(twoElemSequenceIterator.next(), "1")
- XCTAssertEqual(twoElemSequenceIterator.next(), "2")
- XCTAssertNil(twoElemSequenceIterator.next())
+ #expect(twoElemSequenceIterator.next() == "1")
+ #expect(twoElemSequenceIterator.next() == "2")
+ #expect(twoElemSequenceIterator.next() == nil)
}
}
diff --git a/Tests/ConnectionPoolModuleTests/NoKeepAliveBehaviorTests.swift b/Tests/ConnectionPoolModuleTests/NoKeepAliveBehaviorTests.swift
index b1b54023..ef6b001a 100644
--- a/Tests/ConnectionPoolModuleTests/NoKeepAliveBehaviorTests.swift
+++ b/Tests/ConnectionPoolModuleTests/NoKeepAliveBehaviorTests.swift
@@ -1,11 +1,12 @@
import _ConnectionPoolModule
import _ConnectionPoolTestUtils
-import XCTest
+import Testing
-@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
-final class NoKeepAliveBehaviorTests: XCTestCase {
- func testNoKeepAlive() {
+
+@Suite struct NoKeepAliveBehaviorTests {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testNoKeepAlive() {
let keepAliveBehavior = NoOpKeepAliveBehavior(connectionType: MockConnection.self)
- XCTAssertNil(keepAliveBehavior.keepAliveFrequency)
+ #expect(keepAliveBehavior.keepAliveFrequency == nil)
}
}
diff --git a/Tests/ConnectionPoolModuleTests/PoolStateMachine+ConnectionGroupTests.swift b/Tests/ConnectionPoolModuleTests/PoolStateMachine+ConnectionGroupTests.swift
index b09bfcb4..6bfe0f39 100644
--- a/Tests/ConnectionPoolModuleTests/PoolStateMachine+ConnectionGroupTests.swift
+++ b/Tests/ConnectionPoolModuleTests/PoolStateMachine+ConnectionGroupTests.swift
@@ -1,22 +1,12 @@
@testable import _ConnectionPoolModule
import _ConnectionPoolTestUtils
-import XCTest
+import Testing
-@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
-final class PoolStateMachine_ConnectionGroupTests: XCTestCase {
- var idGenerator: ConnectionIDGenerator!
+@Suite struct PoolStateMachine_ConnectionGroupTests {
+ var idGenerator = ConnectionIDGenerator()
- override func setUp() {
- self.idGenerator = ConnectionIDGenerator()
- super.setUp()
- }
-
- override func tearDown() {
- self.idGenerator = nil
- super.tearDown()
- }
-
- func testRefillConnections() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testRefillConnections() {
var connections = TestPoolStateMachine.ConnectionGroup(
generator: self.idGenerator,
minimumConcurrentConnections: 4,
@@ -26,35 +16,36 @@ final class PoolStateMachine_ConnectionGroupTests: XCTestCase {
keepAliveReducesAvailableStreams: true
)
- XCTAssertTrue(connections.isEmpty)
+ #expect(connections.isEmpty == true)
let requests = connections.refillConnections()
- XCTAssertFalse(connections.isEmpty)
+ #expect(connections.isEmpty == false)
- XCTAssertEqual(requests.count, 4)
- XCTAssertNil(connections.createNewDemandConnectionIfPossible())
- XCTAssertNil(connections.createNewOverflowConnectionIfPossible())
- XCTAssertEqual(connections.stats, .init(connecting: 4))
- XCTAssertEqual(connections.soonAvailableConnections, 4)
+ #expect(requests.count == 4)
+ #expect(connections.createNewDemandConnectionIfPossible() == nil)
+ #expect(connections.createNewOverflowConnectionIfPossible() == nil)
+ #expect(connections.stats == .init(connecting: 4))
+ #expect(connections.soonAvailableConnections == 4)
let requests2 = connections.refillConnections()
- XCTAssertTrue(requests2.isEmpty)
+ #expect(requests2.isEmpty == true)
var connected: UInt16 = 0
for request in requests {
let newConnection = MockConnection(id: request.connectionID)
let (_, context) = connections.newConnectionEstablished(newConnection, maxStreams: 1)
- XCTAssertEqual(context.info, .idle(availableStreams: 1, newIdle: true))
- XCTAssertEqual(context.use, .persisted)
+ #expect(context.info == .idle(availableStreams: 1, newIdle: true))
+ #expect(context.use == .persisted)
connected += 1
- XCTAssertEqual(connections.stats, .init(connecting: 4 - connected, idle: connected, availableStreams: connected))
- XCTAssertEqual(connections.soonAvailableConnections, 4 - connected)
+ #expect(connections.stats == .init(connecting: 4 - connected, idle: connected, availableStreams: connected))
+ #expect(connections.soonAvailableConnections == 4 - connected)
}
let requests3 = connections.refillConnections()
- XCTAssertTrue(requests3.isEmpty)
+ #expect(requests3.isEmpty == true)
}
- func testMakeConnectionLeaseItAndDropItHappyPath() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testMakeConnectionLeaseItAndDropItHappyPath() {
var connections = TestPoolStateMachine.ConnectionGroup(
generator: self.idGenerator,
minimumConcurrentConnections: 0,
@@ -65,70 +56,77 @@ final class PoolStateMachine_ConnectionGroupTests: XCTestCase {
)
let requests = connections.refillConnections()
- XCTAssertTrue(connections.isEmpty)
- XCTAssertTrue(requests.isEmpty)
+ #expect(connections.isEmpty)
+ #expect(requests.isEmpty)
guard let request = connections.createNewDemandConnectionIfPossible() else {
- return XCTFail("Expected to receive a connection request")
+ Issue.record("Expected to receive a connection request")
+ return
}
- XCTAssertEqual(request, .init(connectionID: 0))
- XCTAssertFalse(connections.isEmpty)
- XCTAssertEqual(connections.soonAvailableConnections, 1)
- XCTAssertEqual(connections.stats, .init(connecting: 1))
+ #expect(request == .init(connectionID: 0))
+ #expect(!connections.isEmpty)
+ #expect(connections.soonAvailableConnections == 1)
+ #expect(connections.stats == .init(connecting: 1))
let newConnection = MockConnection(id: request.connectionID)
let (_, establishedContext) = connections.newConnectionEstablished(newConnection, maxStreams: 1)
- XCTAssertEqual(establishedContext.info, .idle(availableStreams: 1, newIdle: true))
- XCTAssertEqual(establishedContext.use, .demand)
- XCTAssertEqual(connections.stats, .init(idle: 1, availableStreams: 1))
- XCTAssertEqual(connections.soonAvailableConnections, 0)
+ #expect(establishedContext.info == .idle(availableStreams: 1, newIdle: true))
+ #expect(establishedContext.use == .demand)
+ #expect(connections.stats == .init(idle: 1, availableStreams: 1))
+ #expect(connections.soonAvailableConnections == 0)
guard case .leasedConnection(let leaseResult) = connections.leaseConnectionOrSoonAvailableConnectionCount() else {
- return XCTFail("Expected to lease a connection")
+ Issue.record("Expected to lease a connection")
+ return
}
- XCTAssert(newConnection === leaseResult.connection)
- XCTAssertEqual(connections.stats, .init(leased: 1, leasedStreams: 1))
+ #expect(newConnection === leaseResult.connection)
+ #expect(connections.stats == .init(leased: 1, leasedStreams: 1))
guard let (index, releasedContext) = connections.releaseConnection(leaseResult.connection.id, streams: 1) else {
- return XCTFail("Expected that this connection is still active")
+ Issue.record("Expected that this connection is still active")
+ return
}
- XCTAssertEqual(releasedContext.info, .idle(availableStreams: 1, newIdle: true))
- XCTAssertEqual(releasedContext.use, .demand)
- XCTAssertEqual(connections.stats, .init(idle: 1, availableStreams: 1))
+ #expect(releasedContext.info == .idle(availableStreams: 1, newIdle: true))
+ #expect(releasedContext.use == .demand)
+ #expect(connections.stats == .init(idle: 1, availableStreams: 1))
let parkTimers = connections.parkConnection(at: index, hasBecomeIdle: true)
- XCTAssertEqual(parkTimers, [
+ #expect(parkTimers == [
.init(timerID: 0, connectionID: newConnection.id, usecase: .keepAlive),
.init(timerID: 1, connectionID: newConnection.id, usecase: .idleTimeout),
])
guard let keepAliveAction = connections.keepAliveIfIdle(newConnection.id) else {
- return XCTFail("Expected to get a connection for ping pong")
+ Issue.record("Expected to get a connection for ping pong")
+ return
}
- XCTAssert(newConnection === keepAliveAction.connection)
- XCTAssertEqual(connections.stats, .init(idle: 1, runningKeepAlive: 1, availableStreams: 0))
+ #expect(newConnection === keepAliveAction.connection)
+ #expect(connections.stats == .init(idle: 1, runningKeepAlive: 1, availableStreams: 0))
guard let (_, pingPongContext) = connections.keepAliveSucceeded(newConnection.id) else {
- return XCTFail("Expected to get an AvailableContext")
+ Issue.record("Expected to get an AvailableContext")
+ return
}
- XCTAssertEqual(pingPongContext.info, .idle(availableStreams: 1, newIdle: false))
- XCTAssertEqual(releasedContext.use, .demand)
- XCTAssertEqual(connections.stats, .init(idle: 1, availableStreams: 1))
+ #expect(pingPongContext.info == .idle(availableStreams: 1, newIdle: false))
+ #expect(releasedContext.use == .demand)
+ #expect(connections.stats == .init(idle: 1, availableStreams: 1))
guard let closeAction = connections.closeConnectionIfIdle(newConnection.id) else {
- return XCTFail("Expected to get a connection for ping pong")
+ Issue.record("Expected to get a connection for ping pong")
+ return
}
- XCTAssertEqual(closeAction.timersToCancel, [])
- XCTAssert(closeAction.connection === newConnection)
- XCTAssertEqual(connections.stats, .init(closing: 1, availableStreams: 0))
+ #expect(closeAction.timersToCancel == [])
+ #expect(closeAction.connection === newConnection)
+ #expect(connections.stats == .init(closing: 1, availableStreams: 0))
let closeContext = connections.connectionClosed(newConnection.id)
- XCTAssertEqual(closeContext.connectionsStarting, 0)
- XCTAssertTrue(connections.isEmpty)
- XCTAssertEqual(connections.stats, .init())
+ #expect(closeContext.connectionsStarting == 0)
+ #expect(connections.isEmpty)
+ #expect(connections.stats == .init())
}
- func testBackoffDoneCreatesANewConnectionToReachMinimumConnectionsEvenThoughRetryIsSetToFalse() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testBackoffDoneCreatesANewConnectionToReachMinimumConnectionsEvenThoughRetryIsSetToFalse() {
var connections = TestPoolStateMachine.ConnectionGroup(
generator: self.idGenerator,
minimumConcurrentConnections: 1,
@@ -139,26 +137,30 @@ final class PoolStateMachine_ConnectionGroupTests: XCTestCase {
)
let requests = connections.refillConnections()
- XCTAssertEqual(connections.stats, .init(connecting: 1))
- XCTAssertEqual(connections.soonAvailableConnections, 1)
- XCTAssertFalse(connections.isEmpty)
- XCTAssertEqual(requests.count, 1)
-
- guard let request = requests.first else { return XCTFail("Expected to receive a connection request") }
- XCTAssertEqual(request, .init(connectionID: 0))
+ #expect(connections.stats == .init(connecting: 1))
+ #expect(connections.soonAvailableConnections == 1)
+ #expect(!connections.isEmpty)
+ #expect(requests.count == 1)
+
+ guard let request = requests.first else {
+ Issue.record("Expected to receive a connection request")
+ return
+ }
+ #expect(request == .init(connectionID: 0))
let backoffTimer = connections.backoffNextConnectionAttempt(request.connectionID)
- XCTAssertEqual(connections.stats, .init(backingOff: 1))
+ #expect(connections.stats == .init(backingOff: 1))
let backoffTimerCancellationToken = MockTimerCancellationToken(backoffTimer)
- XCTAssertNil(connections.timerScheduled(backoffTimer, cancelContinuation: backoffTimerCancellationToken))
+ #expect(connections.timerScheduled(backoffTimer, cancelContinuation: backoffTimerCancellationToken) == nil)
let backoffDoneAction = connections.backoffDone(request.connectionID, retry: false)
- XCTAssertEqual(backoffDoneAction, .createConnection(.init(connectionID: 0), backoffTimerCancellationToken))
+ #expect(backoffDoneAction == .createConnection(.init(connectionID: 0), backoffTimerCancellationToken))
- XCTAssertEqual(connections.stats, .init(connecting: 1))
+ #expect(connections.stats == .init(connecting: 1))
}
- func testBackoffDoneCancelsIdleTimerIfAPersistedConnectionIsNotRetried() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testBackoffDoneCancelsIdleTimerIfAPersistedConnectionIsNotRetried() {
var connections = TestPoolStateMachine.ConnectionGroup(
generator: self.idGenerator,
minimumConcurrentConnections: 2,
@@ -169,57 +171,60 @@ final class PoolStateMachine_ConnectionGroupTests: XCTestCase {
)
let requests = connections.refillConnections()
- XCTAssertEqual(connections.stats, .init(connecting: 2))
- XCTAssertEqual(connections.soonAvailableConnections, 2)
- XCTAssertFalse(connections.isEmpty)
- XCTAssertEqual(requests.count, 2)
+ #expect(connections.stats == .init(connecting: 2))
+ #expect(connections.soonAvailableConnections == 2)
+ #expect(!connections.isEmpty)
+ #expect(requests.count == 2)
var requestIterator = requests.makeIterator()
guard let firstRequest = requestIterator.next(), let secondRequest = requestIterator.next() else {
- return XCTFail("Expected to get two requests")
+ Issue.record("Expected to get two requests")
+ return
}
guard let thirdRequest = connections.createNewDemandConnectionIfPossible() else {
- return XCTFail("Expected to get another request")
+ Issue.record("Expected to get another request")
+ return
}
- XCTAssertEqual(connections.stats, .init(connecting: 3))
+ #expect(connections.stats == .init(connecting: 3))
let newSecondConnection = MockConnection(id: secondRequest.connectionID)
let (_, establishedSecondConnectionContext) = connections.newConnectionEstablished(newSecondConnection, maxStreams: 1)
- XCTAssertEqual(establishedSecondConnectionContext.info, .idle(availableStreams: 1, newIdle: true))
- XCTAssertEqual(establishedSecondConnectionContext.use, .persisted)
- XCTAssertEqual(connections.stats, .init(connecting: 2, idle: 1, availableStreams: 1))
- XCTAssertEqual(connections.soonAvailableConnections, 2)
+ #expect(establishedSecondConnectionContext.info == .idle(availableStreams: 1, newIdle: true))
+ #expect(establishedSecondConnectionContext.use == .persisted)
+ #expect(connections.stats == .init(connecting: 2, idle: 1, availableStreams: 1))
+ #expect(connections.soonAvailableConnections == 2)
let newThirdConnection = MockConnection(id: thirdRequest.connectionID)
let (thirdConnectionIndex, establishedThirdConnectionContext) = connections.newConnectionEstablished(newThirdConnection, maxStreams: 1)
- XCTAssertEqual(establishedThirdConnectionContext.info, .idle(availableStreams: 1, newIdle: true))
- XCTAssertEqual(establishedThirdConnectionContext.use, .demand)
- XCTAssertEqual(connections.stats, .init(connecting: 1, idle: 2, availableStreams: 2))
- XCTAssertEqual(connections.soonAvailableConnections, 1)
+ #expect(establishedThirdConnectionContext.info == .idle(availableStreams: 1, newIdle: true))
+ #expect(establishedThirdConnectionContext.use == .demand)
+ #expect(connections.stats == .init(connecting: 1, idle: 2, availableStreams: 2))
+ #expect(connections.soonAvailableConnections == 1)
let thirdConnKeepTimer = TestPoolStateMachine.ConnectionTimer(timerID: 0, connectionID: thirdRequest.connectionID, usecase: .keepAlive)
let thirdConnIdleTimer = TestPoolStateMachine.ConnectionTimer(timerID: 1, connectionID: thirdRequest.connectionID, usecase: .idleTimeout)
let thirdConnIdleTimerCancellationToken = MockTimerCancellationToken(thirdConnIdleTimer)
- XCTAssertEqual(connections.parkConnection(at: thirdConnectionIndex, hasBecomeIdle: true), [thirdConnKeepTimer, thirdConnIdleTimer])
+ #expect(connections.parkConnection(at: thirdConnectionIndex, hasBecomeIdle: true) == [thirdConnKeepTimer, thirdConnIdleTimer])
- XCTAssertNil(connections.timerScheduled(thirdConnKeepTimer, cancelContinuation: .init(thirdConnKeepTimer)))
- XCTAssertNil(connections.timerScheduled(thirdConnIdleTimer, cancelContinuation: thirdConnIdleTimerCancellationToken))
+ #expect(connections.timerScheduled(thirdConnKeepTimer, cancelContinuation: .init(thirdConnKeepTimer)) == nil)
+ #expect(connections.timerScheduled(thirdConnIdleTimer, cancelContinuation: thirdConnIdleTimerCancellationToken) == nil)
let backoffTimer = connections.backoffNextConnectionAttempt(firstRequest.connectionID)
- XCTAssertEqual(connections.stats, .init(backingOff: 1, idle: 2, availableStreams: 2))
+ #expect(connections.stats == .init(backingOff: 1, idle: 2, availableStreams: 2))
let backoffTimerCancellationToken = MockTimerCancellationToken(backoffTimer)
- XCTAssertNil(connections.timerScheduled(backoffTimer, cancelContinuation: backoffTimerCancellationToken))
- XCTAssertEqual(connections.stats, .init(backingOff: 1, idle: 2, availableStreams: 2))
+ #expect(connections.timerScheduled(backoffTimer, cancelContinuation: backoffTimerCancellationToken) == nil)
+ #expect(connections.stats == .init(backingOff: 1, idle: 2, availableStreams: 2))
// connection three should be moved to connection one and for this reason become permanent
- XCTAssertEqual(connections.backoffDone(firstRequest.connectionID, retry: false), .cancelTimers([backoffTimerCancellationToken, thirdConnIdleTimerCancellationToken]))
- XCTAssertEqual(connections.stats, .init(idle: 2, availableStreams: 2))
+ #expect(connections.backoffDone(firstRequest.connectionID, retry: false) == .cancelTimers([backoffTimerCancellationToken, thirdConnIdleTimerCancellationToken]))
+ #expect(connections.stats == .init(idle: 2, availableStreams: 2))
- XCTAssertNil(connections.closeConnectionIfIdle(newThirdConnection.id))
+ #expect(connections.closeConnectionIfIdle(newThirdConnection.id) == nil)
}
- func testBackoffDoneReturnsNilIfOverflowConnection() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testBackoffDoneReturnsNilIfOverflowConnection() {
var connections = TestPoolStateMachine.ConnectionGroup(
generator: self.idGenerator,
minimumConcurrentConnections: 0,
@@ -230,33 +235,36 @@ final class PoolStateMachine_ConnectionGroupTests: XCTestCase {
)
guard let firstRequest = connections.createNewDemandConnectionIfPossible() else {
- return XCTFail("Expected to get two requests")
+ Issue.record("Expected to get two requests")
+ return
}
guard let secondRequest = connections.createNewDemandConnectionIfPossible() else {
- return XCTFail("Expected to get another request")
+ Issue.record("Expected to get another request")
+ return
}
- XCTAssertEqual(connections.stats, .init(connecting: 2))
+ #expect(connections.stats == .init(connecting: 2))
let newFirstConnection = MockConnection(id: firstRequest.connectionID)
let (_, establishedFirstConnectionContext) = connections.newConnectionEstablished(newFirstConnection, maxStreams: 1)
- XCTAssertEqual(establishedFirstConnectionContext.info, .idle(availableStreams: 1, newIdle: true))
- XCTAssertEqual(establishedFirstConnectionContext.use, .demand)
- XCTAssertEqual(connections.stats, .init(connecting: 1, idle: 1, availableStreams: 1))
- XCTAssertEqual(connections.soonAvailableConnections, 1)
+ #expect(establishedFirstConnectionContext.info == .idle(availableStreams: 1, newIdle: true))
+ #expect(establishedFirstConnectionContext.use == .demand)
+ #expect(connections.stats == .init(connecting: 1, idle: 1, availableStreams: 1))
+ #expect(connections.soonAvailableConnections == 1)
let backoffTimer = connections.backoffNextConnectionAttempt(secondRequest.connectionID)
let backoffTimerCancellationToken = MockTimerCancellationToken(backoffTimer)
- XCTAssertEqual(connections.stats, .init(backingOff: 1, idle: 1, availableStreams: 1))
- XCTAssertNil(connections.timerScheduled(backoffTimer, cancelContinuation: backoffTimerCancellationToken))
+ #expect(connections.stats == .init(backingOff: 1, idle: 1, availableStreams: 1))
+ #expect(connections.timerScheduled(backoffTimer, cancelContinuation: backoffTimerCancellationToken) == nil)
- XCTAssertEqual(connections.backoffDone(secondRequest.connectionID, retry: false), .cancelTimers([backoffTimerCancellationToken]))
- XCTAssertEqual(connections.stats, .init(idle: 1, availableStreams: 1))
+ #expect(connections.backoffDone(secondRequest.connectionID, retry: false) == .cancelTimers([backoffTimerCancellationToken]))
+ #expect(connections.stats == .init(idle: 1, availableStreams: 1))
- XCTAssertNotNil(connections.closeConnectionIfIdle(newFirstConnection.id))
+ #expect(connections.closeConnectionIfIdle(newFirstConnection.id) != nil)
}
- func testPingPong() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testPingPong() {
var connections = TestPoolStateMachine.ConnectionGroup(
generator: self.idGenerator,
minimumConcurrentConnections: 1,
@@ -267,35 +275,40 @@ final class PoolStateMachine_ConnectionGroupTests: XCTestCase {
)
let requests = connections.refillConnections()
- XCTAssertFalse(connections.isEmpty)
- XCTAssertEqual(connections.stats, .init(connecting: 1))
+ #expect(!connections.isEmpty)
+ #expect(connections.stats == .init(connecting: 1))
- XCTAssertEqual(requests.count, 1)
- guard let firstRequest = requests.first else { return XCTFail("Expected to have a request here") }
+ #expect(requests.count == 1)
+ guard let firstRequest = requests.first else {
+ Issue.record("Expected to have a request here")
+ return
+ }
let newConnection = MockConnection(id: firstRequest.connectionID)
let (connectionIndex, establishedConnectionContext) = connections.newConnectionEstablished(newConnection, maxStreams: 1)
- XCTAssertEqual(establishedConnectionContext.info, .idle(availableStreams: 1, newIdle: true))
- XCTAssertEqual(establishedConnectionContext.use, .persisted)
- XCTAssertEqual(connections.stats, .init(idle: 1, availableStreams: 1))
+ #expect(establishedConnectionContext.info == .idle(availableStreams: 1, newIdle: true))
+ #expect(establishedConnectionContext.use == .persisted)
+ #expect(connections.stats == .init(idle: 1, availableStreams: 1))
let timers = connections.parkConnection(at: connectionIndex, hasBecomeIdle: true)
let keepAliveTimer = TestPoolStateMachine.ConnectionTimer(timerID: 0, connectionID: firstRequest.connectionID, usecase: .keepAlive)
let keepAliveTimerCancellationToken = MockTimerCancellationToken(keepAliveTimer)
- XCTAssertEqual(timers, [keepAliveTimer])
- XCTAssertNil(connections.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken))
+ #expect(timers == [keepAliveTimer])
+ #expect(connections.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken) == nil)
let keepAliveAction = connections.keepAliveIfIdle(newConnection.id)
- XCTAssertEqual(keepAliveAction, .init(connection: newConnection, keepAliveTimerCancellationContinuation: keepAliveTimerCancellationToken))
- XCTAssertEqual(connections.stats, .init(idle: 1, runningKeepAlive: 1, availableStreams: 0))
+ #expect(keepAliveAction == .init(connection: newConnection, keepAliveTimerCancellationContinuation: keepAliveTimerCancellationToken))
+ #expect(connections.stats == .init(idle: 1, runningKeepAlive: 1, availableStreams: 0))
guard let (_, afterPingIdleContext) = connections.keepAliveSucceeded(newConnection.id) else {
- return XCTFail("Expected to receive an AvailableContext")
+ Issue.record("Expected to receive an AvailableContext")
+ return
}
- XCTAssertEqual(afterPingIdleContext.info, .idle(availableStreams: 1, newIdle: false))
- XCTAssertEqual(afterPingIdleContext.use, .persisted)
- XCTAssertEqual(connections.stats, .init(idle: 1, availableStreams: 1))
+ #expect(afterPingIdleContext.info == .idle(availableStreams: 1, newIdle: false))
+ #expect(afterPingIdleContext.use == .persisted)
+ #expect(connections.stats == .init(idle: 1, availableStreams: 1))
}
- func testKeepAliveShouldNotIndicateCloseConnectionAfterClosed() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testKeepAliveShouldNotIndicateCloseConnectionAfterClosed() {
var connections = TestPoolStateMachine.ConnectionGroup(
generator: self.idGenerator,
minimumConcurrentConnections: 0,
@@ -305,24 +318,28 @@ final class PoolStateMachine_ConnectionGroupTests: XCTestCase {
keepAliveReducesAvailableStreams: true
)
- guard let firstRequest = connections.createNewDemandConnectionIfPossible() else { return XCTFail("Expected to have a request here") }
+ guard let firstRequest = connections.createNewDemandConnectionIfPossible() else {
+ Issue.record("Expected to have a request here")
+ return
+ }
let newConnection = MockConnection(id: firstRequest.connectionID)
let (connectionIndex, establishedConnectionContext) = connections.newConnectionEstablished(newConnection, maxStreams: 1)
- XCTAssertEqual(establishedConnectionContext.info, .idle(availableStreams: 1, newIdle: true))
- XCTAssertEqual(connections.stats, .init(idle: 1, availableStreams: 1))
+ #expect(establishedConnectionContext.info == .idle(availableStreams: 1, newIdle: true))
+ #expect(connections.stats == .init(idle: 1, availableStreams: 1))
_ = connections.parkConnection(at: connectionIndex, hasBecomeIdle: true)
let keepAliveTimer = TestPoolStateMachine.ConnectionTimer(timerID: 0, connectionID: firstRequest.connectionID, usecase: .keepAlive)
let keepAliveTimerCancellationToken = MockTimerCancellationToken(keepAliveTimer)
- XCTAssertNil(connections.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken))
+ #expect(connections.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken) == nil)
let keepAliveAction = connections.keepAliveIfIdle(newConnection.id)
- XCTAssertEqual(keepAliveAction, .init(connection: newConnection, keepAliveTimerCancellationContinuation: keepAliveTimerCancellationToken))
- XCTAssertEqual(connections.stats, .init(idle: 1, runningKeepAlive: 1, availableStreams: 0))
+ #expect(keepAliveAction == .init(connection: newConnection, keepAliveTimerCancellationContinuation: keepAliveTimerCancellationToken))
+ #expect(connections.stats == .init(idle: 1, runningKeepAlive: 1, availableStreams: 0))
_ = connections.closeConnectionIfIdle(newConnection.id)
guard connections.keepAliveFailed(newConnection.id) == nil else {
- return XCTFail("Expected keepAliveFailed not to cause close again")
+ Issue.record("Expected keepAliveFailed not to cause close again")
+ return
}
- XCTAssertEqual(connections.stats, .init(closing: 1))
+ #expect(connections.stats == .init(closing: 1))
}
}
diff --git a/Tests/ConnectionPoolModuleTests/PoolStateMachine+ConnectionStateTests.swift b/Tests/ConnectionPoolModuleTests/PoolStateMachine+ConnectionStateTests.swift
index 7dd2b726..2d81cf38 100644
--- a/Tests/ConnectionPoolModuleTests/PoolStateMachine+ConnectionStateTests.swift
+++ b/Tests/ConnectionPoolModuleTests/PoolStateMachine+ConnectionStateTests.swift
@@ -1,36 +1,36 @@
@testable import _ConnectionPoolModule
import _ConnectionPoolTestUtils
-import XCTest
+import Testing
-@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
-final class PoolStateMachine_ConnectionStateTests: XCTestCase {
+@Suite struct PoolStateMachine_ConnectionStateTests {
typealias TestConnectionState = TestPoolStateMachine.ConnectionState
- func testStartupLeaseReleaseParkLease() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testStartupLeaseReleaseParkLease() {
let connectionID = 1
var state = TestConnectionState(id: connectionID)
- XCTAssertEqual(state.id, connectionID)
- XCTAssertEqual(state.isIdle, false)
- XCTAssertEqual(state.isAvailable, false)
- XCTAssertEqual(state.isConnected, false)
- XCTAssertEqual(state.isLeased, false)
+ #expect(state.id == connectionID)
+ #expect(!state.isIdle)
+ #expect(!state.isAvailable)
+ #expect(!state.isConnected)
+ #expect(!state.isLeased)
let connection = MockConnection(id: connectionID)
- XCTAssertEqual(state.connected(connection, maxStreams: 1), .idle(availableStreams: 1, newIdle: true))
- XCTAssertEqual(state.isIdle, true)
- XCTAssertEqual(state.isAvailable, true)
- XCTAssertEqual(state.isConnected, true)
- XCTAssertEqual(state.isLeased, false)
- XCTAssertEqual(state.lease(streams: 1), .init(connection: connection, timersToCancel: .init(), wasIdle: true))
-
- XCTAssertEqual(state.isIdle, false)
- XCTAssertEqual(state.isAvailable, false)
- XCTAssertEqual(state.isConnected, true)
- XCTAssertEqual(state.isLeased, true)
-
- XCTAssertEqual(state.release(streams: 1), .idle(availableStreams: 1, newIdle: true))
+ #expect(state.connected(connection, maxStreams: 1) == .idle(availableStreams: 1, newIdle: true))
+ #expect(state.isIdle)
+ #expect(state.isAvailable)
+ #expect(state.isConnected)
+ #expect(state.isLeased == false)
+ #expect(state.lease(streams: 1) == .init(connection: connection, timersToCancel: .init(), wasIdle: true))
+
+ #expect(!state.isIdle)
+ #expect(!state.isAvailable)
+ #expect(state.isConnected)
+ #expect(state.isLeased)
+
+ #expect(state.release(streams: 1) == .idle(availableStreams: 1, newIdle: true))
let parkResult = state.parkConnection(scheduleKeepAliveTimer: true, scheduleIdleTimeoutTimer: true)
- XCTAssert(
+ #expect(
parkResult.elementsEqual([
.init(timerID: 0, connectionID: connectionID, usecase: .keepAlive),
.init(timerID: 1, connectionID: connectionID, usecase: .idleTimeout)
@@ -38,31 +38,33 @@ final class PoolStateMachine_ConnectionStateTests: XCTestCase {
)
guard let keepAliveTimer = parkResult.first, let idleTimer = parkResult.second else {
- return XCTFail("Expected to get two timers")
+ Issue.record("Expected to get two timers")
+ return
}
let keepAliveTimerCancellationToken = MockTimerCancellationToken(keepAliveTimer)
let idleTimerCancellationToken = MockTimerCancellationToken(idleTimer)
- XCTAssertNil(state.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken))
- XCTAssertNil(state.timerScheduled(idleTimer, cancelContinuation: idleTimerCancellationToken))
+ #expect(state.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken) == nil)
+ #expect(state.timerScheduled(idleTimer, cancelContinuation: idleTimerCancellationToken) == nil)
let expectLeaseAction = TestConnectionState.LeaseAction(
connection: connection,
timersToCancel: [idleTimerCancellationToken, keepAliveTimerCancellationToken],
wasIdle: true
)
- XCTAssertEqual(state.lease(streams: 1), expectLeaseAction)
+ #expect(state.lease(streams: 1) == expectLeaseAction)
}
- func testStartupParkLeaseBeforeTimersRegistered() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testStartupParkLeaseBeforeTimersRegistered() {
let connectionID = 1
var state = TestConnectionState(id: connectionID)
let connection = MockConnection(id: connectionID)
- XCTAssertEqual(state.connected(connection, maxStreams: 1), .idle(availableStreams: 1, newIdle: true))
+ #expect(state.connected(connection, maxStreams: 1) == .idle(availableStreams: 1, newIdle: true))
let parkResult = state.parkConnection(scheduleKeepAliveTimer: true, scheduleIdleTimeoutTimer: true)
- XCTAssertEqual(
- parkResult,
+ #expect(
+ parkResult ==
[
.init(timerID: 0, connectionID: connectionID, usecase: .keepAlive),
.init(timerID: 1, connectionID: connectionID, usecase: .idleTimeout)
@@ -70,24 +72,26 @@ final class PoolStateMachine_ConnectionStateTests: XCTestCase {
)
guard let keepAliveTimer = parkResult.first, let idleTimer = parkResult.second else {
- return XCTFail("Expected to get two timers")
+ Issue.record("Expected to get two timers")
+ return
}
let keepAliveTimerCancellationToken = MockTimerCancellationToken(keepAliveTimer)
let idleTimerCancellationToken = MockTimerCancellationToken(idleTimer)
- XCTAssertEqual(state.lease(streams: 1), .init(connection: connection, timersToCancel: .init(), wasIdle: true))
+ #expect(state.lease(streams: 1) == .init(connection: connection, timersToCancel: .init(), wasIdle: true))
- XCTAssertEqual(state.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken), keepAliveTimerCancellationToken)
- XCTAssertEqual(state.timerScheduled(idleTimer, cancelContinuation: idleTimerCancellationToken), idleTimerCancellationToken)
+ #expect(state.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken) == keepAliveTimerCancellationToken)
+ #expect(state.timerScheduled(idleTimer, cancelContinuation: idleTimerCancellationToken) == idleTimerCancellationToken)
}
- func testStartupParkLeasePark() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testStartupParkLeasePark() {
let connectionID = 1
var state = TestConnectionState(id: connectionID)
let connection = MockConnection(id: connectionID)
- XCTAssertEqual(state.connected(connection, maxStreams: 1), .idle(availableStreams: 1, newIdle: true))
+ #expect(state.connected(connection, maxStreams: 1) == .idle(availableStreams: 1, newIdle: true))
let parkResult = state.parkConnection(scheduleKeepAliveTimer: true, scheduleIdleTimeoutTimer: true)
- XCTAssert(
+ #expect(
parkResult.elementsEqual([
.init(timerID: 0, connectionID: connectionID, usecase: .keepAlive),
.init(timerID: 1, connectionID: connectionID, usecase: .idleTimeout)
@@ -95,171 +99,186 @@ final class PoolStateMachine_ConnectionStateTests: XCTestCase {
)
guard let keepAliveTimer = parkResult.first, let idleTimer = parkResult.second else {
- return XCTFail("Expected to get two timers")
+ Issue.record("Expected to get two timers")
+ return
}
let initialKeepAliveTimerCancellationToken = MockTimerCancellationToken(keepAliveTimer)
let initialIdleTimerCancellationToken = MockTimerCancellationToken(idleTimer)
- XCTAssertEqual(state.lease(streams: 1), .init(connection: connection, timersToCancel: .init(), wasIdle: true))
+ #expect(state.lease(streams: 1) == .init(connection: connection, timersToCancel: .init(), wasIdle: true))
- XCTAssertEqual(state.release(streams: 1), .idle(availableStreams: 1, newIdle: true))
- XCTAssertEqual(
- state.parkConnection(scheduleKeepAliveTimer: true, scheduleIdleTimeoutTimer: true),
+ #expect(state.release(streams: 1) == .idle(availableStreams: 1, newIdle: true))
+ #expect(
+ state.parkConnection(scheduleKeepAliveTimer: true, scheduleIdleTimeoutTimer: true) ==
[
.init(timerID: 2, connectionID: connectionID, usecase: .keepAlive),
.init(timerID: 3, connectionID: connectionID, usecase: .idleTimeout)
]
)
- XCTAssertEqual(state.timerScheduled(keepAliveTimer, cancelContinuation: initialKeepAliveTimerCancellationToken), initialKeepAliveTimerCancellationToken)
- XCTAssertEqual(state.timerScheduled(idleTimer, cancelContinuation: initialIdleTimerCancellationToken), initialIdleTimerCancellationToken)
+ #expect(state.timerScheduled(keepAliveTimer, cancelContinuation: initialKeepAliveTimerCancellationToken) == initialKeepAliveTimerCancellationToken)
+ #expect(state.timerScheduled(idleTimer, cancelContinuation: initialIdleTimerCancellationToken) == initialIdleTimerCancellationToken)
}
- func testStartupFailed() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testStartupFailed() {
let connectionID = 1
var state = TestConnectionState(id: connectionID)
let firstBackoffTimer = state.failedToConnect()
let firstBackoffTimerCancellationToken = MockTimerCancellationToken(firstBackoffTimer)
- XCTAssertNil(state.timerScheduled(firstBackoffTimer, cancelContinuation: firstBackoffTimerCancellationToken))
- XCTAssertEqual(state.retryConnect(), firstBackoffTimerCancellationToken)
+ #expect(state.timerScheduled(firstBackoffTimer, cancelContinuation: firstBackoffTimerCancellationToken) == nil)
+ #expect(state.retryConnect() == firstBackoffTimerCancellationToken)
let secondBackoffTimer = state.failedToConnect()
let secondBackoffTimerCancellationToken = MockTimerCancellationToken(secondBackoffTimer)
- XCTAssertNil(state.retryConnect())
- XCTAssertEqual(
- state.timerScheduled(secondBackoffTimer, cancelContinuation: secondBackoffTimerCancellationToken),
+ #expect(state.retryConnect() == nil)
+ #expect(
+ state.timerScheduled(secondBackoffTimer, cancelContinuation: secondBackoffTimerCancellationToken) ==
secondBackoffTimerCancellationToken
)
let thirdBackoffTimer = state.failedToConnect()
let thirdBackoffTimerCancellationToken = MockTimerCancellationToken(thirdBackoffTimer)
- XCTAssertNil(state.retryConnect())
+ #expect(state.retryConnect() == nil)
let forthBackoffTimer = state.failedToConnect()
let forthBackoffTimerCancellationToken = MockTimerCancellationToken(forthBackoffTimer)
- XCTAssertEqual(
- state.timerScheduled(thirdBackoffTimer, cancelContinuation: thirdBackoffTimerCancellationToken),
+ #expect(
+ state.timerScheduled(thirdBackoffTimer, cancelContinuation: thirdBackoffTimerCancellationToken) ==
thirdBackoffTimerCancellationToken
)
- XCTAssertNil(
- state.timerScheduled(forthBackoffTimer, cancelContinuation: forthBackoffTimerCancellationToken)
+ #expect(
+ state.timerScheduled(forthBackoffTimer, cancelContinuation: forthBackoffTimerCancellationToken) == nil
)
- XCTAssertEqual(state.retryConnect(), forthBackoffTimerCancellationToken)
+ #expect(state.retryConnect() == forthBackoffTimerCancellationToken)
let connection = MockConnection(id: connectionID)
- XCTAssertEqual(state.connected(connection, maxStreams: 1), .idle(availableStreams: 1, newIdle: true))
+ #expect(state.connected(connection, maxStreams: 1) == .idle(availableStreams: 1, newIdle: true))
}
- func testLeaseMultipleStreams() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testLeaseMultipleStreams() {
let connectionID = 1
var state = TestConnectionState(id: connectionID)
let connection = MockConnection(id: connectionID)
- XCTAssertEqual(state.connected(connection, maxStreams: 100), .idle(availableStreams: 100, newIdle: true))
+ #expect(state.connected(connection, maxStreams: 100) == .idle(availableStreams: 100, newIdle: true))
let timers = state.parkConnection(scheduleKeepAliveTimer: true, scheduleIdleTimeoutTimer: false)
- guard let keepAliveTimer = timers.first else { return XCTFail("Expected to get a keepAliveTimer") }
+ guard let keepAliveTimer = timers.first else {
+ Issue.record("Expected to get a keepAliveTimer")
+ return
+ }
let keepAliveTimerCancellationToken = MockTimerCancellationToken(keepAliveTimer)
- XCTAssertNil(state.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken))
+ #expect(state.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken) == nil)
- XCTAssertEqual(
- state.lease(streams: 30),
+ #expect(
+ state.lease(streams: 30) ==
TestConnectionState.LeaseAction(connection: connection, timersToCancel: [keepAliveTimerCancellationToken], wasIdle: true)
)
- XCTAssertEqual(state.release(streams: 10), .leased(availableStreams: 80))
+ #expect(state.release(streams: 10) == .leased(availableStreams: 80))
- XCTAssertEqual(
- state.lease(streams: 40),
+ #expect(
+ state.lease(streams: 40) ==
TestConnectionState.LeaseAction(connection: connection, timersToCancel: [], wasIdle: false)
)
- XCTAssertEqual(
- state.lease(streams: 40),
+ #expect(
+ state.lease(streams: 40) ==
TestConnectionState.LeaseAction(connection: connection, timersToCancel: [], wasIdle: false)
)
- XCTAssertEqual(state.release(streams: 1), .leased(availableStreams: 1))
- XCTAssertEqual(state.release(streams: 98), .leased(availableStreams: 99))
- XCTAssertEqual(state.release(streams: 1), .idle(availableStreams: 100, newIdle: true))
+ #expect(state.release(streams: 1) == .leased(availableStreams: 1))
+ #expect(state.release(streams: 98) == .leased(availableStreams: 99))
+ #expect(state.release(streams: 1) == .idle(availableStreams: 100, newIdle: true))
}
- func testRunningKeepAliveReducesAvailableStreams() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testRunningKeepAliveReducesAvailableStreams() {
let connectionID = 1
var state = TestConnectionState(id: connectionID)
let connection = MockConnection(id: connectionID)
- XCTAssertEqual(state.connected(connection, maxStreams: 100), .idle(availableStreams: 100, newIdle: true))
+ #expect(state.connected(connection, maxStreams: 100) == .idle(availableStreams: 100, newIdle: true))
let timers = state.parkConnection(scheduleKeepAliveTimer: true, scheduleIdleTimeoutTimer: false)
- guard let keepAliveTimer = timers.first else { return XCTFail("Expected to get a keepAliveTimer") }
+ guard let keepAliveTimer = timers.first else {
+ Issue.record("Expected to get a keepAliveTimer")
+ return
+ }
let keepAliveTimerCancellationToken = MockTimerCancellationToken(keepAliveTimer)
- XCTAssertNil(state.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken))
+ #expect(state.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken) == nil)
- XCTAssertEqual(
- state.runKeepAliveIfIdle(reducesAvailableStreams: true),
+ #expect(
+ state.runKeepAliveIfIdle(reducesAvailableStreams: true) ==
.init(connection: connection, keepAliveTimerCancellationContinuation: keepAliveTimerCancellationToken)
)
- XCTAssertEqual(
- state.lease(streams: 30),
+ #expect(
+ state.lease(streams: 30) ==
TestConnectionState.LeaseAction(connection: connection, timersToCancel: [], wasIdle: true)
)
- XCTAssertEqual(state.release(streams: 10), .leased(availableStreams: 79))
- XCTAssertEqual(state.isAvailable, true)
- XCTAssertEqual(
- state.lease(streams: 79),
+ #expect(state.release(streams: 10) == .leased(availableStreams: 79))
+ #expect(state.isAvailable)
+ #expect(
+ state.lease(streams: 79) ==
TestConnectionState.LeaseAction(connection: connection, timersToCancel: [], wasIdle: false)
)
- XCTAssertEqual(state.isAvailable, false)
- XCTAssertEqual(state.keepAliveSucceeded(), .leased(availableStreams: 1))
- XCTAssertEqual(state.isAvailable, true)
+ #expect(!state.isAvailable)
+ #expect(state.keepAliveSucceeded() == .leased(availableStreams: 1))
+ #expect(state.isAvailable)
}
- func testRunningKeepAliveDoesNotReduceAvailableStreams() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testRunningKeepAliveDoesNotReduceAvailableStreams() {
let connectionID = 1
var state = TestConnectionState(id: connectionID)
let connection = MockConnection(id: connectionID)
- XCTAssertEqual(state.connected(connection, maxStreams: 100), .idle(availableStreams: 100, newIdle: true))
+ #expect(state.connected(connection, maxStreams: 100) == .idle(availableStreams: 100, newIdle: true))
let timers = state.parkConnection(scheduleKeepAliveTimer: true, scheduleIdleTimeoutTimer: false)
- guard let keepAliveTimer = timers.first else { return XCTFail("Expected to get a keepAliveTimer") }
+ guard let keepAliveTimer = timers.first else {
+ Issue.record("Expected to get a keepAliveTimer")
+ return
+ }
let keepAliveTimerCancellationToken = MockTimerCancellationToken(keepAliveTimer)
- XCTAssertNil(state.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken))
+ #expect(state.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken) == nil)
- XCTAssertEqual(
- state.runKeepAliveIfIdle(reducesAvailableStreams: false),
+ #expect(
+ state.runKeepAliveIfIdle(reducesAvailableStreams: false) ==
.init(connection: connection, keepAliveTimerCancellationContinuation: keepAliveTimerCancellationToken)
)
- XCTAssertEqual(
- state.lease(streams: 30),
+ #expect(
+ state.lease(streams: 30) ==
TestConnectionState.LeaseAction(connection: connection, timersToCancel: [], wasIdle: true)
)
- XCTAssertEqual(state.release(streams: 10), .leased(availableStreams: 80))
- XCTAssertEqual(state.keepAliveSucceeded(), .leased(availableStreams: 80))
+ #expect(state.release(streams: 10) == .leased(availableStreams: 80))
+ #expect(state.keepAliveSucceeded() == .leased(availableStreams: 80))
}
- func testRunKeepAliveRacesAgainstIdleClose() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testRunKeepAliveRacesAgainstIdleClose() {
let connectionID = 1
var state = TestConnectionState(id: connectionID)
let connection = MockConnection(id: connectionID)
- XCTAssertEqual(state.connected(connection, maxStreams: 1), .idle(availableStreams: 1, newIdle: true))
+ #expect(state.connected(connection, maxStreams: 1) == .idle(availableStreams: 1, newIdle: true))
let parkResult = state.parkConnection(scheduleKeepAliveTimer: true, scheduleIdleTimeoutTimer: true)
guard let keepAliveTimer = parkResult.first, let idleTimer = parkResult.second else {
- return XCTFail("Expected to get two timers")
+ Issue.record("Expected to get two timers")
+ return
}
- XCTAssertEqual(keepAliveTimer, .init(timerID: 0, connectionID: connectionID, usecase: .keepAlive))
- XCTAssertEqual(idleTimer, .init(timerID: 1, connectionID: connectionID, usecase: .idleTimeout))
+ #expect(keepAliveTimer == .init(timerID: 0, connectionID: connectionID, usecase: .keepAlive))
+ #expect(idleTimer == .init(timerID: 1, connectionID: connectionID, usecase: .idleTimeout))
let keepAliveTimerCancellationToken = MockTimerCancellationToken(keepAliveTimer)
let idleTimerCancellationToken = MockTimerCancellationToken(idleTimer)
- XCTAssertNil(state.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken))
- XCTAssertNil(state.timerScheduled(idleTimer, cancelContinuation: idleTimerCancellationToken))
-
- XCTAssertEqual(state.closeIfIdle(), .init(connection: connection, previousConnectionState: .idle, cancelTimers: [keepAliveTimerCancellationToken, idleTimerCancellationToken], usedStreams: 0, maxStreams: 1, runningKeepAlive: false))
- XCTAssertEqual(state.runKeepAliveIfIdle(reducesAvailableStreams: true), .none)
+ #expect(state.timerScheduled(keepAliveTimer, cancelContinuation: keepAliveTimerCancellationToken) == nil)
+ #expect(state.timerScheduled(idleTimer, cancelContinuation: idleTimerCancellationToken) == nil)
+ #expect(state.closeIfIdle() == .init(connection: connection, previousConnectionState: .idle, cancelTimers: [keepAliveTimerCancellationToken, idleTimerCancellationToken], usedStreams: 0, maxStreams: 1, runningKeepAlive: false))
+ #expect(state.runKeepAliveIfIdle(reducesAvailableStreams: true) == .none)
}
}
diff --git a/Tests/ConnectionPoolModuleTests/PoolStateMachine+RequestQueueTests.swift b/Tests/ConnectionPoolModuleTests/PoolStateMachine+RequestQueueTests.swift
index b74b86cc..458c6b3f 100644
--- a/Tests/ConnectionPoolModuleTests/PoolStateMachine+RequestQueueTests.swift
+++ b/Tests/ConnectionPoolModuleTests/PoolStateMachine+RequestQueueTests.swift
@@ -1,148 +1,152 @@
@testable import _ConnectionPoolModule
import _ConnectionPoolTestUtils
-import XCTest
+import Testing
-@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
-final class PoolStateMachine_RequestQueueTests: XCTestCase {
+@Suite struct PoolStateMachine_RequestQueueTests {
typealias TestQueue = TestPoolStateMachine.RequestQueue
- func testHappyPath() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testHappyPath() {
var queue = TestQueue()
- XCTAssert(queue.isEmpty)
+ #expect(queue.isEmpty)
- let request1 = MockRequest()
+ let request1 = MockRequest(connectionType: MockConnection.self)
queue.queue(request1)
- XCTAssertEqual(queue.count, 1)
- XCTAssertFalse(queue.isEmpty)
+ #expect(queue.count == 1)
+ #expect(!queue.isEmpty)
let popResult = queue.pop(max: 3)
- XCTAssert(popResult.elementsEqual([request1]))
- XCTAssert(queue.isEmpty)
- XCTAssertEqual(queue.count, 0)
+ #expect(popResult.elementsEqual([request1]))
+ #expect(queue.isEmpty)
+ #expect(queue.count == 0)
}
- func testEnqueueAndPopMultipleRequests() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testEnqueueAndPopMultipleRequests() {
var queue = TestQueue()
- XCTAssert(queue.isEmpty)
+ #expect(queue.isEmpty)
- var request1 = MockRequest()
+ var request1 = MockRequest(connectionType: MockConnection.self)
queue.queue(request1)
- var request2 = MockRequest()
+ var request2 = MockRequest(connectionType: MockConnection.self)
queue.queue(request2)
- var request3 = MockRequest()
+ var request3 = MockRequest(connectionType: MockConnection.self)
queue.queue(request3)
do {
- XCTAssertEqual(queue.count, 3)
- XCTAssertFalse(queue.isEmpty)
+ #expect(queue.count == 3)
+ #expect(!queue.isEmpty)
let popResult = queue.pop(max: 3)
- XCTAssert(popResult.elementsEqual([request1, request2, request3]))
- XCTAssert(queue.isEmpty)
- XCTAssertEqual(queue.count, 0)
+ #expect(popResult.elementsEqual([request1, request2, request3]))
+ #expect(queue.isEmpty)
+ #expect(queue.count == 0)
}
- XCTAssert(isKnownUniquelyReferenced(&request1))
- XCTAssert(isKnownUniquelyReferenced(&request2))
- XCTAssert(isKnownUniquelyReferenced(&request3))
+ #expect(isKnownUniquelyReferenced(&request1))
+ #expect(isKnownUniquelyReferenced(&request2))
+ #expect(isKnownUniquelyReferenced(&request3))
}
- func testEnqueueAndPopOnlyOne() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testEnqueueAndPopOnlyOne() {
var queue = TestQueue()
- XCTAssert(queue.isEmpty)
+ #expect(queue.isEmpty)
- var request1 = MockRequest()
+ var request1 = MockRequest(connectionType: MockConnection.self)
queue.queue(request1)
- var request2 = MockRequest()
+ var request2 = MockRequest(connectionType: MockConnection.self)
queue.queue(request2)
- var request3 = MockRequest()
+ var request3 = MockRequest(connectionType: MockConnection.self)
queue.queue(request3)
do {
- XCTAssertEqual(queue.count, 3)
- XCTAssertFalse(queue.isEmpty)
+ #expect(queue.count == 3)
+ #expect(!queue.isEmpty)
let popResult = queue.pop(max: 1)
- XCTAssert(popResult.elementsEqual([request1]))
- XCTAssertFalse(queue.isEmpty)
- XCTAssertEqual(queue.count, 2)
+ #expect(popResult.elementsEqual([request1]))
+ #expect(!queue.isEmpty)
+ #expect(queue.count == 2)
let removeAllResult = queue.removeAll()
- XCTAssert(Set(removeAllResult) == [request2, request3])
+ #expect(Set(removeAllResult) == [request2, request3])
}
- XCTAssert(isKnownUniquelyReferenced(&request1))
- XCTAssert(isKnownUniquelyReferenced(&request2))
- XCTAssert(isKnownUniquelyReferenced(&request3))
+ #expect(isKnownUniquelyReferenced(&request1))
+ #expect(isKnownUniquelyReferenced(&request2))
+ #expect(isKnownUniquelyReferenced(&request3))
}
- func testCancellation() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testCancellation() {
var queue = TestQueue()
- XCTAssert(queue.isEmpty)
+ #expect(queue.isEmpty)
- var request1 = MockRequest()
+ var request1 = MockRequest(connectionType: MockConnection.self)
queue.queue(request1)
- var request2 = MockRequest()
+ var request2 = MockRequest(connectionType: MockConnection.self)
queue.queue(request2)
- var request3 = MockRequest()
+ var request3 = MockRequest(connectionType: MockConnection.self)
queue.queue(request3)
do {
- XCTAssertEqual(queue.count, 3)
+ #expect(queue.count == 3)
let returnedRequest2 = queue.remove(request2.id)
- XCTAssert(returnedRequest2 === request2)
- XCTAssertEqual(queue.count, 2)
- XCTAssertFalse(queue.isEmpty)
+ #expect(returnedRequest2 === request2)
+ #expect(queue.count == 2)
+ #expect(!queue.isEmpty)
}
// still retained by the deque inside the queue
- XCTAssertEqual(queue.requests.count, 2)
- XCTAssertEqual(queue.queue.count, 3)
+ #expect(queue.requests.count == 2)
+ #expect(queue.queue.count == 3)
do {
- XCTAssertEqual(queue.count, 2)
- XCTAssertFalse(queue.isEmpty)
+ #expect(queue.count == 2)
+ #expect(!queue.isEmpty)
let popResult = queue.pop(max: 3)
- XCTAssert(popResult.elementsEqual([request1, request3]))
- XCTAssert(queue.isEmpty)
- XCTAssertEqual(queue.count, 0)
+ #expect(popResult.elementsEqual([request1, request3]))
+ #expect(queue.isEmpty)
+ #expect(queue.count == 0)
}
- XCTAssert(isKnownUniquelyReferenced(&request1))
- XCTAssert(isKnownUniquelyReferenced(&request2))
- XCTAssert(isKnownUniquelyReferenced(&request3))
+ #expect(isKnownUniquelyReferenced(&request1))
+ #expect(isKnownUniquelyReferenced(&request2))
+ #expect(isKnownUniquelyReferenced(&request3))
}
- func testRemoveAllAfterCancellation() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testRemoveAllAfterCancellation() {
var queue = TestQueue()
- XCTAssert(queue.isEmpty)
+ #expect(queue.isEmpty)
- var request1 = MockRequest()
+ var request1 = MockRequest(connectionType: MockConnection.self)
queue.queue(request1)
- var request2 = MockRequest()
+ var request2 = MockRequest(connectionType: MockConnection.self)
queue.queue(request2)
- var request3 = MockRequest()
+ var request3 = MockRequest(connectionType: MockConnection.self)
queue.queue(request3)
do {
- XCTAssertEqual(queue.count, 3)
+ #expect(queue.count == 3)
let returnedRequest2 = queue.remove(request2.id)
- XCTAssert(returnedRequest2 === request2)
- XCTAssertEqual(queue.count, 2)
- XCTAssertFalse(queue.isEmpty)
+ #expect(returnedRequest2 === request2)
+ #expect(queue.count == 2)
+ #expect(!queue.isEmpty)
}
// still retained by the deque inside the queue
- XCTAssertEqual(queue.requests.count, 2)
- XCTAssertEqual(queue.queue.count, 3)
+ #expect(queue.requests.count == 2)
+ #expect(queue.queue.count == 3)
do {
- XCTAssertEqual(queue.count, 2)
- XCTAssertFalse(queue.isEmpty)
+ #expect(queue.count == 2)
+ #expect(!queue.isEmpty)
let removeAllResult = queue.removeAll()
- XCTAssert(Set(removeAllResult) == [request1, request3])
- XCTAssert(queue.isEmpty)
- XCTAssertEqual(queue.count, 0)
+ #expect(Set(removeAllResult) == [request1, request3])
+ #expect(queue.isEmpty)
+ #expect(queue.count == 0)
}
- XCTAssert(isKnownUniquelyReferenced(&request1))
- XCTAssert(isKnownUniquelyReferenced(&request2))
- XCTAssert(isKnownUniquelyReferenced(&request3))
+ #expect(isKnownUniquelyReferenced(&request1))
+ #expect(isKnownUniquelyReferenced(&request2))
+ #expect(isKnownUniquelyReferenced(&request3))
}
}
diff --git a/Tests/ConnectionPoolModuleTests/PoolStateMachineTests.swift b/Tests/ConnectionPoolModuleTests/PoolStateMachineTests.swift
index c0b6ddcd..c748de28 100644
--- a/Tests/ConnectionPoolModuleTests/PoolStateMachineTests.swift
+++ b/Tests/ConnectionPoolModuleTests/PoolStateMachineTests.swift
@@ -1,21 +1,21 @@
@testable import _ConnectionPoolModule
import _ConnectionPoolTestUtils
-import XCTest
+import Testing
@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
typealias TestPoolStateMachine = PoolStateMachine<
MockConnection,
ConnectionIDGenerator,
MockConnection.ID,
- MockRequest,
- MockRequest.ID,
+ MockRequest,
+ MockRequest.ID,
MockTimerCancellationToken
>
-@available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
-final class PoolStateMachineTests: XCTestCase {
+@Suite struct PoolStateMachineTests {
- func testConnectionsAreCreatedAndParkedOnStartup() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testConnectionsAreCreatedAndParkedOnStartup() {
var configuration = PoolConfiguration()
configuration.minimumConnectionCount = 2
configuration.maximumConnectionSoftLimit = 4
@@ -33,25 +33,26 @@ final class PoolStateMachineTests: XCTestCase {
do {
let requests = stateMachine.refillConnections()
- XCTAssertEqual(requests.count, 2)
+ #expect(requests.count == 2)
let createdAction1 = stateMachine.connectionEstablished(connection1, maxStreams: 1)
let connection1KeepAliveTimer = TestPoolStateMachine.Timer(.init(timerID: 0, connectionID: 0, usecase: .keepAlive), duration: .seconds(10))
let connection1KeepAliveTimerCancellationToken = MockTimerCancellationToken(connection1KeepAliveTimer)
- XCTAssertEqual(createdAction1.request, .none)
- XCTAssertEqual(createdAction1.connection, .scheduleTimers([connection1KeepAliveTimer]))
+ #expect(createdAction1.request == .none)
+ #expect(createdAction1.connection == .scheduleTimers([connection1KeepAliveTimer]))
- XCTAssertEqual(stateMachine.timerScheduled(connection1KeepAliveTimer, cancelContinuation: connection1KeepAliveTimerCancellationToken), .none)
+ #expect(stateMachine.timerScheduled(connection1KeepAliveTimer, cancelContinuation: connection1KeepAliveTimerCancellationToken) == .none)
let createdAction2 = stateMachine.connectionEstablished(connection2, maxStreams: 1)
let connection2KeepAliveTimer = TestPoolStateMachine.Timer(.init(timerID: 0, connectionID: 1, usecase: .keepAlive), duration: .seconds(10))
let connection2KeepAliveTimerCancellationToken = MockTimerCancellationToken(connection2KeepAliveTimer)
- XCTAssertEqual(createdAction2.request, .none)
- XCTAssertEqual(createdAction2.connection, .scheduleTimers([connection2KeepAliveTimer]))
- XCTAssertEqual(stateMachine.timerScheduled(connection2KeepAliveTimer, cancelContinuation: connection2KeepAliveTimerCancellationToken), .none)
+ #expect(createdAction2.request == .none)
+ #expect(createdAction2.connection == .scheduleTimers([connection2KeepAliveTimer]))
+ #expect(stateMachine.timerScheduled(connection2KeepAliveTimer, cancelContinuation: connection2KeepAliveTimerCancellationToken) == .none)
}
}
- func testConnectionsNoKeepAliveRun() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testConnectionsNoKeepAliveRun() {
var configuration = PoolConfiguration()
configuration.minimumConnectionCount = 1
configuration.maximumConnectionSoftLimit = 4
@@ -69,51 +70,52 @@ final class PoolStateMachineTests: XCTestCase {
// refill pool to at least one connection
let requests = stateMachine.refillConnections()
- XCTAssertEqual(requests.count, 1)
+ #expect(requests.count == 1)
let createdAction1 = stateMachine.connectionEstablished(connection1, maxStreams: 1)
- XCTAssertEqual(createdAction1.request, .none)
- XCTAssertEqual(createdAction1.connection, .scheduleTimers([]))
+ #expect(createdAction1.request == .none)
+ #expect(createdAction1.connection == .scheduleTimers([]))
// lease connection 1
- let request1 = MockRequest()
+ let request1 = MockRequest(connectionType: MockConnection.self)
let leaseRequest1 = stateMachine.leaseConnection(request1)
- XCTAssertEqual(leaseRequest1.connection, .cancelTimers([]))
- XCTAssertEqual(leaseRequest1.request, .leaseConnection(.init(element: request1), connection1))
+ #expect(leaseRequest1.connection == .cancelTimers([]))
+ #expect(leaseRequest1.request == .leaseConnection(.init(element: request1), connection1))
// release connection 1
- XCTAssertEqual(stateMachine.releaseConnection(connection1, streams: 1), .none())
+ #expect(stateMachine.releaseConnection(connection1, streams: 1) == .none())
// lease connection 1
- let request2 = MockRequest()
+ let request2 = MockRequest(connectionType: MockConnection.self)
let leaseRequest2 = stateMachine.leaseConnection(request2)
- XCTAssertEqual(leaseRequest2.connection, .cancelTimers([]))
- XCTAssertEqual(leaseRequest2.request, .leaseConnection(.init(element: request2), connection1))
+ #expect(leaseRequest2.connection == .cancelTimers([]))
+ #expect(leaseRequest2.request == .leaseConnection(.init(element: request2), connection1))
// request connection while none is available
- let request3 = MockRequest()
+ let request3 = MockRequest(connectionType: MockConnection.self)
let leaseRequest3 = stateMachine.leaseConnection(request3)
- XCTAssertEqual(leaseRequest3.connection, .makeConnection(.init(connectionID: 1), []))
- XCTAssertEqual(leaseRequest3.request, .none)
+ #expect(leaseRequest3.connection == .makeConnection(.init(connectionID: 1), []))
+ #expect(leaseRequest3.request == .none)
// make connection 2 and lease immediately
let connection2 = MockConnection(id: 1)
let createdAction2 = stateMachine.connectionEstablished(connection2, maxStreams: 1)
- XCTAssertEqual(createdAction2.request, .leaseConnection(.init(element: request3), connection2))
- XCTAssertEqual(createdAction2.connection, .none)
+ #expect(createdAction2.request == .leaseConnection(.init(element: request3), connection2))
+ #expect(createdAction2.connection == .none)
// release connection 2
let connection2IdleTimer = TestPoolStateMachine.Timer(.init(timerID: 0, connectionID: 1, usecase: .idleTimeout), duration: configuration.idleTimeoutDuration)
let connection2IdleTimerCancellationToken = MockTimerCancellationToken(connection2IdleTimer)
- XCTAssertEqual(
- stateMachine.releaseConnection(connection2, streams: 1),
+ #expect(
+ stateMachine.releaseConnection(connection2, streams: 1) ==
.init(request: .none, connection: .scheduleTimers([connection2IdleTimer]))
)
- XCTAssertEqual(stateMachine.timerScheduled(connection2IdleTimer, cancelContinuation: connection2IdleTimerCancellationToken), .none)
- XCTAssertEqual(stateMachine.timerTriggered(connection2IdleTimer), .init(request: .none, connection: .closeConnection(connection2, [connection2IdleTimerCancellationToken])))
+ #expect(stateMachine.timerScheduled(connection2IdleTimer, cancelContinuation: connection2IdleTimerCancellationToken) == .none)
+ #expect(stateMachine.timerTriggered(connection2IdleTimer) == .init(request: .none, connection: .closeConnection(connection2, [connection2IdleTimerCancellationToken])))
}
- func testOnlyOverflowConnections() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testOnlyOverflowConnections() {
var configuration = PoolConfiguration()
configuration.minimumConnectionCount = 0
configuration.maximumConnectionSoftLimit = 0
@@ -129,49 +131,50 @@ final class PoolStateMachineTests: XCTestCase {
// don't refill pool
let requests = stateMachine.refillConnections()
- XCTAssertEqual(requests.count, 0)
+ #expect(requests.count == 0)
// request connection while none exists
- let request1 = MockRequest()
+ let request1 = MockRequest(connectionType: MockConnection.self)
let leaseRequest1 = stateMachine.leaseConnection(request1)
- XCTAssertEqual(leaseRequest1.connection, .makeConnection(.init(connectionID: 0), []))
- XCTAssertEqual(leaseRequest1.request, .none)
+ #expect(leaseRequest1.connection == .makeConnection(.init(connectionID: 0), []))
+ #expect(leaseRequest1.request == .none)
// make connection 1 and lease immediately
let connection1 = MockConnection(id: 0)
let createdAction1 = stateMachine.connectionEstablished(connection1, maxStreams: 1)
- XCTAssertEqual(createdAction1.request, .leaseConnection(.init(element: request1), connection1))
- XCTAssertEqual(createdAction1.connection, .none)
+ #expect(createdAction1.request == .leaseConnection(.init(element: request1), connection1))
+ #expect(createdAction1.connection == .none)
// request connection while none is available
- let request2 = MockRequest()
+ let request2 = MockRequest(connectionType: MockConnection.self)
let leaseRequest2 = stateMachine.leaseConnection(request2)
- XCTAssertEqual(leaseRequest2.connection, .makeConnection(.init(connectionID: 1), []))
- XCTAssertEqual(leaseRequest2.request, .none)
+ #expect(leaseRequest2.connection == .makeConnection(.init(connectionID: 1), []))
+ #expect(leaseRequest2.request == .none)
// release connection 1 should be leased again immediately
let releaseRequest1 = stateMachine.releaseConnection(connection1, streams: 1)
- XCTAssertEqual(releaseRequest1.request, .leaseConnection(.init(element: request2), connection1))
- XCTAssertEqual(releaseRequest1.connection, .none)
+ #expect(releaseRequest1.request == .leaseConnection(.init(element: request2), connection1))
+ #expect(releaseRequest1.connection == .none)
// connection 2 comes up and should be closed right away
let connection2 = MockConnection(id: 1)
let createdAction2 = stateMachine.connectionEstablished(connection2, maxStreams: 1)
- XCTAssertEqual(createdAction2.request, .none)
- XCTAssertEqual(createdAction2.connection, .closeConnection(connection2, []))
- XCTAssertEqual(stateMachine.connectionClosed(connection2), .none())
+ #expect(createdAction2.request == .none)
+ #expect(createdAction2.connection == .closeConnection(connection2, []))
+ #expect(stateMachine.connectionClosed(connection2) == .none())
// release connection 1 should be closed as well
let releaseRequest2 = stateMachine.releaseConnection(connection1, streams: 1)
- XCTAssertEqual(releaseRequest2.request, .none)
- XCTAssertEqual(releaseRequest2.connection, .closeConnection(connection1, []))
+ #expect(releaseRequest2.request == .none)
+ #expect(releaseRequest2.connection == .closeConnection(connection1, []))
let shutdownAction = stateMachine.triggerForceShutdown()
- XCTAssertEqual(shutdownAction.request, .failRequests(.init(), .poolShutdown))
- XCTAssertEqual(shutdownAction.connection, .shutdown(.init()))
+ #expect(shutdownAction.request == .failRequests(.init(), .poolShutdown))
+ #expect(shutdownAction.connection == .shutdown(.init()))
}
- func testDemandConnectionIsMadePermanentIfPermanentIsClose() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testDemandConnectionIsMadePermanentIfPermanentIsClose() {
var configuration = PoolConfiguration()
configuration.minimumConnectionCount = 1
configuration.maximumConnectionSoftLimit = 2
@@ -189,44 +192,45 @@ final class PoolStateMachineTests: XCTestCase {
// refill pool to at least one connection
let requests = stateMachine.refillConnections()
- XCTAssertEqual(requests.count, 1)
+ #expect(requests.count == 1)
let createdAction1 = stateMachine.connectionEstablished(connection1, maxStreams: 1)
- XCTAssertEqual(createdAction1.request, .none)
- XCTAssertEqual(createdAction1.connection, .scheduleTimers([]))
+ #expect(createdAction1.request == .none)
+ #expect(createdAction1.connection == .scheduleTimers([]))
// lease connection 1
- let request1 = MockRequest()
+ let request1 = MockRequest(connectionType: MockConnection.self)
let leaseRequest1 = stateMachine.leaseConnection(request1)
- XCTAssertEqual(leaseRequest1.connection, .cancelTimers([]))
- XCTAssertEqual(leaseRequest1.request, .leaseConnection(.init(element: request1), connection1))
+ #expect(leaseRequest1.connection == .cancelTimers([]))
+ #expect(leaseRequest1.request == .leaseConnection(.init(element: request1), connection1))
// request connection while none is available
- let request2 = MockRequest()
+ let request2 = MockRequest(connectionType: MockConnection.self)
let leaseRequest2 = stateMachine.leaseConnection(request2)
- XCTAssertEqual(leaseRequest2.connection, .makeConnection(.init(connectionID: 1), []))
- XCTAssertEqual(leaseRequest2.request, .none)
+ #expect(leaseRequest2.connection == .makeConnection(.init(connectionID: 1), []))
+ #expect(leaseRequest2.request == .none)
// make connection 2 and lease immediately
let connection2 = MockConnection(id: 1)
let createdAction2 = stateMachine.connectionEstablished(connection2, maxStreams: 1)
- XCTAssertEqual(createdAction2.request, .leaseConnection(.init(element: request2), connection2))
- XCTAssertEqual(createdAction2.connection, .none)
+ #expect(createdAction2.request == .leaseConnection(.init(element: request2), connection2))
+ #expect(createdAction2.connection == .none)
// release connection 2
let connection2IdleTimer = TestPoolStateMachine.Timer(.init(timerID: 0, connectionID: 1, usecase: .idleTimeout), duration: configuration.idleTimeoutDuration)
let connection2IdleTimerCancellationToken = MockTimerCancellationToken(connection2IdleTimer)
- XCTAssertEqual(
- stateMachine.releaseConnection(connection2, streams: 1),
+ #expect(
+ stateMachine.releaseConnection(connection2, streams: 1) ==
.init(request: .none, connection: .scheduleTimers([connection2IdleTimer]))
)
- XCTAssertEqual(stateMachine.timerScheduled(connection2IdleTimer, cancelContinuation: connection2IdleTimerCancellationToken), .none)
+ #expect(stateMachine.timerScheduled(connection2IdleTimer, cancelContinuation: connection2IdleTimerCancellationToken) == .none)
// connection 1 is dropped
- XCTAssertEqual(stateMachine.connectionClosed(connection1), .init(request: .none, connection: .cancelTimers([connection2IdleTimerCancellationToken])))
+ #expect(stateMachine.connectionClosed(connection1) == .init(request: .none, connection: .cancelTimers([connection2IdleTimerCancellationToken])))
}
- func testReleaseLoosesRaceAgainstClosed() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testReleaseLoosesRaceAgainstClosed() {
var configuration = PoolConfiguration()
configuration.minimumConnectionCount = 0
configuration.maximumConnectionSoftLimit = 2
@@ -242,32 +246,33 @@ final class PoolStateMachineTests: XCTestCase {
// don't refill pool
let requests = stateMachine.refillConnections()
- XCTAssertEqual(requests.count, 0)
+ #expect(requests.count == 0)
// request connection while none exists
- let request1 = MockRequest()
+ let request1 = MockRequest(connectionType: MockConnection.self)
let leaseRequest1 = stateMachine.leaseConnection(request1)
- XCTAssertEqual(leaseRequest1.connection, .makeConnection(.init(connectionID: 0), []))
- XCTAssertEqual(leaseRequest1.request, .none)
+ #expect(leaseRequest1.connection == .makeConnection(.init(connectionID: 0), []))
+ #expect(leaseRequest1.request == .none)
// make connection 1 and lease immediately
let connection1 = MockConnection(id: 0)
let createdAction1 = stateMachine.connectionEstablished(connection1, maxStreams: 1)
- XCTAssertEqual(createdAction1.request, .leaseConnection(.init(element: request1), connection1))
- XCTAssertEqual(createdAction1.connection, .none)
+ #expect(createdAction1.request == .leaseConnection(.init(element: request1), connection1))
+ #expect(createdAction1.connection == .none)
// connection got closed
let closedAction = stateMachine.connectionClosed(connection1)
- XCTAssertEqual(closedAction.connection, .none)
- XCTAssertEqual(closedAction.request, .none)
+ #expect(closedAction.connection == .none)
+ #expect(closedAction.request == .none)
// release connection 1 should be leased again immediately
let releaseRequest1 = stateMachine.releaseConnection(connection1, streams: 1)
- XCTAssertEqual(releaseRequest1.request, .none)
- XCTAssertEqual(releaseRequest1.connection, .none)
+ #expect(releaseRequest1.request == .none)
+ #expect(releaseRequest1.connection == .none)
}
- func testKeepAliveOnClosingConnection() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testKeepAliveOnClosingConnection() {
var configuration = PoolConfiguration()
configuration.minimumConnectionCount = 0
configuration.maximumConnectionSoftLimit = 2
@@ -275,7 +280,6 @@ final class PoolStateMachineTests: XCTestCase {
configuration.keepAliveDuration = .seconds(2)
configuration.idleTimeoutDuration = .seconds(4)
-
var stateMachine = TestPoolStateMachine(
configuration: configuration,
generator: .init(),
@@ -284,46 +288,46 @@ final class PoolStateMachineTests: XCTestCase {
// don't refill pool
let requests = stateMachine.refillConnections()
- XCTAssertEqual(requests.count, 0)
+ #expect(requests.count == 0)
// request connection while none exists
- let request1 = MockRequest()
+ let request1 = MockRequest(connectionType: MockConnection.self)
let leaseRequest1 = stateMachine.leaseConnection(request1)
- XCTAssertEqual(leaseRequest1.connection, .makeConnection(.init(connectionID: 0), []))
- XCTAssertEqual(leaseRequest1.request, .none)
+ #expect(leaseRequest1.connection == .makeConnection(.init(connectionID: 0), []))
+ #expect(leaseRequest1.request == .none)
// make connection 1
let connection1 = MockConnection(id: 0)
let createdAction1 = stateMachine.connectionEstablished(connection1, maxStreams: 1)
- XCTAssertEqual(createdAction1.request, .leaseConnection(.init(element: request1), connection1))
- XCTAssertEqual(createdAction1.connection, .none)
+ #expect(createdAction1.request == .leaseConnection(.init(element: request1), connection1))
+ #expect(createdAction1.connection == .none)
_ = stateMachine.releaseConnection(connection1, streams: 1)
// trigger keep alive
let keepAliveAction1 = stateMachine.connectionKeepAliveTimerTriggered(connection1.id)
- XCTAssertEqual(keepAliveAction1.connection, .runKeepAlive(connection1, nil))
+ #expect(keepAliveAction1.connection == .runKeepAlive(connection1, nil))
// fail keep alive and cause closed
let keepAliveFailed1 = stateMachine.connectionKeepAliveFailed(connection1.id)
- XCTAssertEqual(keepAliveFailed1.connection, .closeConnection(connection1, []))
+ #expect(keepAliveFailed1.connection == .closeConnection(connection1, []))
connection1.closeIfClosing()
// request connection while none exists anymore
- let request2 = MockRequest()
+ let request2 = MockRequest(connectionType: MockConnection.self)
let leaseRequest2 = stateMachine.leaseConnection(request2)
- XCTAssertEqual(leaseRequest2.connection, .makeConnection(.init(connectionID: 1), []))
- XCTAssertEqual(leaseRequest2.request, .none)
+ #expect(leaseRequest2.connection == .makeConnection(.init(connectionID: 1), []))
+ #expect(leaseRequest2.request == .none)
// make connection 2
let connection2 = MockConnection(id: 1)
let createdAction2 = stateMachine.connectionEstablished(connection2, maxStreams: 1)
- XCTAssertEqual(createdAction2.request, .leaseConnection(.init(element: request2), connection2))
- XCTAssertEqual(createdAction2.connection, .none)
+ #expect(createdAction2.request == .leaseConnection(.init(element: request2), connection2))
+ #expect(createdAction2.connection == .none)
_ = stateMachine.releaseConnection(connection2, streams: 1)
// trigger keep alive while connection is still open
let keepAliveAction2 = stateMachine.connectionKeepAliveTimerTriggered(connection2.id)
- XCTAssertEqual(keepAliveAction2.connection, .runKeepAlive(connection2, nil))
+ #expect(keepAliveAction2.connection == .runKeepAlive(connection2, nil))
// close connection in the middle of keep alive
connection2.close()
@@ -331,10 +335,11 @@ final class PoolStateMachineTests: XCTestCase {
// fail keep alive and cause closed
let keepAliveFailed2 = stateMachine.connectionKeepAliveFailed(connection2.id)
- XCTAssertEqual(keepAliveFailed2.connection, .closeConnection(connection2, []))
+ #expect(keepAliveFailed2.connection == .closeConnection(connection2, []))
}
- func testConnectionIsEstablishedAfterFailedKeepAliveIfNotEnoughConnectionsLeft() {
+ @available(macOS 13.0, iOS 16.0, tvOS 16.0, watchOS 9.0, *)
+ @Test func testConnectionIsEstablishedAfterFailedKeepAliveIfNotEnoughConnectionsLeft() {
var configuration = PoolConfiguration()
configuration.minimumConnectionCount = 1
configuration.maximumConnectionSoftLimit = 2
@@ -351,35 +356,38 @@ final class PoolStateMachineTests: XCTestCase {
// refill pool
let requests = stateMachine.refillConnections()
- XCTAssertEqual(requests.count, 1)
+ #expect(requests.count == 1)
// one connection should exist
- let request = MockRequest()
+ let request = MockRequest(connectionType: MockConnection.self)
let leaseRequest = stateMachine.leaseConnection(request)
- XCTAssertEqual(leaseRequest.connection, .none)
- XCTAssertEqual(leaseRequest.request, .none)
+ #expect(leaseRequest.connection == .none)
+ #expect(leaseRequest.request == .none)
// make connection 1
let connection = MockConnection(id: 0)
let createdAction = stateMachine.connectionEstablished(connection, maxStreams: 1)
- XCTAssertEqual(createdAction.request, .leaseConnection(.init(element: request), connection))
- XCTAssertEqual(createdAction.connection, .none)
+ #expect(createdAction.request == .leaseConnection(.init(element: request), connection))
+ #expect(createdAction.connection == .none)
_ = stateMachine.releaseConnection(connection, streams: 1)
// trigger keep alive
let keepAliveAction = stateMachine.connectionKeepAliveTimerTriggered(connection.id)
- XCTAssertEqual(keepAliveAction.connection, .runKeepAlive(connection, nil))
+ #expect(keepAliveAction.connection == .runKeepAlive(connection, nil))
// fail keep alive, cause closed and make new connection
let keepAliveFailed = stateMachine.connectionKeepAliveFailed(connection.id)
- XCTAssertEqual(keepAliveFailed.connection, .closeConnection(connection, []))
+ #expect(keepAliveFailed.connection == .closeConnection(connection, []))
let connectionClosed = stateMachine.connectionClosed(connection)
- XCTAssertEqual(connectionClosed.connection, .makeConnection(.init(connectionID: 1), []))
+ #expect(connectionClosed.connection == .makeConnection(.init(connectionID: 1), []))
connection.closeIfClosing()
let establishAction = stateMachine.connectionEstablished(.init(id: 1), maxStreams: 1)
- XCTAssertEqual(establishAction.request, .none)
- guard case .scheduleTimers(let timers) = establishAction.connection else { return XCTFail("Unexpected connection action") }
- XCTAssertEqual(timers, [.init(.init(timerID: 0, connectionID: 1, usecase: .keepAlive), duration: configuration.keepAliveDuration!)])
+ #expect(establishAction.request == .none)
+ if case .scheduleTimers(let timers) = establishAction.connection {
+ #expect(timers == [.init(.init(timerID: 0, connectionID: 1, usecase: .keepAlive), duration: configuration.keepAliveDuration!)])
+ } else {
+ Issue.record("Unexpected connection action")
+ }
}
}
diff --git a/Tests/ConnectionPoolModuleTests/TinyFastSequenceTests.swift b/Tests/ConnectionPoolModuleTests/TinyFastSequenceTests.swift
index 1a2836b9..9dfac549 100644
--- a/Tests/ConnectionPoolModuleTests/TinyFastSequenceTests.swift
+++ b/Tests/ConnectionPoolModuleTests/TinyFastSequenceTests.swift
@@ -1,72 +1,84 @@
@testable import _ConnectionPoolModule
-import XCTest
+import Testing
-final class TinyFastSequenceTests: XCTestCase {
- func testCountIsEmptyAndIterator() async {
+@Suite struct TinyFastSequenceTests {
+ @Test func testCountIsEmptyAndIterator() {
var sequence = TinyFastSequence()
- XCTAssertEqual(sequence.count, 0)
- XCTAssertEqual(sequence.isEmpty, true)
- XCTAssertEqual(sequence.first, nil)
- XCTAssertEqual(Array(sequence), [])
+ #expect(sequence.count == 0)
+ #expect(sequence.isEmpty == true)
+ #expect(sequence.first == nil)
+ #expect(Array(sequence) == [])
sequence.append(1)
- XCTAssertEqual(sequence.count, 1)
- XCTAssertEqual(sequence.isEmpty, false)
- XCTAssertEqual(sequence.first, 1)
- XCTAssertEqual(Array(sequence), [1])
+ #expect(sequence.count == 1)
+ #expect(sequence.isEmpty == false)
+ #expect(sequence.first == 1)
+ #expect(Array(sequence) == [1])
sequence.append(2)
- XCTAssertEqual(sequence.count, 2)
- XCTAssertEqual(sequence.isEmpty, false)
- XCTAssertEqual(sequence.first, 1)
- XCTAssertEqual(Array(sequence), [1, 2])
+ #expect(sequence.count == 2)
+ #expect(sequence.isEmpty == false)
+ #expect(sequence.first == 1)
+ #expect(Array(sequence) == [1, 2])
sequence.append(3)
- XCTAssertEqual(sequence.count, 3)
- XCTAssertEqual(sequence.isEmpty, false)
- XCTAssertEqual(sequence.first, 1)
- XCTAssertEqual(Array(sequence), [1, 2, 3])
+ #expect(sequence.count == 3)
+ #expect(sequence.isEmpty == false)
+ #expect(sequence.first == 1)
+ #expect(Array(sequence) == [1, 2, 3])
}
- func testReserveCapacityIsForwarded() {
+ @Test func testReserveCapacityIsForwarded() {
var emptySequence = TinyFastSequence()
emptySequence.reserveCapacity(8)
emptySequence.append(1)
emptySequence.append(2)
emptySequence.append(3)
guard case .n(let array) = emptySequence.base else {
- return XCTFail("Expected sequence to be backed by an array")
+ Issue.record("Expected sequence to be backed by an array")
+ return
}
- XCTAssertEqual(array.capacity, 8)
+ #expect(array.capacity >= 8)
var oneElemSequence = TinyFastSequence(element: 1)
oneElemSequence.reserveCapacity(8)
oneElemSequence.append(2)
oneElemSequence.append(3)
guard case .n(let array) = oneElemSequence.base else {
- return XCTFail("Expected sequence to be backed by an array")
+ Issue.record("Expected sequence to be backed by an array")
+ return
}
- XCTAssertEqual(array.capacity, 8)
+ #expect(array.capacity >= 8)
var twoElemSequence = TinyFastSequence([1, 2])
twoElemSequence.reserveCapacity(8)
+ twoElemSequence.append(3)
guard case .n(let array) = twoElemSequence.base else {
- return XCTFail("Expected sequence to be backed by an array")
+ Issue.record("Expected sequence to be backed by an array")
+ return
}
- XCTAssertEqual(array.capacity, 8)
+ #expect(array.capacity >= 8)
+
+ var threeElemSequence = TinyFastSequence([1, 2, 3])
+ threeElemSequence.reserveCapacity(8)
+ guard case .n(let array) = threeElemSequence.base else {
+ Issue.record("Expected sequence to be backed by an array")
+ return
+ }
+ #expect(array.capacity >= 8)
}
- func testNewSequenceSlowPath() {
+ @Test func testNewSequenceSlowPath() {
let sequence = TinyFastSequence("AB".utf8)
- XCTAssertEqual(Array(sequence), [UInt8(ascii: "A"), UInt8(ascii: "B")])
+ #expect(Array(sequence) == [UInt8(ascii: "A"), UInt8(ascii: "B")])
}
- func testSingleItem() {
+ @Test func testSingleItem() {
let sequence = TinyFastSequence("A".utf8)
- XCTAssertEqual(Array(sequence), [UInt8(ascii: "A")])
+ #expect(Array(sequence) == [UInt8(ascii: "A")])
}
- func testEmptyCollection() {
+ @Test func testEmptyCollection() {
let sequence = TinyFastSequence("".utf8)
- XCTAssertTrue(sequence.isEmpty)
- XCTAssertEqual(sequence.count, 0)
- XCTAssertEqual(Array(sequence), [])
+ #expect(sequence.isEmpty == true)
+ #expect(sequence.count == 0)
+ #expect(Array(sequence) == [])
}
}
diff --git a/Tests/IntegrationTests/PostgresClientTests.swift b/Tests/IntegrationTests/PostgresClientTests.swift
index eaf3663f..9ac92754 100644
--- a/Tests/IntegrationTests/PostgresClientTests.swift
+++ b/Tests/IntegrationTests/PostgresClientTests.swift
@@ -338,7 +338,7 @@ final class PostgresClientTests: XCTestCase {
)
var count = 0
- for try await (id, label) in rows.decode((Int, String).self) {
+ for try await _ in rows.decode((Int, String).self) {
count += 1
}
XCTAssertEqual(count, 1)
diff --git a/Tests/IntegrationTests/PostgresNIOTests.swift b/Tests/IntegrationTests/PostgresNIOTests.swift
index 9a58f050..5d27e36a 100644
--- a/Tests/IntegrationTests/PostgresNIOTests.swift
+++ b/Tests/IntegrationTests/PostgresNIOTests.swift
@@ -930,6 +930,22 @@ final class PostgresNIOTests: XCTestCase {
}
}
+ func testJSONBDecodeString() {
+ var conn: PostgresConnection?
+ XCTAssertNoThrow(conn = try PostgresConnection.test(on: eventLoop).wait())
+ defer { XCTAssertNoThrow(try conn?.close().wait()) }
+
+ do {
+ var rows: PostgresQueryResult?
+ XCTAssertNoThrow(rows = try conn?.query("select '{\"hello\": \"world\"}'::jsonb as data").wait())
+
+ var resultString: String?
+ XCTAssertNoThrow(resultString = try rows?.first?.decode(String.self, context: .default))
+
+ XCTAssertEqual(resultString, "{\"hello\": \"world\"}")
+ }
+ }
+
func testInt4RangeSerialize() async throws {
let conn: PostgresConnection = try await PostgresConnection.test(on: eventLoop).get()
self.addTeardownBlock {
diff --git a/Tests/PostgresNIOTests/New/Connection State Machine/AuthenticationStateMachineTests.swift b/Tests/PostgresNIOTests/New/Connection State Machine/AuthenticationStateMachineTests.swift
index df881f90..99f7f5e9 100644
--- a/Tests/PostgresNIOTests/New/Connection State Machine/AuthenticationStateMachineTests.swift
+++ b/Tests/PostgresNIOTests/New/Connection State Machine/AuthenticationStateMachineTests.swift
@@ -1,82 +1,84 @@
-import XCTest
+import Testing
import NIOCore
@testable import PostgresNIO
-class AuthenticationStateMachineTests: XCTestCase {
-
- func testAuthenticatePlaintext() {
+@Suite struct AuthenticationStateMachineTests {
+
+ @Test func testAuthenticatePlaintext() {
let authContext = AuthContext(username: "test", password: "abc123", database: "test")
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .disable), .provideAuthenticationContext)
-
- XCTAssertEqual(state.provideAuthenticationContext(authContext), .sendStartupMessage(authContext))
- XCTAssertEqual(state.authenticationMessageReceived(.plaintext), .sendPasswordMessage(.cleartext, authContext))
- XCTAssertEqual(state.authenticationMessageReceived(.ok), .wait)
+ #expect(state.connected(tls: .disable) == .provideAuthenticationContext)
+
+ #expect(state.provideAuthenticationContext(authContext) == .sendStartupMessage(authContext))
+ #expect(state.authenticationMessageReceived(.plaintext) == .sendPasswordMessage(.cleartext, authContext))
+ #expect(state.authenticationMessageReceived(.ok) == .wait)
}
- func testAuthenticateMD5() {
+ @Test func testAuthenticateMD5() {
let authContext = AuthContext(username: "test", password: "abc123", database: "test")
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .disable), .provideAuthenticationContext)
+ #expect(state.connected(tls: .disable) == .provideAuthenticationContext)
let salt: UInt32 = 0x00_01_02_03
- XCTAssertEqual(state.provideAuthenticationContext(authContext), .sendStartupMessage(authContext))
- XCTAssertEqual(state.authenticationMessageReceived(.md5(salt: salt)), .sendPasswordMessage(.md5(salt: salt), authContext))
- XCTAssertEqual(state.authenticationMessageReceived(.ok), .wait)
+ #expect(state.provideAuthenticationContext(authContext) == .sendStartupMessage(authContext))
+ #expect(state.authenticationMessageReceived(.md5(salt: salt)) == .sendPasswordMessage(.md5(salt: salt), authContext))
+ #expect(state.authenticationMessageReceived(.ok) == .wait)
}
- func testAuthenticateMD5WithoutPassword() {
+ @Test func testAuthenticateMD5WithoutPassword() {
let authContext = AuthContext(username: "test", password: nil, database: "test")
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .disable), .provideAuthenticationContext)
+ #expect(state.connected(tls: .disable) == .provideAuthenticationContext)
let salt: UInt32 = 0x00_01_02_03
- XCTAssertEqual(state.provideAuthenticationContext(authContext), .sendStartupMessage(authContext))
- XCTAssertEqual(state.authenticationMessageReceived(.md5(salt: salt)),
+ #expect(state.provideAuthenticationContext(authContext) == .sendStartupMessage(authContext))
+ #expect(state.authenticationMessageReceived(.md5(salt: salt)) ==
.closeConnectionAndCleanup(.init(action: .close, tasks: [], error: .authMechanismRequiresPassword, closePromise: nil)))
}
- func testAuthenticateOkAfterStartUpWithoutAuthChallenge() {
+ @Test func testAuthenticateOkAfterStartUpWithoutAuthChallenge() {
let authContext = AuthContext(username: "test", password: "abc123", database: "test")
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .disable), .provideAuthenticationContext)
- XCTAssertEqual(state.provideAuthenticationContext(authContext), .sendStartupMessage(authContext))
- XCTAssertEqual(state.authenticationMessageReceived(.ok), .wait)
+ #expect(state.connected(tls: .disable) == .provideAuthenticationContext)
+ #expect(state.provideAuthenticationContext(authContext) == .sendStartupMessage(authContext))
+ #expect(state.authenticationMessageReceived(.ok) == .wait)
}
- func testAuthenticateSCRAMSHA256WithAtypicalEncoding() {
+ @Test func testAuthenticateSCRAMSHA256WithAtypicalEncoding() {
let authContext = AuthContext(username: "test", password: "abc123", database: "test")
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .disable), .provideAuthenticationContext)
- XCTAssertEqual(state.provideAuthenticationContext(authContext), .sendStartupMessage(authContext))
-
+ #expect(state.connected(tls: .disable) == .provideAuthenticationContext)
+ #expect(state.provideAuthenticationContext(authContext) == .sendStartupMessage(authContext))
+
let saslResponse = state.authenticationMessageReceived(.sasl(names: ["SCRAM-SHA-256"]))
guard case .sendSaslInitialResponse(name: let name, initialResponse: let responseData) = saslResponse else {
- return XCTFail("\(saslResponse) is not .sendSaslInitialResponse")
+ Issue.record("\(saslResponse) is not .sendSaslInitialResponse")
+ return
}
let responseString = String(decoding: responseData, as: UTF8.self)
- XCTAssertEqual(name, "SCRAM-SHA-256")
- XCTAssert(responseString.starts(with: "n,,n=test,r="))
-
+ #expect(name == "SCRAM-SHA-256")
+ #expect(responseString.starts(with: "n,,n=test,r="))
+
let saslContinueResponse = state.authenticationMessageReceived(.saslContinue(data: .init(bytes:
"r=\(responseString.dropFirst(12))RUJSZHhkeUVFNzRLNERKMkxmU05ITU1NZWcxaQ==,s=ijgUVaWgCDLRJyF963BKNA==,i=4096".utf8
)))
guard case .sendSaslResponse(let responseData2) = saslContinueResponse else {
- return XCTFail("\(saslContinueResponse) is not .sendSaslResponse")
+ Issue.record("\(saslContinueResponse) is not .sendSaslResponse")
+ return
}
let response2String = String(decoding: responseData2, as: UTF8.self)
- XCTAssertEqual(response2String.prefix(76), "c=biws,r=\(responseString.dropFirst(12))RUJSZHhkeUVFNzRLNERKMkxmU05ITU1NZWcxaQ==,p=")
+ #expect(response2String.prefix(76) == "c=biws,r=\(responseString.dropFirst(12))RUJSZHhkeUVFNzRLNERKMkxmU05ITU1NZWcxaQ==,p=")
}
- func testAuthenticationFailure() {
+ @Test func testAuthenticationFailure() {
let authContext = AuthContext(username: "test", password: "abc123", database: "test")
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .disable), .provideAuthenticationContext)
+ #expect(state.connected(tls: .disable) == .provideAuthenticationContext)
let salt: UInt32 = 0x00_01_02_03
- XCTAssertEqual(state.provideAuthenticationContext(authContext), .sendStartupMessage(authContext))
- XCTAssertEqual(state.authenticationMessageReceived(.md5(salt: salt)), .sendPasswordMessage(.md5(salt: salt), authContext))
+ #expect(state.provideAuthenticationContext(authContext) == .sendStartupMessage(authContext))
+ #expect(state.authenticationMessageReceived(.md5(salt: salt)) == .sendPasswordMessage(.md5(salt: salt), authContext))
let fields: [PostgresBackendMessage.Field: String] = [
.message: "password authentication failed for user \"postgres\"",
.severity: "FATAL",
@@ -86,13 +88,13 @@ class AuthenticationStateMachineTests: XCTestCase {
.line: "334",
.file: "auth.c"
]
- XCTAssertEqual(state.errorReceived(.init(fields: fields)),
+ #expect(state.errorReceived(.init(fields: fields)) ==
.closeConnectionAndCleanup(.init(action: .close, tasks: [], error: .server(.init(fields: fields)), closePromise: nil)))
}
// MARK: Test unsupported messages
- func testUnsupportedAuthMechanism() {
+ @Test func testUnsupportedAuthMechanism() {
let unsupported: [(PostgresBackendMessage.Authentication, PSQLError.UnsupportedAuthScheme)] = [
(.kerberosV5, .kerberosV5),
(.scmCredential, .scmCredential),
@@ -104,14 +106,14 @@ class AuthenticationStateMachineTests: XCTestCase {
for (message, mechanism) in unsupported {
let authContext = AuthContext(username: "test", password: "abc123", database: "test")
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .disable), .provideAuthenticationContext)
- XCTAssertEqual(state.provideAuthenticationContext(authContext), .sendStartupMessage(authContext))
- XCTAssertEqual(state.authenticationMessageReceived(message),
+ #expect(state.connected(tls: .disable) == .provideAuthenticationContext)
+ #expect(state.provideAuthenticationContext(authContext) == .sendStartupMessage(authContext))
+ #expect(state.authenticationMessageReceived(message) ==
.closeConnectionAndCleanup(.init(action: .close, tasks: [], error: .unsupportedAuthMechanism(mechanism), closePromise: nil)))
}
}
- func testUnexpectedMessagesAfterStartUp() {
+ @Test func testUnexpectedMessagesAfterStartUp() {
var buffer = ByteBuffer()
buffer.writeBytes([0, 1, 2, 3, 4, 5, 6, 7, 8])
let unexpected: [PostgresBackendMessage.Authentication] = [
@@ -123,14 +125,14 @@ class AuthenticationStateMachineTests: XCTestCase {
for message in unexpected {
let authContext = AuthContext(username: "test", password: "abc123", database: "test")
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .disable), .provideAuthenticationContext)
- XCTAssertEqual(state.provideAuthenticationContext(authContext), .sendStartupMessage(authContext))
- XCTAssertEqual(state.authenticationMessageReceived(message),
+ #expect(state.connected(tls: .disable) == .provideAuthenticationContext)
+ #expect(state.provideAuthenticationContext(authContext) == .sendStartupMessage(authContext))
+ #expect(state.authenticationMessageReceived(message) ==
.closeConnectionAndCleanup(.init(action: .close, tasks: [], error: .unexpectedBackendMessage(.authentication(message)), closePromise: nil)))
}
}
- func testUnexpectedMessagesAfterPasswordSent() {
+ @Test func testUnexpectedMessagesAfterPasswordSent() {
let salt: UInt32 = 0x00_01_02_03
var buffer = ByteBuffer()
buffer.writeBytes([0, 1, 2, 3, 4, 5, 6, 7, 8])
@@ -150,10 +152,10 @@ class AuthenticationStateMachineTests: XCTestCase {
for message in unexpected {
let authContext = AuthContext(username: "test", password: "abc123", database: "test")
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .disable), .provideAuthenticationContext)
- XCTAssertEqual(state.provideAuthenticationContext(authContext), .sendStartupMessage(authContext))
- XCTAssertEqual(state.authenticationMessageReceived(.md5(salt: salt)), .sendPasswordMessage(.md5(salt: salt), authContext))
- XCTAssertEqual(state.authenticationMessageReceived(message),
+ #expect(state.connected(tls: .disable) == .provideAuthenticationContext)
+ #expect(state.provideAuthenticationContext(authContext) == .sendStartupMessage(authContext))
+ #expect(state.authenticationMessageReceived(.md5(salt: salt)) == .sendPasswordMessage(.md5(salt: salt), authContext))
+ #expect(state.authenticationMessageReceived(message) ==
.closeConnectionAndCleanup(.init(action: .close, tasks: [], error: .unexpectedBackendMessage(.authentication(message)), closePromise: nil)))
}
}
diff --git a/Tests/PostgresNIOTests/New/Connection State Machine/ConnectionStateMachineTests.swift b/Tests/PostgresNIOTests/New/Connection State Machine/ConnectionStateMachineTests.swift
index f3d72a5e..445feb25 100644
--- a/Tests/PostgresNIOTests/New/Connection State Machine/ConnectionStateMachineTests.swift
+++ b/Tests/PostgresNIOTests/New/Connection State Machine/ConnectionStateMachineTests.swift
@@ -1,162 +1,159 @@
-import XCTest
+import Testing
@testable import PostgresNIO
@testable import NIOCore
import NIOPosix
import NIOSSL
-class ConnectionStateMachineTests: XCTestCase {
-
- func testStartup() {
+@Suite struct ConnectionStateMachineTests {
+
+ @Test func testStartup() {
let authContext = AuthContext(username: "test", password: "abc123", database: "test")
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .disable), .provideAuthenticationContext)
- XCTAssertEqual(state.provideAuthenticationContext(authContext), .sendStartupMessage(authContext))
- XCTAssertEqual(state.authenticationMessageReceived(.plaintext), .sendPasswordMessage(.cleartext, authContext))
- XCTAssertEqual(state.authenticationMessageReceived(.ok), .wait)
+ #expect(state.connected(tls: .disable) == .provideAuthenticationContext)
+ #expect(state.provideAuthenticationContext(authContext) == .sendStartupMessage(authContext))
+ #expect(state.authenticationMessageReceived(.plaintext) == .sendPasswordMessage(.cleartext, authContext))
+ #expect(state.authenticationMessageReceived(.ok) == .wait)
}
- func testSSLStartupSuccess() {
+ @Test func testSSLStartupSuccess() {
let authContext = AuthContext(username: "test", password: "abc123", database: "test")
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .require), .sendSSLRequest)
- XCTAssertEqual(state.sslSupportedReceived(unprocessedBytes: 0), .establishSSLConnection)
- XCTAssertEqual(state.sslHandlerAdded(), .wait)
- XCTAssertEqual(state.sslEstablished(), .provideAuthenticationContext)
- XCTAssertEqual(state.provideAuthenticationContext(authContext), .sendStartupMessage(authContext))
+ #expect(state.connected(tls: .require) == .sendSSLRequest)
+ #expect(state.sslSupportedReceived(unprocessedBytes: 0) == .establishSSLConnection)
+ #expect(state.sslHandlerAdded() == .wait)
+ #expect(state.sslEstablished() == .provideAuthenticationContext)
+ #expect(state.provideAuthenticationContext(authContext) == .sendStartupMessage(authContext))
let salt: UInt32 = 0x00_01_02_03
- XCTAssertEqual(state.authenticationMessageReceived(.md5(salt: salt)), .sendPasswordMessage(.md5(salt: salt), authContext))
+ #expect(state.authenticationMessageReceived(.md5(salt: salt)) == .sendPasswordMessage(.md5(salt: salt), authContext))
}
- func testSSLStartupFailureTooManyBytesRemaining() {
+ @Test func testSSLStartupFailureTooManyBytesRemaining() {
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .require), .sendSSLRequest)
+ #expect(state.connected(tls: .require) == .sendSSLRequest)
let failError = PSQLError.receivedUnencryptedDataAfterSSLRequest
- XCTAssertEqual(state.sslSupportedReceived(unprocessedBytes: 1), .closeConnectionAndCleanup(.init(action: .close, tasks: [], error: failError, closePromise: nil)))
+ #expect(state.sslSupportedReceived(unprocessedBytes: 1) == .closeConnectionAndCleanup(.init(action: .close, tasks: [], error: failError, closePromise: nil)))
}
- func testSSLStartupFailHandler() {
+ @Test func testSSLStartupFailHandler() {
struct SSLHandlerAddError: Error, Equatable {}
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .require), .sendSSLRequest)
- XCTAssertEqual(state.sslSupportedReceived(unprocessedBytes: 0), .establishSSLConnection)
+ #expect(state.connected(tls: .require) == .sendSSLRequest)
+ #expect(state.sslSupportedReceived(unprocessedBytes: 0) == .establishSSLConnection)
let failError = PSQLError.failedToAddSSLHandler(underlying: SSLHandlerAddError())
- XCTAssertEqual(state.errorHappened(failError), .closeConnectionAndCleanup(.init(action: .close, tasks: [], error: failError, closePromise: nil)))
+ #expect(state.errorHappened(failError) == .closeConnectionAndCleanup(.init(action: .close, tasks: [], error: failError, closePromise: nil)))
}
- func testTLSRequiredStartupSSLUnsupported() {
+ @Test func testTLSRequiredStartupSSLUnsupported() {
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .require), .sendSSLRequest)
- XCTAssertEqual(state.sslUnsupportedReceived(),
+ #expect(state.connected(tls: .require) == .sendSSLRequest)
+ #expect(state.sslUnsupportedReceived() ==
.closeConnectionAndCleanup(.init(action: .close, tasks: [], error: PSQLError.sslUnsupported, closePromise: nil)))
}
- func testTLSPreferredStartupSSLUnsupported() {
+ @Test func testTLSPreferredStartupSSLUnsupported() {
var state = ConnectionStateMachine(requireBackendKeyData: true)
- XCTAssertEqual(state.connected(tls: .prefer), .sendSSLRequest)
- XCTAssertEqual(state.sslUnsupportedReceived(), .provideAuthenticationContext)
+ #expect(state.connected(tls: .prefer) == .sendSSLRequest)
+ #expect(state.sslUnsupportedReceived() == .provideAuthenticationContext)
}
- func testParameterStatusReceivedAndBackendKeyAfterAuthenticated() {
+ @Test func testParameterStatusReceivedAndBackendKeyAfterAuthenticated() {
var state = ConnectionStateMachine(.authenticated(nil, [:]))
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "DateStyle", value: "ISO, MDY")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "application_name", value: "")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "server_encoding", value: "UTF8")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "integer_datetimes", value: "on")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "client_encoding", value: "UTF8")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "TimeZone", value: "Etc/UTC")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "is_superuser", value: "on")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "server_version", value: "13.1 (Debian 13.1-1.pgdg100+1)")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "session_authorization", value: "postgres")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "IntervalStyle", value: "postgres")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "standard_conforming_strings", value: "on")), .wait)
-
- XCTAssertEqual(state.backendKeyDataReceived(.init(processID: 2730, secretKey: 882037977)), .wait)
- XCTAssertEqual(state.readyForQueryReceived(.idle), .fireEventReadyForQuery)
+ #expect(state.parameterStatusReceived(.init(parameter: "DateStyle", value: "ISO, MDY")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "application_name", value: "")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "server_encoding", value: "UTF8")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "integer_datetimes", value: "on")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "client_encoding", value: "UTF8")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "TimeZone", value: "Etc/UTC")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "is_superuser", value: "on")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "server_version", value: "13.1 (Debian 13.1-1.pgdg100+1)")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "session_authorization", value: "postgres")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "IntervalStyle", value: "postgres")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "standard_conforming_strings", value: "on")) == .wait)
+
+ #expect(state.backendKeyDataReceived(.init(processID: 2730, secretKey: 882037977)) == .wait)
+ #expect(state.readyForQueryReceived(.idle) == .fireEventReadyForQuery)
}
- func testBackendKeyAndParameterStatusReceivedAfterAuthenticated() {
+ @Test func testBackendKeyAndParameterStatusReceivedAfterAuthenticated() {
var state = ConnectionStateMachine(.authenticated(nil, [:]))
- XCTAssertEqual(state.backendKeyDataReceived(.init(processID: 2730, secretKey: 882037977)), .wait)
-
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "DateStyle", value: "ISO, MDY")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "application_name", value: "")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "server_encoding", value: "UTF8")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "integer_datetimes", value: "on")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "client_encoding", value: "UTF8")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "TimeZone", value: "Etc/UTC")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "is_superuser", value: "on")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "server_version", value: "13.1 (Debian 13.1-1.pgdg100+1)")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "session_authorization", value: "postgres")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "IntervalStyle", value: "postgres")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "standard_conforming_strings", value: "on")), .wait)
-
- XCTAssertEqual(state.readyForQueryReceived(.idle), .fireEventReadyForQuery)
+ #expect(state.backendKeyDataReceived(.init(processID: 2730, secretKey: 882037977)) == .wait)
+
+ #expect(state.parameterStatusReceived(.init(parameter: "DateStyle", value: "ISO, MDY")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "application_name", value: "")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "server_encoding", value: "UTF8")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "integer_datetimes", value: "on")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "client_encoding", value: "UTF8")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "TimeZone", value: "Etc/UTC")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "is_superuser", value: "on")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "server_version", value: "13.1 (Debian 13.1-1.pgdg100+1)")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "session_authorization", value: "postgres")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "IntervalStyle", value: "postgres")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "standard_conforming_strings", value: "on")) == .wait)
+
+ #expect(state.readyForQueryReceived(.idle) == .fireEventReadyForQuery)
}
- func testReadyForQueryReceivedWithoutBackendKeyAfterAuthenticated() {
+ @Test func testReadyForQueryReceivedWithoutBackendKeyAfterAuthenticated() {
var state = ConnectionStateMachine(.authenticated(nil, [:]), requireBackendKeyData: true)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "DateStyle", value: "ISO, MDY")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "application_name", value: "")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "server_encoding", value: "UTF8")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "integer_datetimes", value: "on")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "client_encoding", value: "UTF8")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "TimeZone", value: "Etc/UTC")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "is_superuser", value: "on")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "server_version", value: "13.1 (Debian 13.1-1.pgdg100+1)")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "session_authorization", value: "postgres")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "IntervalStyle", value: "postgres")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "standard_conforming_strings", value: "on")), .wait)
-
- XCTAssertEqual(state.readyForQueryReceived(.idle),
+ #expect(state.parameterStatusReceived(.init(parameter: "DateStyle", value: "ISO, MDY")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "application_name", value: "")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "server_encoding", value: "UTF8")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "integer_datetimes", value: "on")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "client_encoding", value: "UTF8")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "TimeZone", value: "Etc/UTC")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "is_superuser", value: "on")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "server_version", value: "13.1 (Debian 13.1-1.pgdg100+1)")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "session_authorization", value: "postgres")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "IntervalStyle", value: "postgres")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "standard_conforming_strings", value: "on")) == .wait)
+
+ #expect(state.readyForQueryReceived(.idle) ==
.closeConnectionAndCleanup(.init(action: .close, tasks: [], error: PSQLError.unexpectedBackendMessage(.readyForQuery(.idle)), closePromise: nil)))
}
- func testReadyForQueryReceivedWithoutUnneededBackendKeyAfterAuthenticated() {
+ @Test func testReadyForQueryReceivedWithoutUnneededBackendKeyAfterAuthenticated() {
var state = ConnectionStateMachine(.authenticated(nil, [:]), requireBackendKeyData: false)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "DateStyle", value: "ISO, MDY")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "application_name", value: "")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "server_encoding", value: "UTF8")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "integer_datetimes", value: "on")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "client_encoding", value: "UTF8")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "TimeZone", value: "Etc/UTC")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "is_superuser", value: "on")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "server_version", value: "13.1 (Debian 13.1-1.pgdg100+1)")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "session_authorization", value: "postgres")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "IntervalStyle", value: "postgres")), .wait)
- XCTAssertEqual(state.parameterStatusReceived(.init(parameter: "standard_conforming_strings", value: "on")), .wait)
-
- XCTAssertEqual(state.readyForQueryReceived(.idle), .fireEventReadyForQuery)
+ #expect(state.parameterStatusReceived(.init(parameter: "DateStyle", value: "ISO, MDY")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "application_name", value: "")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "server_encoding", value: "UTF8")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "integer_datetimes", value: "on")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "client_encoding", value: "UTF8")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "TimeZone", value: "Etc/UTC")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "is_superuser", value: "on")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "server_version", value: "13.1 (Debian 13.1-1.pgdg100+1)")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "session_authorization", value: "postgres")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "IntervalStyle", value: "postgres")) == .wait)
+ #expect(state.parameterStatusReceived(.init(parameter: "standard_conforming_strings", value: "on")) == .wait)
+
+ #expect(state.readyForQueryReceived(.idle) == .fireEventReadyForQuery)
}
- func testErrorIsIgnoredWhenClosingConnection() {
+ @Test func testErrorIsIgnoredWhenClosingConnection() {
// test ignore unclean shutdown when closing connection
var stateIgnoreChannelError = ConnectionStateMachine(.closing(nil))
- XCTAssertEqual(stateIgnoreChannelError.errorHappened(.connectionError(underlying: NIOSSLError.uncleanShutdown)), .wait)
- XCTAssertEqual(stateIgnoreChannelError.closed(), .fireChannelInactive)
-
+ #expect(stateIgnoreChannelError.errorHappened(.connectionError(underlying: NIOSSLError.uncleanShutdown)) == .wait)
+ #expect(stateIgnoreChannelError.closed() == .fireChannelInactive)
+
// test ignore any other error when closing connection
var stateIgnoreErrorMessage = ConnectionStateMachine(.closing(nil))
- XCTAssertEqual(stateIgnoreErrorMessage.errorReceived(.init(fields: [:])), .wait)
- XCTAssertEqual(stateIgnoreErrorMessage.closed(), .fireChannelInactive)
+ #expect(stateIgnoreErrorMessage.errorReceived(.init(fields: [:])) == .wait)
+ #expect(stateIgnoreErrorMessage.closed() == .fireChannelInactive)
}
- func testFailQueuedQueriesOnAuthenticationFailure() throws {
- let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
- defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) }
-
+ @Test func testFailQueuedQueriesOnAuthenticationFailure() throws {
let authContext = AuthContext(username: "test", password: "abc123", database: "test")
let salt: UInt32 = 0x00_01_02_03
- let queryPromise = eventLoopGroup.next().makePromise(of: PSQLRowStream.self)
+ let queryPromise = NIOSingletons.posixEventLoopGroup.next().makePromise(of: PSQLRowStream.self)
var state = ConnectionStateMachine(requireBackendKeyData: true)
let extendedQueryContext = ExtendedQueryContext(
@@ -164,10 +161,10 @@ class ConnectionStateMachineTests: XCTestCase {
logger: .psqlTest,
promise: queryPromise)
- XCTAssertEqual(state.enqueue(task: .extendedQuery(extendedQueryContext)), .wait)
- XCTAssertEqual(state.connected(tls: .disable), .provideAuthenticationContext)
- XCTAssertEqual(state.provideAuthenticationContext(authContext), .sendStartupMessage(authContext))
- XCTAssertEqual(state.authenticationMessageReceived(.md5(salt: salt)), .sendPasswordMessage(.md5(salt: salt), authContext))
+ #expect(state.enqueue(task: .extendedQuery(extendedQueryContext)) == .wait)
+ #expect(state.connected(tls: .disable) == .provideAuthenticationContext)
+ #expect(state.provideAuthenticationContext(authContext) == .sendStartupMessage(authContext))
+ #expect(state.authenticationMessageReceived(.md5(salt: salt)) == .sendPasswordMessage(.md5(salt: salt), authContext))
let fields: [PostgresBackendMessage.Field: String] = [
.message: "password authentication failed for user \"postgres\"",
.severity: "FATAL",
@@ -177,10 +174,10 @@ class ConnectionStateMachineTests: XCTestCase {
.line: "334",
.file: "auth.c"
]
- XCTAssertEqual(state.errorReceived(.init(fields: fields)),
+ #expect(state.errorReceived(.init(fields: fields)) ==
.closeConnectionAndCleanup(.init(action: .close, tasks: [.extendedQuery(extendedQueryContext)], error: .server(.init(fields: fields)), closePromise: nil)))
- XCTAssertNil(queryPromise.futureResult._value)
+ #expect(queryPromise.futureResult._value == nil)
// make sure we don't crash
queryPromise.fail(PSQLError.server(.init(fields: fields)))
diff --git a/Tests/PostgresNIOTests/New/Connection State Machine/ExtendedQueryStateMachineTests.swift b/Tests/PostgresNIOTests/New/Connection State Machine/ExtendedQueryStateMachineTests.swift
index ae484acc..872664af 100644
--- a/Tests/PostgresNIOTests/New/Connection State Machine/ExtendedQueryStateMachineTests.swift
+++ b/Tests/PostgresNIOTests/New/Connection State Machine/ExtendedQueryStateMachineTests.swift
@@ -114,7 +114,7 @@ class ExtendedQueryStateMachineTests: XCTestCase {
.failQuery(promise, with: psqlError, cleanupContext: .init(action: .close, tasks: [], error: psqlError, closePromise: nil)))
}
- func testExtendedQueryIsCancelledImmediatly() {
+ func testExtendedQueryIsCancelledImmediately() {
var state = ConnectionStateMachine.readyForQuery()
let logger = Logger.psqlTest
diff --git a/Tests/PostgresNIOTests/New/Data/Array+PSQLCodableTests.swift b/Tests/PostgresNIOTests/New/Data/Array+PSQLCodableTests.swift
index bfffef52..1602cee0 100644
--- a/Tests/PostgresNIOTests/New/Data/Array+PSQLCodableTests.swift
+++ b/Tests/PostgresNIOTests/New/Data/Array+PSQLCodableTests.swift
@@ -1,137 +1,141 @@
-import XCTest
+import Foundation
+import Testing
import NIOCore
@testable import PostgresNIO
-class Array_PSQLCodableTests: XCTestCase {
+@Suite struct Array_PSQLCodableTests {
- func testArrayTypes() {
+ @Test func testArrayTypes() {
+ #expect(Bool.psqlArrayType == .boolArray)
+ #expect(Bool.psqlType == .bool)
+ #expect([Bool].psqlType == .boolArray)
- XCTAssertEqual(Bool.psqlArrayType, .boolArray)
- XCTAssertEqual(Bool.psqlType, .bool)
- XCTAssertEqual([Bool].psqlType, .boolArray)
+ #expect(ByteBuffer.psqlArrayType == .byteaArray)
+ #expect(ByteBuffer.psqlType == .bytea)
+ #expect([ByteBuffer].psqlType == .byteaArray)
- XCTAssertEqual(ByteBuffer.psqlArrayType, .byteaArray)
- XCTAssertEqual(ByteBuffer.psqlType, .bytea)
- XCTAssertEqual([ByteBuffer].psqlType, .byteaArray)
+ #expect(UInt8.psqlArrayType == .charArray)
+ #expect(UInt8.psqlType == .char)
+ #expect([UInt8].psqlType == .charArray)
- XCTAssertEqual(UInt8.psqlArrayType, .charArray)
- XCTAssertEqual(UInt8.psqlType, .char)
- XCTAssertEqual([UInt8].psqlType, .charArray)
+ #expect(Int16.psqlArrayType == .int2Array)
+ #expect(Int16.psqlType == .int2)
+ #expect([Int16].psqlType == .int2Array)
- XCTAssertEqual(Int16.psqlArrayType, .int2Array)
- XCTAssertEqual(Int16.psqlType, .int2)
- XCTAssertEqual([Int16].psqlType, .int2Array)
+ #expect(Int32.psqlArrayType == .int4Array)
+ #expect(Int32.psqlType == .int4)
+ #expect([Int32].psqlType == .int4Array)
- XCTAssertEqual(Int32.psqlArrayType, .int4Array)
- XCTAssertEqual(Int32.psqlType, .int4)
- XCTAssertEqual([Int32].psqlType, .int4Array)
-
- XCTAssertEqual(Int64.psqlArrayType, .int8Array)
- XCTAssertEqual(Int64.psqlType, .int8)
- XCTAssertEqual([Int64].psqlType, .int8Array)
+ #expect(Int64.psqlArrayType == .int8Array)
+ #expect(Int64.psqlType == .int8)
+ #expect([Int64].psqlType == .int8Array)
#if (arch(i386) || arch(arm))
- XCTAssertEqual(Int.psqlArrayType, .int4Array)
- XCTAssertEqual(Int.psqlType, .int4)
- XCTAssertEqual([Int].psqlType, .int4Array)
+ #expect(Int.psqlArrayType == .int4Array)
+ #expect(Int.psqlType == .int4)
+ #expect([Int].psqlType == .int4Array)
#else
- XCTAssertEqual(Int.psqlArrayType, .int8Array)
- XCTAssertEqual(Int.psqlType, .int8)
- XCTAssertEqual([Int].psqlType, .int8Array)
+ #expect(Int.psqlArrayType == .int8Array)
+ #expect(Int.psqlType == .int8)
+ #expect([Int].psqlType == .int8Array)
#endif
- XCTAssertEqual(Float.psqlArrayType, .float4Array)
- XCTAssertEqual(Float.psqlType, .float4)
- XCTAssertEqual([Float].psqlType, .float4Array)
+ #expect(Float.psqlArrayType == .float4Array)
+ #expect(Float.psqlType == .float4)
+ #expect([Float].psqlType == .float4Array)
- XCTAssertEqual(Double.psqlArrayType, .float8Array)
- XCTAssertEqual(Double.psqlType, .float8)
- XCTAssertEqual([Double].psqlType, .float8Array)
+ #expect(Double.psqlArrayType == .float8Array)
+ #expect(Double.psqlType == .float8)
+ #expect([Double].psqlType == .float8Array)
- XCTAssertEqual(String.psqlArrayType, .textArray)
- XCTAssertEqual(String.psqlType, .text)
- XCTAssertEqual([String].psqlType, .textArray)
+ #expect(String.psqlArrayType == .textArray)
+ #expect(String.psqlType == .text)
+ #expect([String].psqlType == .textArray)
- XCTAssertEqual(UUID.psqlArrayType, .uuidArray)
- XCTAssertEqual(UUID.psqlType, .uuid)
- XCTAssertEqual([UUID].psqlType, .uuidArray)
+ #expect(UUID.psqlArrayType == .uuidArray)
+ #expect(UUID.psqlType == .uuid)
+ #expect([UUID].psqlType == .uuidArray)
- XCTAssertEqual(Date.psqlArrayType, .timestamptzArray)
- XCTAssertEqual(Date.psqlType, .timestamptz)
- XCTAssertEqual([Date].psqlType, .timestamptzArray)
+ #expect(Date.psqlArrayType == .timestamptzArray)
+ #expect(Date.psqlType == .timestamptz)
+ #expect([Date].psqlType == .timestamptzArray)
- XCTAssertEqual(Range.psqlArrayType, .int4RangeArray)
- XCTAssertEqual(Range.psqlType, .int4Range)
- XCTAssertEqual([Range].psqlType, .int4RangeArray)
+ #expect(Range.psqlArrayType == .int4RangeArray)
+ #expect(Range.psqlType == .int4Range)
+ #expect([Range].psqlType == .int4RangeArray)
- XCTAssertEqual(ClosedRange.psqlArrayType, .int4RangeArray)
- XCTAssertEqual(ClosedRange.psqlType, .int4Range)
- XCTAssertEqual([ClosedRange].psqlType, .int4RangeArray)
+ #expect(ClosedRange.psqlArrayType == .int4RangeArray)
+ #expect(ClosedRange.psqlType == .int4Range)
+ #expect([ClosedRange].psqlType == .int4RangeArray)
- XCTAssertEqual(Range.psqlArrayType, .int8RangeArray)
- XCTAssertEqual(Range.psqlType, .int8Range)
- XCTAssertEqual([Range].psqlType, .int8RangeArray)
+ #expect(Range.psqlArrayType == .int8RangeArray)
+ #expect(Range.psqlType == .int8Range)
+ #expect([Range].psqlType == .int8RangeArray)
- XCTAssertEqual(ClosedRange.psqlArrayType, .int8RangeArray)
- XCTAssertEqual(ClosedRange.psqlType, .int8Range)
- XCTAssertEqual([ClosedRange].psqlType, .int8RangeArray)
+ #expect(ClosedRange.psqlArrayType == .int8RangeArray)
+ #expect(ClosedRange.psqlType == .int8Range)
+ #expect([ClosedRange].psqlType == .int8RangeArray)
}
- func testStringArrayRoundTrip() {
+ @Test func testStringArrayRoundTrip() {
let values = ["foo", "bar", "hello", "world"]
var buffer = ByteBuffer()
values.encode(into: &buffer, context: .default)
var result: [String]?
- XCTAssertNoThrow(result = try [String](from: &buffer, type: .textArray, format: .binary, context: .default))
- XCTAssertEqual(values, result)
+ #expect(throws: Never.self) {
+ result = try [String](from: &buffer, type: .textArray, format: .binary, context: .default)
+ }
+ #expect(values == result)
}
- func testEmptyStringArrayRoundTrip() {
+ @Test func testEmptyStringArrayRoundTrip() {
let values: [String] = []
var buffer = ByteBuffer()
values.encode(into: &buffer, context: .default)
var result: [String]?
- XCTAssertNoThrow(result = try [String](from: &buffer, type: .textArray, format: .binary, context: .default))
- XCTAssertEqual(values, result)
+ #expect(throws: Never.self) {
+ result = try [String](from: &buffer, type: .textArray, format: .binary, context: .default)
+ }
+ #expect(values == result)
}
- func testDecodeFailureIsNotEmptyOutOfScope() {
+ @Test func testDecodeFailureIsNotEmptyOutOfScope() {
var buffer = ByteBuffer()
buffer.writeInteger(Int32(2)) // invalid value
buffer.writeInteger(Int32(0))
buffer.writeInteger(String.psqlType.rawValue)
- XCTAssertThrowsError(try [String](from: &buffer, type: .textArray, format: .binary, context: .default)) {
- XCTAssertEqual($0 as? PostgresDecodingError.Code, .failure)
+ #expect(throws: PostgresDecodingError.Code.failure) {
+ try [String](from: &buffer, type: .textArray, format: .binary, context: .default)
}
}
- func testDecodeFailureSecondValueIsUnexpected() {
+ @Test func testDecodeFailureSecondValueIsUnexpected() {
var buffer = ByteBuffer()
buffer.writeInteger(Int32(0)) // is empty
buffer.writeInteger(Int32(1)) // invalid value, must always be 0
buffer.writeInteger(String.psqlType.rawValue)
- XCTAssertThrowsError(try [String](from: &buffer, type: .textArray, format: .binary, context: .default)) {
- XCTAssertEqual($0 as? PostgresDecodingError.Code, .failure)
+ #expect(throws: PostgresDecodingError.Code.failure) {
+ try [String](from: &buffer, type: .textArray, format: .binary, context: .default)
}
}
- func testDecodeFailureTriesDecodeInt8() {
+ @Test func testDecodeFailureTriesDecodeInt8() {
let value: Int64 = 1 << 32
var buffer = ByteBuffer()
value.encode(into: &buffer, context: .default)
- XCTAssertThrowsError(try [String](from: &buffer, type: .textArray, format: .binary, context: .default)) {
- XCTAssertEqual($0 as? PostgresDecodingError.Code, .failure)
+ #expect(throws: PostgresDecodingError.Code.failure) {
+ try [String](from: &buffer, type: .textArray, format: .binary, context: .default)
}
}
- func testDecodeFailureInvalidNumberOfArrayElements() {
+ @Test func testDecodeFailureInvalidNumberOfArrayElements() {
var buffer = ByteBuffer()
buffer.writeInteger(Int32(1)) // invalid value
buffer.writeInteger(Int32(0))
@@ -139,12 +143,12 @@ class Array_PSQLCodableTests: XCTestCase {
buffer.writeInteger(Int32(-123)) // expected element count
buffer.writeInteger(Int32(1)) // dimensions... must be one
- XCTAssertThrowsError(try [String](from: &buffer, type: .textArray, format: .binary, context: .default)) {
- XCTAssertEqual($0 as? PostgresDecodingError.Code, .failure)
+ #expect(throws: PostgresDecodingError.Code.failure) {
+ try [String](from: &buffer, type: .textArray, format: .binary, context: .default)
}
}
- func testDecodeFailureInvalidNumberOfDimensions() {
+ @Test func testDecodeFailureInvalidNumberOfDimensions() {
var buffer = ByteBuffer()
buffer.writeInteger(Int32(1)) // invalid value
buffer.writeInteger(Int32(0))
@@ -152,12 +156,12 @@ class Array_PSQLCodableTests: XCTestCase {
buffer.writeInteger(Int32(1)) // expected element count
buffer.writeInteger(Int32(2)) // dimensions... must be one
- XCTAssertThrowsError(try [String](from: &buffer, type: .textArray, format: .binary, context: .default)) {
- XCTAssertEqual($0 as? PostgresDecodingError.Code, .failure)
+ #expect(throws: PostgresDecodingError.Code.failure) {
+ try [String](from: &buffer, type: .textArray, format: .binary, context: .default)
}
}
- func testDecodeUnexpectedEnd() {
+ @Test func testDecodeUnexpectedEnd() {
var unexpectedEndInElementLengthBuffer = ByteBuffer()
unexpectedEndInElementLengthBuffer.writeInteger(Int32(1)) // invalid value
unexpectedEndInElementLengthBuffer.writeInteger(Int32(0))
@@ -166,8 +170,8 @@ class Array_PSQLCodableTests: XCTestCase {
unexpectedEndInElementLengthBuffer.writeInteger(Int32(1)) // dimensions
unexpectedEndInElementLengthBuffer.writeInteger(Int16(1)) // length of element, must be Int32
- XCTAssertThrowsError(try [String](from: &unexpectedEndInElementLengthBuffer, type: .textArray, format: .binary, context: .default)) {
- XCTAssertEqual($0 as? PostgresDecodingError.Code, .failure)
+ #expect(throws: PostgresDecodingError.Code.failure) {
+ try [String](from: &unexpectedEndInElementLengthBuffer, type: .textArray, format: .binary, context: .default)
}
var unexpectedEndInElementBuffer = ByteBuffer()
@@ -179,8 +183,8 @@ class Array_PSQLCodableTests: XCTestCase {
unexpectedEndInElementBuffer.writeInteger(Int32(12)) // length of element, must be Int32
unexpectedEndInElementBuffer.writeString("Hello World") // only 11 bytes, 12 needed!
- XCTAssertThrowsError(try [String](from: &unexpectedEndInElementBuffer, type: .textArray, format: .binary, context: .default)) {
- XCTAssertEqual($0 as? PostgresDecodingError.Code, .failure)
+ #expect(throws: PostgresDecodingError.Code.failure) {
+ try [String](from: &unexpectedEndInElementBuffer, type: .textArray, format: .binary, context: .default)
}
}
}
diff --git a/Tests/PostgresNIOTests/New/Data/Bool+PSQLCodableTests.swift b/Tests/PostgresNIOTests/New/Data/Bool+PSQLCodableTests.swift
index e6e43f0b..d23eff08 100644
--- a/Tests/PostgresNIOTests/New/Data/Bool+PSQLCodableTests.swift
+++ b/Tests/PostgresNIOTests/New/Data/Bool+PSQLCodableTests.swift
@@ -1,89 +1,97 @@
-import XCTest
+import Testing
import NIOCore
@testable import PostgresNIO
-class Bool_PSQLCodableTests: XCTestCase {
+@Suite struct Bool_PSQLCodableTests {
// MARK: - Binary
- func testBinaryTrueRoundTrip() {
+ @Test func testBinaryTrueRoundTrip() {
let value = true
var buffer = ByteBuffer()
value.encode(into: &buffer, context: .default)
- XCTAssertEqual(Bool.psqlType, .bool)
- XCTAssertEqual(Bool.psqlFormat, .binary)
- XCTAssertEqual(buffer.readableBytes, 1)
- XCTAssertEqual(buffer.getInteger(at: buffer.readerIndex, as: UInt8.self), 1)
+ #expect(Bool.psqlType == .bool)
+ #expect(Bool.psqlFormat == .binary)
+ #expect(buffer.readableBytes == 1)
+ #expect(buffer.getInteger(at: buffer.readerIndex, as: UInt8.self) == 1)
var result: Bool?
- XCTAssertNoThrow(result = try Bool(from: &buffer, type: .bool, format: .binary, context: .default))
- XCTAssertEqual(value, result)
+ #expect(throws: Never.self) {
+ result = try Bool(from: &buffer, type: .bool, format: .binary, context: .default)
+ }
+ #expect(value == result)
}
- func testBinaryFalseRoundTrip() {
+ @Test func testBinaryFalseRoundTrip() {
let value = false
var buffer = ByteBuffer()
value.encode(into: &buffer, context: .default)
- XCTAssertEqual(Bool.psqlType, .bool)
- XCTAssertEqual(Bool.psqlFormat, .binary)
- XCTAssertEqual(buffer.readableBytes, 1)
- XCTAssertEqual(buffer.getInteger(at: buffer.readerIndex, as: UInt8.self), 0)
+ #expect(Bool.psqlType == .bool)
+ #expect(Bool.psqlFormat == .binary)
+ #expect(buffer.readableBytes == 1)
+ #expect(buffer.getInteger(at: buffer.readerIndex, as: UInt8.self) == 0)
var result: Bool?
- XCTAssertNoThrow(result = try Bool(from: &buffer, type: .bool, format: .binary, context: .default))
- XCTAssertEqual(value, result)
+ #expect(throws: Never.self) {
+ result = try Bool(from: &buffer, type: .bool, format: .binary, context: .default)
+ }
+ #expect(value == result)
}
- func testBinaryDecodeBoolInvalidLength() {
+ @Test func testBinaryDecodeBoolInvalidLength() {
var buffer = ByteBuffer()
buffer.writeInteger(Int64(1))
- XCTAssertThrowsError(try Bool(from: &buffer, type: .bool, format: .binary, context: .default)) {
- XCTAssertEqual($0 as? PostgresDecodingError.Code, .failure)
+ #expect(throws: PostgresDecodingError.Code.failure) {
+ try Bool(from: &buffer, type: .bool, format: .binary, context: .default)
}
}
- func testBinaryDecodeBoolInvalidValue() {
+ @Test func testBinaryDecodeBoolInvalidValue() {
var buffer = ByteBuffer()
buffer.writeInteger(UInt8(13))
- XCTAssertThrowsError(try Bool(from: &buffer, type: .bool, format: .binary, context: .default)) {
- XCTAssertEqual($0 as? PostgresDecodingError.Code, .failure)
+ #expect(throws: PostgresDecodingError.Code.failure) {
+ try Bool(from: &buffer, type: .bool, format: .binary, context: .default)
}
}
// MARK: - Text
- func testTextTrueDecode() {
+ @Test func testTextTrueDecode() {
let value = true
var buffer = ByteBuffer()
buffer.writeInteger(UInt8(ascii: "t"))
var result: Bool?
- XCTAssertNoThrow(result = try Bool(from: &buffer, type: .bool, format: .text, context: .default))
- XCTAssertEqual(value, result)
+ #expect(throws: Never.self) {
+ result = try Bool(from: &buffer, type: .bool, format: .text, context: .default)
+ }
+ #expect(value == result)
}
- func testTextFalseDecode() {
+ @Test func testTextFalseDecode() {
let value = false
var buffer = ByteBuffer()
buffer.writeInteger(UInt8(ascii: "f"))
var result: Bool?
- XCTAssertNoThrow(result = try Bool(from: &buffer, type: .bool, format: .text, context: .default))
- XCTAssertEqual(value, result)
+ #expect(throws: Never.self) {
+ result = try Bool(from: &buffer, type: .bool, format: .text, context: .default)
+ }
+ #expect(value == result)
}
- func testTextDecodeBoolInvalidValue() {
+ @Test func testTextDecodeBoolInvalidValue() {
var buffer = ByteBuffer()
buffer.writeInteger(UInt8(13))
- XCTAssertThrowsError(try Bool(from: &buffer, type: .bool, format: .text, context: .default)) {
- XCTAssertEqual($0 as? PostgresDecodingError.Code, .failure)
+ #expect(throws: PostgresDecodingError.Code.failure) {
+ try Bool(from: &buffer, type: .bool, format: .text, context: .default)
}
}
}
diff --git a/Tests/PostgresNIOTests/New/Data/Bytes+PSQLCodableTests.swift b/Tests/PostgresNIOTests/New/Data/Bytes+PSQLCodableTests.swift
index 9230aee7..77051775 100644
--- a/Tests/PostgresNIOTests/New/Data/Bytes+PSQLCodableTests.swift
+++ b/Tests/PostgresNIOTests/New/Data/Bytes+PSQLCodableTests.swift
@@ -1,34 +1,35 @@
-import XCTest
+import struct Foundation.Data
+import Testing
import NIOCore
@testable import PostgresNIO
-class Bytes_PSQLCodableTests: XCTestCase {
-
- func testDataRoundTrip() {
+@Suite struct Bytes_PSQLCodableTests {
+
+ @Test func testDataRoundTrip() {
let data = Data((0...UInt8.max))
var buffer = ByteBuffer()
data.encode(into: &buffer, context: .default)
- XCTAssertEqual(ByteBuffer.psqlType, .bytea)
-
+ #expect(ByteBuffer.psqlType == .bytea)
+
var result: Data?
result = Data(from: &buffer, type: .bytea, format: .binary, context: .default)
- XCTAssertEqual(data, result)
+ #expect(data == result)
}
- func testByteBufferRoundTrip() {
+ @Test func testByteBufferRoundTrip() {
let bytes = ByteBuffer(bytes: (0...UInt8.max))
var buffer = ByteBuffer()
bytes.encode(into: &buffer, context: .default)
- XCTAssertEqual(ByteBuffer.psqlType, .bytea)
-
+ #expect(ByteBuffer.psqlType == .bytea)
+
var result: ByteBuffer?
result = ByteBuffer(from: &buffer, type: .bytea, format: .binary, context: .default)
- XCTAssertEqual(bytes, result)
+ #expect(bytes == result)
}
- func testEncodeSequenceWhereElementUInt8() {
+ @Test func testEncodeSequenceWhereElementUInt8() {
struct ByteSequence: Sequence, PostgresEncodable {
typealias Element = UInt8
typealias Iterator = Array.Iterator
@@ -47,7 +48,7 @@ class Bytes_PSQLCodableTests: XCTestCase {
let sequence = ByteSequence()
var buffer = ByteBuffer()
sequence.encode(into: &buffer, context: .default)
- XCTAssertEqual(ByteSequence.psqlType, .bytea)
- XCTAssertEqual(buffer.readableBytes, 256)
+ #expect(ByteSequence.psqlType == .bytea)
+ #expect(buffer.readableBytes == 256)
}
}
diff --git a/Tests/PostgresNIOTests/New/Data/String+PSQLCodableTests.swift b/Tests/PostgresNIOTests/New/Data/String+PSQLCodableTests.swift
index aadeabff..c1843c2a 100644
--- a/Tests/PostgresNIOTests/New/Data/String+PSQLCodableTests.swift
+++ b/Tests/PostgresNIOTests/New/Data/String+PSQLCodableTests.swift
@@ -52,4 +52,15 @@ class String_PSQLCodableTests: XCTestCase {
XCTAssertEqual($0 as? PostgresDecodingError.Code, .failure)
}
}
+
+ func testDecodeFromJSONB() {
+ let json = #"{"hello": "world"}"#
+ var buffer = ByteBuffer()
+ buffer.writeInteger(UInt8(1))
+ buffer.writeString(json)
+
+ var decoded: String?
+ XCTAssertNoThrow(decoded = try String(from: &buffer, type: .jsonb, format: .binary, context: .default))
+ XCTAssertEqual(decoded, json)
+ }
}
diff --git a/Tests/PostgresNIOTests/New/Extensions/PSQLBackendMessageEncoder.swift b/Tests/PostgresNIOTests/New/Extensions/PSQLBackendMessageEncoder.swift
index 9614bf1e..0c6b37ef 100644
--- a/Tests/PostgresNIOTests/New/Extensions/PSQLBackendMessageEncoder.swift
+++ b/Tests/PostgresNIOTests/New/Extensions/PSQLBackendMessageEncoder.swift
@@ -28,6 +28,8 @@ struct PSQLBackendMessageEncoder: MessageToByteEncoder {
case .commandComplete(let string):
self.encode(messageID: message.id, payload: StringPayload(string), into: &buffer)
+ case .copyInResponse(let copyInResponse):
+ self.encode(messageID: message.id, payload: copyInResponse, into: &buffer)
case .dataRow(let row):
self.encode(messageID: message.id, payload: row, into: &buffer)
@@ -99,6 +101,8 @@ extension PostgresBackendMessage {
return .closeComplete
case .commandComplete:
return .commandComplete
+ case .copyInResponse:
+ return .copyInResponse
case .dataRow:
return .dataRow
case .emptyQueryResponse:
@@ -184,6 +188,16 @@ extension PostgresBackendMessage.BackendKeyData: PSQLMessagePayloadEncodable {
}
}
+extension PostgresBackendMessage.CopyInResponse: PSQLMessagePayloadEncodable {
+ public func encode(into buffer: inout ByteBuffer) {
+ buffer.writeInteger(Int8(self.format.rawValue))
+ buffer.writeInteger(Int16(self.columnFormats.count))
+ for columnFormat in columnFormats {
+ buffer.writeInteger(Int16(columnFormat.rawValue))
+ }
+ }
+}
+
extension DataRow: PSQLMessagePayloadEncodable {
public func encode(into buffer: inout ByteBuffer) {
buffer.writeInteger(self.columnCount, as: Int16.self)
diff --git a/Tests/PostgresNIOTests/New/Extensions/PSQLFrontendMessageDecoder.swift b/Tests/PostgresNIOTests/New/Extensions/PSQLFrontendMessageDecoder.swift
index 55ccd0a9..d913da22 100644
--- a/Tests/PostgresNIOTests/New/Extensions/PSQLFrontendMessageDecoder.swift
+++ b/Tests/PostgresNIOTests/New/Extensions/PSQLFrontendMessageDecoder.swift
@@ -168,6 +168,18 @@ extension PostgresFrontendMessage {
)
)
+ case .copyData:
+ return .copyData(CopyData(data: buffer))
+
+ case .copyDone:
+ return .copyDone
+
+ case .copyFail:
+ guard let message = buffer.readNullTerminatedString() else {
+ throw PSQLPartialDecodingError.fieldNotDecodable(type: String.self)
+ }
+ return .copyFail(CopyFail(message: message))
+
case .close:
preconditionFailure("TODO: Unimplemented")
diff --git a/Tests/PostgresNIOTests/New/Extensions/PostgresFrontendMessage.swift b/Tests/PostgresNIOTests/New/Extensions/PostgresFrontendMessage.swift
index 2532959a..5fc8144b 100644
--- a/Tests/PostgresNIOTests/New/Extensions/PostgresFrontendMessage.swift
+++ b/Tests/PostgresNIOTests/New/Extensions/PostgresFrontendMessage.swift
@@ -36,6 +36,14 @@ enum PostgresFrontendMessage: Equatable {
let secretKey: Int32
}
+ struct CopyData: Hashable {
+ var data: ByteBuffer
+ }
+
+ struct CopyFail: Hashable {
+ var message: String
+ }
+
enum Close: Hashable {
case preparedStatement(String)
case portal(String)
@@ -170,6 +178,9 @@ enum PostgresFrontendMessage: Equatable {
case bind(Bind)
case cancel(Cancel)
+ case copyData(CopyData)
+ case copyDone
+ case copyFail(CopyFail)
case close(Close)
case describe(Describe)
case execute(Execute)
@@ -186,6 +197,9 @@ enum PostgresFrontendMessage: Equatable {
enum ID: UInt8, Equatable {
case bind
+ case copyData
+ case copyDone
+ case copyFail
case close
case describe
case execute
@@ -201,12 +215,18 @@ enum PostgresFrontendMessage: Equatable {
switch rawValue {
case UInt8(ascii: "B"):
self = .bind
+ case UInt8(ascii: "c"):
+ self = .copyDone
case UInt8(ascii: "C"):
self = .close
+ case UInt8(ascii: "d"):
+ self = .copyData
case UInt8(ascii: "D"):
self = .describe
case UInt8(ascii: "E"):
self = .execute
+ case UInt8(ascii: "f"):
+ self = .copyFail
case UInt8(ascii: "H"):
self = .flush
case UInt8(ascii: "P"):
@@ -230,6 +250,12 @@ enum PostgresFrontendMessage: Equatable {
switch self {
case .bind:
return UInt8(ascii: "B")
+ case .copyData:
+ return UInt8(ascii: "d")
+ case .copyDone:
+ return UInt8(ascii: "c")
+ case .copyFail:
+ return UInt8(ascii: "f")
case .close:
return UInt8(ascii: "C")
case .describe:
@@ -263,6 +289,12 @@ extension PostgresFrontendMessage {
return .bind
case .cancel:
preconditionFailure("Cancel messages don't have an identifier")
+ case .copyData:
+ return .copyData
+ case .copyDone:
+ return .copyDone
+ case .copyFail:
+ return .copyFail
case .close:
return .close
case .describe:
diff --git a/Tests/PostgresNIOTests/New/Messages/AuthenticationTests.swift b/Tests/PostgresNIOTests/New/Messages/AuthenticationTests.swift
index 06e39aae..3b857157 100644
--- a/Tests/PostgresNIOTests/New/Messages/AuthenticationTests.swift
+++ b/Tests/PostgresNIOTests/New/Messages/AuthenticationTests.swift
@@ -1,10 +1,10 @@
-import XCTest
+import Testing
import NIOCore
import NIOTestUtils
@testable import PostgresNIO
-class AuthenticationTests: XCTestCase {
-
+@Suite struct AuthenticationTests {
+
func testDecodeAuthentication() {
var expected = [PostgresBackendMessage]()
var buffer = ByteBuffer()
@@ -39,9 +39,11 @@ class AuthenticationTests: XCTestCase {
encoder.encode(data: .authentication(.sspi), out: &buffer)
expected.append(.authentication(.sspi))
- XCTAssertNoThrow(try ByteToMessageDecoderVerifier.verifyDecoder(
- inputOutputPairs: [(buffer, expected)],
- decoderFactory: { PostgresBackendMessageDecoder(hasAlreadyReceivedBytes: false) }
- ))
+ #expect(throws: Never.self) {
+ try ByteToMessageDecoderVerifier.verifyDecoder(
+ inputOutputPairs: [(buffer, expected)],
+ decoderFactory: { PostgresBackendMessageDecoder(hasAlreadyReceivedBytes: false) }
+ )
+ }
}
}
diff --git a/Tests/PostgresNIOTests/New/Messages/BackendKeyDataTests.swift b/Tests/PostgresNIOTests/New/Messages/BackendKeyDataTests.swift
index d41607e3..204b544d 100644
--- a/Tests/PostgresNIOTests/New/Messages/BackendKeyDataTests.swift
+++ b/Tests/PostgresNIOTests/New/Messages/BackendKeyDataTests.swift
@@ -1,10 +1,10 @@
-import XCTest
+import Testing
import NIOCore
import NIOTestUtils
@testable import PostgresNIO
-class BackendKeyDataTests: XCTestCase {
- func testDecode() {
+@Suite struct BackendKeyDataTests {
+ @Test func testDecode() {
let buffer = ByteBuffer.backendMessage(id: .backendKeyData) { buffer in
buffer.writeInteger(Int32(1234))
buffer.writeInteger(Int32(4567))
@@ -14,12 +14,15 @@ class BackendKeyDataTests: XCTestCase {
(buffer, [PostgresBackendMessage.backendKeyData(.init(processID: 1234, secretKey: 4567))]),
]
- XCTAssertNoThrow(try ByteToMessageDecoderVerifier.verifyDecoder(
- inputOutputPairs: expectedInOuts,
- decoderFactory: { PostgresBackendMessageDecoder(hasAlreadyReceivedBytes: false) }))
+ #expect(throws: Never.self) {
+ try ByteToMessageDecoderVerifier.verifyDecoder(
+ inputOutputPairs: expectedInOuts,
+ decoderFactory: { PostgresBackendMessageDecoder(hasAlreadyReceivedBytes: false) }
+ )
+ }
}
- func testDecodeInvalidLength() {
+ @Test func testDecodeInvalidLength() {
var buffer = ByteBuffer()
buffer.psqlWriteBackendMessageID(.backendKeyData)
buffer.writeInteger(Int32(11))
@@ -30,10 +33,11 @@ class BackendKeyDataTests: XCTestCase {
(buffer, [PostgresBackendMessage.backendKeyData(.init(processID: 1234, secretKey: 4567))]),
]
- XCTAssertThrowsError(try ByteToMessageDecoderVerifier.verifyDecoder(
- inputOutputPairs: expected,
- decoderFactory: { PostgresBackendMessageDecoder(hasAlreadyReceivedBytes: false) })) {
- XCTAssert($0 is PostgresMessageDecodingError)
+ #expect(throws: PostgresMessageDecodingError.self) {
+ try ByteToMessageDecoderVerifier.verifyDecoder(
+ inputOutputPairs: expected,
+ decoderFactory: { PostgresBackendMessageDecoder(hasAlreadyReceivedBytes: false) }
+ )
}
}
}
diff --git a/Tests/PostgresNIOTests/New/Messages/BindTests.swift b/Tests/PostgresNIOTests/New/Messages/BindTests.swift
index d5ec5b30..24925fdf 100644
--- a/Tests/PostgresNIOTests/New/Messages/BindTests.swift
+++ b/Tests/PostgresNIOTests/New/Messages/BindTests.swift
@@ -1,10 +1,10 @@
-import XCTest
+import Testing
import NIOCore
@testable import PostgresNIO
-class BindTests: XCTestCase {
-
- func testEncodeBind() {
+@Suite struct BindTests {
+
+ @Test func testEncodeBind() {
var bindings = PostgresBindings()
bindings.append("Hello", context: .default)
bindings.append("World", context: .default)
@@ -14,34 +14,34 @@ class BindTests: XCTestCase {
encoder.bind(portalName: "", preparedStatementName: "", bind: bindings)
var byteBuffer = encoder.flushBuffer()
- XCTAssertEqual(byteBuffer.readableBytes, 37)
- XCTAssertEqual(PostgresFrontendMessage.ID.bind.rawValue, byteBuffer.readInteger(as: UInt8.self))
- XCTAssertEqual(byteBuffer.readInteger(as: Int32.self), 36)
- XCTAssertEqual("", byteBuffer.readNullTerminatedString())
- XCTAssertEqual("", byteBuffer.readNullTerminatedString())
+ #expect(byteBuffer.readableBytes == 37)
+ #expect(PostgresFrontendMessage.ID.bind.rawValue == byteBuffer.readInteger(as: UInt8.self))
+ #expect(byteBuffer.readInteger(as: Int32.self) == 36)
+ #expect("" == byteBuffer.readNullTerminatedString())
+ #expect("" == byteBuffer.readNullTerminatedString())
// the number of parameters
- XCTAssertEqual(2, byteBuffer.readInteger(as: Int16.self))
+ #expect(2 == byteBuffer.readInteger(as: Int16.self))
// all (two) parameters have the same format (binary)
- XCTAssertEqual(1, byteBuffer.readInteger(as: Int16.self))
- XCTAssertEqual(1, byteBuffer.readInteger(as: Int16.self))
-
+ #expect(1 == byteBuffer.readInteger(as: Int16.self))
+ #expect(1 == byteBuffer.readInteger(as: Int16.self))
+
// read number of parameters
- XCTAssertEqual(2, byteBuffer.readInteger(as: Int16.self))
-
+ #expect(2 == byteBuffer.readInteger(as: Int16.self))
+
// hello length
- XCTAssertEqual(5, byteBuffer.readInteger(as: Int32.self))
- XCTAssertEqual("Hello", byteBuffer.readString(length: 5))
-
+ #expect(5 == byteBuffer.readInteger(as: Int32.self))
+ #expect("Hello" == byteBuffer.readString(length: 5))
+
// world length
- XCTAssertEqual(5, byteBuffer.readInteger(as: Int32.self))
- XCTAssertEqual("World", byteBuffer.readString(length: 5))
-
+ #expect(5 == byteBuffer.readInteger(as: Int32.self))
+ #expect("World" == byteBuffer.readString(length: 5))
+
// all response values have the same format: therefore one format byte is next
- XCTAssertEqual(1, byteBuffer.readInteger(as: Int16.self))
+ #expect(1 == byteBuffer.readInteger(as: Int16.self))
// all response values have the same format (binary)
- XCTAssertEqual(1, byteBuffer.readInteger(as: Int16.self))
-
+ #expect(1 == byteBuffer.readInteger(as: Int16.self))
+
// nothing left to read
- XCTAssertEqual(byteBuffer.readableBytes, 0)
+ #expect(byteBuffer.readableBytes == 0)
}
}
diff --git a/Tests/PostgresNIOTests/New/Messages/CancelTests.swift b/Tests/PostgresNIOTests/New/Messages/CancelTests.swift
index 5548aae3..c2da01d3 100644
--- a/Tests/PostgresNIOTests/New/Messages/CancelTests.swift
+++ b/Tests/PostgresNIOTests/New/Messages/CancelTests.swift
@@ -1,21 +1,20 @@
-import XCTest
+import Testing
import NIOCore
@testable import PostgresNIO
-class CancelTests: XCTestCase {
-
- func testEncodeCancel() {
+@Suite struct CancelTests {
+ @Test func testEncodeCancel() {
let processID: Int32 = 1234
let secretKey: Int32 = 4567
var encoder = PostgresFrontendMessageEncoder(buffer: .init())
encoder.cancel(processID: processID, secretKey: secretKey)
var byteBuffer = encoder.flushBuffer()
- XCTAssertEqual(byteBuffer.readableBytes, 16)
- XCTAssertEqual(16, byteBuffer.readInteger(as: Int32.self)) // payload length
- XCTAssertEqual(80877102, byteBuffer.readInteger(as: Int32.self)) // cancel request code
- XCTAssertEqual(processID, byteBuffer.readInteger(as: Int32.self))
- XCTAssertEqual(secretKey, byteBuffer.readInteger(as: Int32.self))
- XCTAssertEqual(byteBuffer.readableBytes, 0)
+ #expect(byteBuffer.readableBytes == 16)
+ #expect(16 == byteBuffer.readInteger(as: Int32.self)) // payload length
+ #expect(80877102 == byteBuffer.readInteger(as: Int32.self)) // cancel request code
+ #expect(processID == byteBuffer.readInteger(as: Int32.self))
+ #expect(secretKey == byteBuffer.readInteger(as: Int32.self))
+ #expect(byteBuffer.readableBytes == 0)
}
}
diff --git a/Tests/PostgresNIOTests/New/Messages/CloseTests.swift b/Tests/PostgresNIOTests/New/Messages/CloseTests.swift
index a8e1cfeb..9d6f1b37 100644
--- a/Tests/PostgresNIOTests/New/Messages/CloseTests.swift
+++ b/Tests/PostgresNIOTests/New/Messages/CloseTests.swift
@@ -1,31 +1,31 @@
-import XCTest
+import Testing
import NIOCore
@testable import PostgresNIO
-class CloseTests: XCTestCase {
- func testEncodeClosePortal() {
+@Suite struct CloseTests {
+ @Test func testEncodeClosePortal() {
var encoder = PostgresFrontendMessageEncoder(buffer: .init())
encoder.closePortal("Hello")
var byteBuffer = encoder.flushBuffer()
- XCTAssertEqual(byteBuffer.readableBytes, 12)
- XCTAssertEqual(PostgresFrontendMessage.ID.close.rawValue, byteBuffer.readInteger(as: UInt8.self))
- XCTAssertEqual(11, byteBuffer.readInteger(as: Int32.self))
- XCTAssertEqual(UInt8(ascii: "P"), byteBuffer.readInteger(as: UInt8.self))
- XCTAssertEqual("Hello", byteBuffer.readNullTerminatedString())
- XCTAssertEqual(byteBuffer.readableBytes, 0)
+ #expect(byteBuffer.readableBytes == 12)
+ #expect(PostgresFrontendMessage.ID.close.rawValue == byteBuffer.readInteger(as: UInt8.self))
+ #expect(11 == byteBuffer.readInteger(as: Int32.self))
+ #expect(UInt8(ascii: "P") == byteBuffer.readInteger(as: UInt8.self))
+ #expect("Hello" == byteBuffer.readNullTerminatedString())
+ #expect(byteBuffer.readableBytes == 0)
}
- func testEncodeCloseUnnamedStatement() {
+ @Test func testEncodeCloseUnnamedStatement() {
var encoder = PostgresFrontendMessageEncoder(buffer: .init())
encoder.closePreparedStatement("")
var byteBuffer = encoder.flushBuffer()
- XCTAssertEqual(byteBuffer.readableBytes, 7)
- XCTAssertEqual(PostgresFrontendMessage.ID.close.rawValue, byteBuffer.readInteger(as: UInt8.self))
- XCTAssertEqual(6, byteBuffer.readInteger(as: Int32.self))
- XCTAssertEqual(UInt8(ascii: "S"), byteBuffer.readInteger(as: UInt8.self))
- XCTAssertEqual("", byteBuffer.readNullTerminatedString())
- XCTAssertEqual(byteBuffer.readableBytes, 0)
+ #expect(byteBuffer.readableBytes == 7)
+ #expect(PostgresFrontendMessage.ID.close.rawValue == byteBuffer.readInteger(as: UInt8.self))
+ #expect(6 == byteBuffer.readInteger(as: Int32.self))
+ #expect(UInt8(ascii: "S") == byteBuffer.readInteger(as: UInt8.self))
+ #expect("" == byteBuffer.readNullTerminatedString())
+ #expect(byteBuffer.readableBytes == 0)
}
}
diff --git a/Tests/PostgresNIOTests/New/Messages/CopyTests.swift b/Tests/PostgresNIOTests/New/Messages/CopyTests.swift
new file mode 100644
index 00000000..01136d05
--- /dev/null
+++ b/Tests/PostgresNIOTests/New/Messages/CopyTests.swift
@@ -0,0 +1,137 @@
+import Testing
+import NIOCore
+import NIOTestUtils
+@testable import PostgresNIO
+
+@Suite struct CopyTests {
+ @Test func testDecodeCopyInResponseMessage() throws {
+ let expected: [PostgresBackendMessage] = [
+ .copyInResponse(.init(format: .textual, columnFormats: [.textual, .textual])),
+ .copyInResponse(.init(format: .binary, columnFormats: [.binary, .binary])),
+ .copyInResponse(.init(format: .binary, columnFormats: [.textual, .binary]))
+ ]
+
+ var buffer = ByteBuffer()
+
+ for message in expected {
+ guard case .copyInResponse(let message) = message else {
+ Issue.record("Expected only to get copyInResponse here!")
+ return
+ }
+ buffer.writeBackendMessage(id: .copyInResponse ) { buffer in
+ buffer.writeInteger(Int8(message.format.rawValue))
+ buffer.writeInteger(Int16(message.columnFormats.count))
+ for columnFormat in message.columnFormats {
+ buffer.writeInteger(UInt16(columnFormat.rawValue))
+ }
+ }
+ }
+ try ByteToMessageDecoderVerifier.verifyDecoder(
+ inputOutputPairs: [(buffer, expected)],
+ decoderFactory: { PostgresBackendMessageDecoder(hasAlreadyReceivedBytes: true) }
+ )
+ }
+
+ @Test func testDecodeFailureBecauseOfEmptyMessage() {
+ var buffer = ByteBuffer()
+ buffer.writeBackendMessage(id: .copyInResponse) { _ in}
+
+ #expect(throws: PostgresMessageDecodingError.self) {
+ try ByteToMessageDecoderVerifier.verifyDecoder(
+ inputOutputPairs: [(buffer, [])],
+ decoderFactory: { PostgresBackendMessageDecoder(hasAlreadyReceivedBytes: true) }
+ )
+ }
+ }
+
+
+ @Test func testDecodeFailureBecauseOfInvalidFormat() {
+ var buffer = ByteBuffer()
+ buffer.writeBackendMessage(id: .copyInResponse) { buffer in
+ buffer.writeInteger(Int8(20)) // Only 0 and 1 are valid formats
+ }
+
+ #expect(throws: PostgresMessageDecodingError.self) {
+ try ByteToMessageDecoderVerifier.verifyDecoder(
+ inputOutputPairs: [(buffer, [])],
+ decoderFactory: { PostgresBackendMessageDecoder(hasAlreadyReceivedBytes: true) }
+ )
+ }
+ }
+
+ @Test func testDecodeFailureBecauseOfMissingColumnNumber() {
+ var buffer = ByteBuffer()
+ buffer.writeBackendMessage(id: .copyInResponse) { buffer in
+ buffer.writeInteger(Int8(0))
+ }
+
+ #expect(throws: PostgresMessageDecodingError.self) {
+ try ByteToMessageDecoderVerifier.verifyDecoder(
+ inputOutputPairs: [(buffer, [])],
+ decoderFactory: { PostgresBackendMessageDecoder(hasAlreadyReceivedBytes: true) }
+ )
+ }
+ }
+
+ @Test func testDecodeFailureBecauseOfMissingColumns() {
+ var buffer = ByteBuffer()
+ buffer.writeBackendMessage(id: .copyInResponse) { buffer in
+ buffer.writeInteger(Int8(0))
+ buffer.writeInteger(Int16(20)) // 20 columns promised, none given
+ }
+
+ #expect(throws: PostgresMessageDecodingError.self) {
+ try ByteToMessageDecoderVerifier.verifyDecoder(
+ inputOutputPairs: [(buffer, [])],
+ decoderFactory: { PostgresBackendMessageDecoder(hasAlreadyReceivedBytes: true) }
+ )
+ }
+ }
+
+ @Test func testDecodeFailureBecauseOfInvalidColumnFormat() {
+ var buffer = ByteBuffer()
+ buffer.writeBackendMessage(id: .copyInResponse) { buffer in
+ buffer.writeInteger(Int8(0))
+ buffer.writeInteger(Int16(1))
+ buffer.writeInteger(Int8(20)) // Only 0 and 1 are valid formats
+ }
+
+ #expect(throws: PostgresMessageDecodingError.self) {
+ try ByteToMessageDecoderVerifier.verifyDecoder(
+ inputOutputPairs: [(buffer, [])],
+ decoderFactory: { PostgresBackendMessageDecoder(hasAlreadyReceivedBytes: true) }
+ )
+ }
+ }
+
+ @Test func testEncodeCopyDataHeader() {
+ var encoder = PostgresFrontendMessageEncoder(buffer: .init())
+ encoder.copyDataHeader(dataLength: 3)
+ var byteBuffer = encoder.flushBuffer()
+
+ #expect(byteBuffer.readableBytes == 5)
+ #expect(PostgresFrontendMessage.ID.copyData.rawValue == byteBuffer.readInteger(as: UInt8.self))
+ #expect(byteBuffer.readInteger(as: Int32.self) == 7)
+ }
+
+ @Test func testEncodeCopyDone() {
+ var encoder = PostgresFrontendMessageEncoder(buffer: .init())
+ encoder.copyDone()
+ var byteBuffer = encoder.flushBuffer()
+
+ #expect(byteBuffer.readableBytes == 5)
+ #expect(PostgresFrontendMessage.ID.copyDone.rawValue == byteBuffer.readInteger(as: UInt8.self))
+ #expect(byteBuffer.readInteger(as: Int32.self) == 4)
+ }
+
+ @Test func testEncodeCopyFail() {
+ var encoder = PostgresFrontendMessageEncoder(buffer: .init())
+ encoder.copyFail(message: "Oh, no :(")
+ var byteBuffer = encoder.flushBuffer()
+
+ #expect(byteBuffer.readableBytes == 15)
+ #expect(PostgresFrontendMessage.ID.copyFail.rawValue == byteBuffer.readInteger(as: UInt8.self))
+ #expect(byteBuffer.readInteger(as: Int32.self) == 14)
+ #expect(byteBuffer.readNullTerminatedString() == "Oh, no :(")
+ }
+}
diff --git a/Tests/PostgresNIOTests/New/Messages/DataRowTests.swift b/Tests/PostgresNIOTests/New/Messages/DataRowTests.swift
index a90d1e93..f185877a 100644
--- a/Tests/PostgresNIOTests/New/Messages/DataRowTests.swift
+++ b/Tests/PostgresNIOTests/New/Messages/DataRowTests.swift
@@ -1,10 +1,11 @@
-import XCTest
+import Foundation
+import Testing
import NIOCore
import NIOTestUtils
@testable import PostgresNIO
-class DataRowTests: XCTestCase {
- func testDecode() {
+@Suite struct DataRowTests {
+ @Test func testDecode() {
let buffer = ByteBuffer.backendMessage(id: .dataRow) { buffer in
// the data row has 3 columns
buffer.writeInteger(3, as: Int16.self)
@@ -26,23 +27,26 @@ class DataRowTests: XCTestCase {
(buffer, [PostgresBackendMessage.dataRow(.init(columnCount: 3, bytes: rowSlice))]),
]
- XCTAssertNoThrow(try ByteToMessageDecoderVerifier.verifyDecoder(
- inputOutputPairs: expectedInOuts,
- decoderFactory: { PostgresBackendMessageDecoder(hasAlreadyReceivedBytes: false) }))
+ #expect(throws: Never.self) {
+ try ByteToMessageDecoderVerifier.verifyDecoder(
+ inputOutputPairs: expectedInOuts,
+ decoderFactory: { PostgresBackendMessageDecoder(hasAlreadyReceivedBytes: false) }
+ )
+ }
}
- func testIteratingElements() {
+ @Test func testIteratingElements() {
let dataRow = DataRow.makeTestDataRow(nil, ByteBuffer(), ByteBuffer(repeating: 5, count: 10))
var iterator = dataRow.makeIterator()
- XCTAssertEqual(dataRow.count, 3)
- XCTAssertEqual(iterator.next(), .some(.none))
- XCTAssertEqual(iterator.next(), ByteBuffer())
- XCTAssertEqual(iterator.next(), ByteBuffer(repeating: 5, count: 10))
- XCTAssertEqual(iterator.next(), .none)
+ #expect(dataRow.count == 3)
+ #expect(iterator.next() == .some(.none))
+ #expect(iterator.next() == ByteBuffer())
+ #expect(iterator.next() == ByteBuffer(repeating: 5, count: 10))
+ #expect(iterator.next() == .none)
}
- func testIndexAfterAndSubscript() {
+ @Test func testIndexAfterAndSubscript() {
let dataRow = DataRow.makeTestDataRow(
nil,
ByteBuffer(),
@@ -51,18 +55,18 @@ class DataRowTests: XCTestCase {
)
var index = dataRow.startIndex
- XCTAssertEqual(dataRow[index], .none)
+ #expect(dataRow[index] == .none)
index = dataRow.index(after: index)
- XCTAssertEqual(dataRow[index], ByteBuffer())
+ #expect(dataRow[index] == ByteBuffer())
index = dataRow.index(after: index)
- XCTAssertEqual(dataRow[index], ByteBuffer(repeating: 5, count: 10))
+ #expect(dataRow[index] == ByteBuffer(repeating: 5, count: 10))
index = dataRow.index(after: index)
- XCTAssertEqual(dataRow[index], .none)
+ #expect(dataRow[index] == .none)
index = dataRow.index(after: index)
- XCTAssertEqual(index, dataRow.endIndex)
+ #expect(index == dataRow.endIndex)
}
- func testIndexComparison() {
+ @Test func testIndexComparison() {
let dataRow = DataRow.makeTestDataRow(
nil,
ByteBuffer(),
@@ -73,18 +77,18 @@ class DataRowTests: XCTestCase {
let startIndex = dataRow.startIndex
let secondIndex = dataRow.index(after: startIndex)
- XCTAssertLessThanOrEqual(startIndex, secondIndex)
- XCTAssertLessThan(startIndex, secondIndex)
-
- XCTAssertGreaterThanOrEqual(secondIndex, startIndex)
- XCTAssertGreaterThan(secondIndex, startIndex)
-
- XCTAssertFalse(secondIndex == startIndex)
- XCTAssertEqual(secondIndex, secondIndex)
- XCTAssertEqual(startIndex, startIndex)
+ #expect(startIndex <= secondIndex)
+ #expect(startIndex < secondIndex)
+
+ #expect(secondIndex >= startIndex)
+ #expect(secondIndex > startIndex)
+
+ #expect(secondIndex != startIndex)
+ #expect(secondIndex == secondIndex)
+ #expect(startIndex == startIndex)
}
- func testColumnSubscript() {
+ @Test func testColumnSubscript() {
let dataRow = DataRow.makeTestDataRow(
nil,
ByteBuffer(),
@@ -92,14 +96,14 @@ class DataRowTests: XCTestCase {
nil
)
- XCTAssertEqual(dataRow.count, 4)
- XCTAssertEqual(dataRow[column: 0], .none)
- XCTAssertEqual(dataRow[column: 1], ByteBuffer())
- XCTAssertEqual(dataRow[column: 2], ByteBuffer(repeating: 5, count: 10))
- XCTAssertEqual(dataRow[column: 3], .none)
+ #expect(dataRow.count == 4)
+ #expect(dataRow[column: 0] == .none)
+ #expect(dataRow[column: 1] == ByteBuffer())
+ #expect(dataRow[column: 2] == ByteBuffer(repeating: 5, count: 10))
+ #expect(dataRow[column: 3] == .none)
}
- func testWithContiguousStorageIfAvailable() {
+ @Test func testWithContiguousStorageIfAvailable() {
let dataRow = DataRow.makeTestDataRow(
nil,
ByteBuffer(),
@@ -107,9 +111,9 @@ class DataRowTests: XCTestCase {
nil
)
- XCTAssertNil(dataRow.withContiguousStorageIfAvailable { _ in
- return XCTFail("DataRow does not have a contiguous storage")
- })
+ #expect(dataRow.withContiguousStorageIfAvailable { _ in
+ Issue.record("DataRow does not have a contiguous storage")
+ } == nil)
}
}
diff --git a/Tests/PostgresNIOTests/New/Messages/DescribeTests.swift b/Tests/PostgresNIOTests/New/Messages/DescribeTests.swift
index cb3c745b..42a521aa 100644
--- a/Tests/PostgresNIOTests/New/Messages/DescribeTests.swift
+++ b/Tests/PostgresNIOTests/New/Messages/DescribeTests.swift
@@ -1,33 +1,32 @@
-import XCTest
+import Testing
import NIOCore
@testable import PostgresNIO
-class DescribeTests: XCTestCase {
-
- func testEncodeDescribePortal() {
+@Suite struct DescribeTests {
+ @Test func testEncodeDescribePortal() {
var encoder = PostgresFrontendMessageEncoder(buffer: .init())
encoder.describePortal("Hello")
var byteBuffer = encoder.flushBuffer()
- XCTAssertEqual(byteBuffer.readableBytes, 12)
- XCTAssertEqual(PostgresFrontendMessage.ID.describe.rawValue, byteBuffer.readInteger(as: UInt8.self))
- XCTAssertEqual(11, byteBuffer.readInteger(as: Int32.self))
- XCTAssertEqual(UInt8(ascii: "P"), byteBuffer.readInteger(as: UInt8.self))
- XCTAssertEqual("Hello", byteBuffer.readNullTerminatedString())
- XCTAssertEqual(byteBuffer.readableBytes, 0)
+ #expect(byteBuffer.readableBytes == 12)
+ #expect(PostgresFrontendMessage.ID.describe.rawValue == byteBuffer.readInteger(as: UInt8.self))
+ #expect(11 == byteBuffer.readInteger(as: Int32.self))
+ #expect(UInt8(ascii: "P") == byteBuffer.readInteger(as: UInt8.self))
+ #expect("Hello" == byteBuffer.readNullTerminatedString())
+ #expect(byteBuffer.readableBytes == 0)
}
-
- func testEncodeDescribeUnnamedStatement() {
+
+ @Test func testEncodeDescribeUnnamedStatement() {
var encoder = PostgresFrontendMessageEncoder(buffer: .init())
encoder.describePreparedStatement("")
var byteBuffer = encoder.flushBuffer()
- XCTAssertEqual(byteBuffer.readableBytes, 7)
- XCTAssertEqual(PostgresFrontendMessage.ID.describe.rawValue, byteBuffer.readInteger(as: UInt8.self))
- XCTAssertEqual(6, byteBuffer.readInteger(as: Int32.self))
- XCTAssertEqual(UInt8(ascii: "S"), byteBuffer.readInteger(as: UInt8.self))
- XCTAssertEqual("", byteBuffer.readNullTerminatedString())
- XCTAssertEqual(byteBuffer.readableBytes, 0)
+ #expect(byteBuffer.readableBytes == 7)
+ #expect(PostgresFrontendMessage.ID.describe.rawValue == byteBuffer.readInteger(as: UInt8.self))
+ #expect(6 == byteBuffer.readInteger(as: Int32.self))
+ #expect(UInt8(ascii: "S") == byteBuffer.readInteger(as: UInt8.self))
+ #expect("" == byteBuffer.readNullTerminatedString())
+ #expect(byteBuffer.readableBytes == 0)
}
}
diff --git a/Tests/PostgresNIOTests/New/Messages/ExecuteTests.swift b/Tests/PostgresNIOTests/New/Messages/ExecuteTests.swift
index 834ad0dd..985ab10e 100644
--- a/Tests/PostgresNIOTests/New/Messages/ExecuteTests.swift
+++ b/Tests/PostgresNIOTests/New/Messages/ExecuteTests.swift
@@ -1,18 +1,17 @@
-import XCTest
+import Testing
import NIOCore
@testable import PostgresNIO
-class ExecuteTests: XCTestCase {
-
- func testEncodeExecute() {
+@Suite struct ExecuteTests {
+ @Test func testEncodeExecute() {
var encoder = PostgresFrontendMessageEncoder(buffer: .init())
encoder.execute(portalName: "", maxNumberOfRows: 0)
var byteBuffer = encoder.flushBuffer()
- XCTAssertEqual(byteBuffer.readableBytes, 10) // 1 (id) + 4 (length) + 1 (empty null terminated string) + 4 (count)
- XCTAssertEqual(PostgresFrontendMessage.ID.execute.rawValue, byteBuffer.readInteger(as: UInt8.self))
- XCTAssertEqual(9, byteBuffer.readInteger(as: Int32.self)) // length
- XCTAssertEqual("", byteBuffer.readNullTerminatedString())
- XCTAssertEqual(0, byteBuffer.readInteger(as: Int32.self))
+ #expect(byteBuffer.readableBytes == 10) // 1 (id) + 4 (length) + 1 (empty null terminated string) + 4 (count)
+ #expect(PostgresFrontendMessage.ID.execute.rawValue == byteBuffer.readInteger(as: UInt8.self))
+ #expect(9 == byteBuffer.readInteger(as: Int32.self)) // length
+ #expect("" == byteBuffer.readNullTerminatedString())
+ #expect(0 == byteBuffer.readInteger(as: Int32.self))
}
}
diff --git a/Tests/PostgresNIOTests/New/Messages/ParseTests.swift b/Tests/PostgresNIOTests/New/Messages/ParseTests.swift
index 9f81e4e4..e40dbbfe 100644
--- a/Tests/PostgresNIOTests/New/Messages/ParseTests.swift
+++ b/Tests/PostgresNIOTests/New/Messages/ParseTests.swift
@@ -1,9 +1,9 @@
-import XCTest
+import Testing
import NIOCore
@testable import PostgresNIO
-class ParseTests: XCTestCase {
- func testEncode() {
+@Suite struct ParseTests {
+ @Test func testEncode() {
let preparedStatementName = "test"
let query = "SELECT version()"
let parameters: [PostgresDataType] = [.bool, .int8, .bytea, .varchar, .text, .uuid, .json, .jsonbArray]
@@ -22,14 +22,14 @@ class ParseTests: XCTestCase {
// + 4 preparedStatement (3 + 1 null terminator)
// + 1 query ()
- XCTAssertEqual(byteBuffer.readableBytes, length)
- XCTAssertEqual(byteBuffer.readInteger(as: UInt8.self), PostgresFrontendMessage.ID.parse.rawValue)
- XCTAssertEqual(byteBuffer.readInteger(as: Int32.self), Int32(length - 1))
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), preparedStatementName)
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), query)
- XCTAssertEqual(byteBuffer.readInteger(as: UInt16.self), UInt16(parameters.count))
+ #expect(byteBuffer.readableBytes == length)
+ #expect(byteBuffer.readInteger(as: UInt8.self) == PostgresFrontendMessage.ID.parse.rawValue)
+ #expect(byteBuffer.readInteger(as: Int32.self) == Int32(length - 1))
+ #expect(byteBuffer.readNullTerminatedString() == preparedStatementName)
+ #expect(byteBuffer.readNullTerminatedString() == query)
+ #expect(byteBuffer.readInteger(as: UInt16.self) == UInt16(parameters.count))
for dataType in parameters {
- XCTAssertEqual(byteBuffer.readInteger(as: UInt32.self), dataType.rawValue)
+ #expect(byteBuffer.readInteger(as: UInt32.self) == dataType.rawValue)
}
}
}
diff --git a/Tests/PostgresNIOTests/New/Messages/PasswordTests.swift b/Tests/PostgresNIOTests/New/Messages/PasswordTests.swift
index 4a4833d2..cf4ad83f 100644
--- a/Tests/PostgresNIOTests/New/Messages/PasswordTests.swift
+++ b/Tests/PostgresNIOTests/New/Messages/PasswordTests.swift
@@ -1,10 +1,9 @@
-import XCTest
+import Testing
import NIOCore
@testable import PostgresNIO
-class PasswordTests: XCTestCase {
-
- func testEncodePassword() {
+@Suite struct PasswordTests {
+ @Test func testEncodePassword() {
var encoder = PostgresFrontendMessageEncoder(buffer: .init())
// md522d085ed8dc3377968dc1c1a40519a2a = "abc123" with salt 1, 2, 3, 4
let password = "md522d085ed8dc3377968dc1c1a40519a2a"
@@ -13,9 +12,9 @@ class PasswordTests: XCTestCase {
let expectedLength = 41 // 1 (id) + 4 (length) + 35 (string) + 1 (null termination)
- XCTAssertEqual(byteBuffer.readableBytes, expectedLength)
- XCTAssertEqual(byteBuffer.readInteger(as: UInt8.self), PostgresFrontendMessage.ID.password.rawValue)
- XCTAssertEqual(byteBuffer.readInteger(as: Int32.self), Int32(expectedLength - 1)) // length
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), "md522d085ed8dc3377968dc1c1a40519a2a")
+ #expect(byteBuffer.readableBytes == expectedLength)
+ #expect(byteBuffer.readInteger(as: UInt8.self) == PostgresFrontendMessage.ID.password.rawValue)
+ #expect(byteBuffer.readInteger(as: Int32.self) == Int32(expectedLength - 1)) // length
+ #expect(byteBuffer.readNullTerminatedString() == "md522d085ed8dc3377968dc1c1a40519a2a")
}
}
diff --git a/Tests/PostgresNIOTests/New/Messages/SASLInitialResponseTests.swift b/Tests/PostgresNIOTests/New/Messages/SASLInitialResponseTests.swift
index 90aa6b34..7ba31057 100644
--- a/Tests/PostgresNIOTests/New/Messages/SASLInitialResponseTests.swift
+++ b/Tests/PostgresNIOTests/New/Messages/SASLInitialResponseTests.swift
@@ -1,10 +1,10 @@
-import XCTest
+import Testing
import NIOCore
@testable import PostgresNIO
-class SASLInitialResponseTests: XCTestCase {
+@Suite struct SASLInitialResponseTests {
- func testEncode() {
+ @Test func testEncode() {
var encoder = PostgresFrontendMessageEncoder(buffer: .init())
let saslMechanism = "hello"
let initialData: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7]
@@ -19,16 +19,16 @@ class SASLInitialResponseTests: XCTestCase {
// + 4 initialData length
// + 8 initialData
- XCTAssertEqual(byteBuffer.readableBytes, length)
- XCTAssertEqual(byteBuffer.readInteger(as: UInt8.self), PostgresFrontendMessage.ID.saslInitialResponse.rawValue)
- XCTAssertEqual(byteBuffer.readInteger(as: Int32.self), Int32(length - 1))
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), saslMechanism)
- XCTAssertEqual(byteBuffer.readInteger(as: Int32.self), Int32(initialData.count))
- XCTAssertEqual(byteBuffer.readBytes(length: initialData.count), initialData)
- XCTAssertEqual(byteBuffer.readableBytes, 0)
+ #expect(byteBuffer.readableBytes == length)
+ #expect(byteBuffer.readInteger(as: UInt8.self) == PostgresFrontendMessage.ID.saslInitialResponse.rawValue)
+ #expect(byteBuffer.readInteger(as: Int32.self) == Int32(length - 1))
+ #expect(byteBuffer.readNullTerminatedString() == saslMechanism)
+ #expect(byteBuffer.readInteger(as: Int32.self) == Int32(initialData.count))
+ #expect(byteBuffer.readBytes(length: initialData.count) == initialData)
+ #expect(byteBuffer.readableBytes == 0)
}
- func testEncodeWithoutData() {
+ @Test func testEncodeWithoutData() {
var encoder = PostgresFrontendMessageEncoder(buffer: .init())
let saslMechanism = "hello"
let initialData: [UInt8] = []
@@ -43,12 +43,12 @@ class SASLInitialResponseTests: XCTestCase {
// + 4 initialData length
// + 0 initialData
- XCTAssertEqual(byteBuffer.readableBytes, length)
- XCTAssertEqual(byteBuffer.readInteger(as: UInt8.self), PostgresFrontendMessage.ID.saslInitialResponse.rawValue)
- XCTAssertEqual(byteBuffer.readInteger(as: Int32.self), Int32(length - 1))
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), saslMechanism)
- XCTAssertEqual(byteBuffer.readInteger(as: Int32.self), Int32(-1))
- XCTAssertEqual(byteBuffer.readBytes(length: initialData.count), initialData)
- XCTAssertEqual(byteBuffer.readableBytes, 0)
+ #expect(byteBuffer.readableBytes == length)
+ #expect(byteBuffer.readInteger(as: UInt8.self) == PostgresFrontendMessage.ID.saslInitialResponse.rawValue)
+ #expect(byteBuffer.readInteger(as: Int32.self) == Int32(length - 1))
+ #expect(byteBuffer.readNullTerminatedString() == saslMechanism)
+ #expect(byteBuffer.readInteger(as: Int32.self) == Int32(-1))
+ #expect(byteBuffer.readBytes(length: initialData.count) == initialData)
+ #expect(byteBuffer.readableBytes == 0)
}
}
diff --git a/Tests/PostgresNIOTests/New/Messages/SASLResponseTests.swift b/Tests/PostgresNIOTests/New/Messages/SASLResponseTests.swift
index cdb0f10b..a2a06418 100644
--- a/Tests/PostgresNIOTests/New/Messages/SASLResponseTests.swift
+++ b/Tests/PostgresNIOTests/New/Messages/SASLResponseTests.swift
@@ -1,10 +1,10 @@
-import XCTest
+import Testing
import NIOCore
@testable import PostgresNIO
-class SASLResponseTests: XCTestCase {
+@Suite struct SASLResponseTests {
- func testEncodeWithData() {
+ @Test func testEncodeWithData() {
var encoder = PostgresFrontendMessageEncoder(buffer: .init())
let data: [UInt8] = [0, 1, 2, 3, 4, 5, 6, 7]
encoder.saslResponse(data)
@@ -12,14 +12,14 @@ class SASLResponseTests: XCTestCase {
let length: Int = 1 + 4 + (data.count)
- XCTAssertEqual(byteBuffer.readableBytes, length)
- XCTAssertEqual(byteBuffer.readInteger(as: UInt8.self), PostgresFrontendMessage.ID.saslResponse.rawValue)
- XCTAssertEqual(byteBuffer.readInteger(as: Int32.self), Int32(length - 1))
- XCTAssertEqual(byteBuffer.readBytes(length: data.count), data)
- XCTAssertEqual(byteBuffer.readableBytes, 0)
+ #expect(byteBuffer.readableBytes == length)
+ #expect(byteBuffer.readInteger(as: UInt8.self) == PostgresFrontendMessage.ID.saslResponse.rawValue)
+ #expect(byteBuffer.readInteger(as: Int32.self) == Int32(length - 1))
+ #expect(byteBuffer.readBytes(length: data.count) == data)
+ #expect(byteBuffer.readableBytes == 0)
}
- func testEncodeWithoutData() {
+ @Test func testEncodeWithoutData() {
var encoder = PostgresFrontendMessageEncoder(buffer: .init())
let data: [UInt8] = []
encoder.saslResponse(data)
@@ -27,9 +27,9 @@ class SASLResponseTests: XCTestCase {
let length: Int = 1 + 4
- XCTAssertEqual(byteBuffer.readableBytes, length)
- XCTAssertEqual(byteBuffer.readInteger(as: UInt8.self), PostgresFrontendMessage.ID.saslResponse.rawValue)
- XCTAssertEqual(byteBuffer.readInteger(as: Int32.self), Int32(length - 1))
- XCTAssertEqual(byteBuffer.readableBytes, 0)
+ #expect(byteBuffer.readableBytes == length)
+ #expect(byteBuffer.readInteger(as: UInt8.self) == PostgresFrontendMessage.ID.saslResponse.rawValue)
+ #expect(byteBuffer.readInteger(as: Int32.self) == Int32(length - 1))
+ #expect(byteBuffer.readableBytes == 0)
}
}
diff --git a/Tests/PostgresNIOTests/New/Messages/StartupTests.swift b/Tests/PostgresNIOTests/New/Messages/StartupTests.swift
index 5af3bf34..23d022d9 100644
--- a/Tests/PostgresNIOTests/New/Messages/StartupTests.swift
+++ b/Tests/PostgresNIOTests/New/Messages/StartupTests.swift
@@ -1,10 +1,9 @@
-import XCTest
+import Testing
import NIOCore
@testable import PostgresNIO
-class StartupTests: XCTestCase {
-
- func testStartupMessageWithDatabase() {
+@Suite struct StartupTests {
+ @Test func testStartupMessageWithDatabase() {
var encoder = PostgresFrontendMessageEncoder(buffer: .init())
var byteBuffer = ByteBuffer()
@@ -15,18 +14,18 @@ class StartupTests: XCTestCase {
byteBuffer = encoder.flushBuffer()
let byteBufferLength = Int32(byteBuffer.readableBytes)
- XCTAssertEqual(byteBufferLength, byteBuffer.readInteger())
- XCTAssertEqual(PostgresFrontendMessage.Startup.versionThree, byteBuffer.readInteger())
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), "user")
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), "test")
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), "database")
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), "abc123")
- XCTAssertEqual(byteBuffer.readInteger(), UInt8(0))
+ #expect(byteBufferLength == byteBuffer.readInteger())
+ #expect(PostgresFrontendMessage.Startup.versionThree == byteBuffer.readInteger())
+ #expect(byteBuffer.readNullTerminatedString() == "user")
+ #expect(byteBuffer.readNullTerminatedString() == "test")
+ #expect(byteBuffer.readNullTerminatedString() == "database")
+ #expect(byteBuffer.readNullTerminatedString() == "abc123")
+ #expect(byteBuffer.readInteger() == UInt8(0))
- XCTAssertEqual(byteBuffer.readableBytes, 0)
+ #expect(byteBuffer.readableBytes == 0)
}
- func testStartupMessageWithoutDatabase() {
+ @Test func testStartupMessageWithoutDatabase() {
var encoder = PostgresFrontendMessageEncoder(buffer: .init())
var byteBuffer = ByteBuffer()
@@ -36,16 +35,16 @@ class StartupTests: XCTestCase {
byteBuffer = encoder.flushBuffer()
let byteBufferLength = Int32(byteBuffer.readableBytes)
- XCTAssertEqual(byteBufferLength, byteBuffer.readInteger())
- XCTAssertEqual(PostgresFrontendMessage.Startup.versionThree, byteBuffer.readInteger())
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), "user")
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), "test")
- XCTAssertEqual(byteBuffer.readInteger(), UInt8(0))
+ #expect(byteBufferLength == byteBuffer.readInteger())
+ #expect(PostgresFrontendMessage.Startup.versionThree == byteBuffer.readInteger())
+ #expect(byteBuffer.readNullTerminatedString() == "user")
+ #expect(byteBuffer.readNullTerminatedString() == "test")
+ #expect(byteBuffer.readInteger() == UInt8(0))
- XCTAssertEqual(byteBuffer.readableBytes, 0)
+ #expect(byteBuffer.readableBytes == 0)
}
- func testStartupMessageWithAdditionalOptions() {
+ @Test func testStartupMessageWithAdditionalOptions() {
var encoder = PostgresFrontendMessageEncoder(buffer: .init())
var byteBuffer = ByteBuffer()
@@ -56,17 +55,17 @@ class StartupTests: XCTestCase {
byteBuffer = encoder.flushBuffer()
let byteBufferLength = Int32(byteBuffer.readableBytes)
- XCTAssertEqual(byteBufferLength, byteBuffer.readInteger())
- XCTAssertEqual(PostgresFrontendMessage.Startup.versionThree, byteBuffer.readInteger())
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), "user")
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), "test")
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), "database")
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), "abc123")
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), "some")
- XCTAssertEqual(byteBuffer.readNullTerminatedString(), "options")
- XCTAssertEqual(byteBuffer.readInteger(), UInt8(0))
-
- XCTAssertEqual(byteBuffer.readableBytes, 0)
+ #expect(byteBufferLength == byteBuffer.readInteger())
+ #expect(PostgresFrontendMessage.Startup.versionThree == byteBuffer.readInteger())
+ #expect(byteBuffer.readNullTerminatedString() == "user")
+ #expect(byteBuffer.readNullTerminatedString() == "test")
+ #expect(byteBuffer.readNullTerminatedString() == "database")
+ #expect(byteBuffer.readNullTerminatedString() == "abc123")
+ #expect(byteBuffer.readNullTerminatedString() == "some")
+ #expect(byteBuffer.readNullTerminatedString() == "options")
+ #expect(byteBuffer.readInteger() == UInt8(0))
+
+ #expect(byteBuffer.readableBytes == 0)
}
}
diff --git a/Tests/PostgresNIOTests/New/PostgresCellTests.swift b/Tests/PostgresNIOTests/New/PostgresCellTests.swift
index 6458d063..4ed64b3f 100644
--- a/Tests/PostgresNIOTests/New/PostgresCellTests.swift
+++ b/Tests/PostgresNIOTests/New/PostgresCellTests.swift
@@ -1,9 +1,9 @@
@testable import PostgresNIO
-import XCTest
+import Testing
import NIOCore
-final class PostgresCellTests: XCTestCase {
- func testDecodingANonOptionalString() {
+@Suite struct PostgresCellTests {
+ @Test func testDecodingANonOptionalString() {
let cell = PostgresCell(
bytes: ByteBuffer(string: "Hello world"),
dataType: .text,
@@ -13,11 +13,13 @@ final class PostgresCellTests: XCTestCase {
)
var result: String?
- XCTAssertNoThrow(result = try cell.decode(String.self, context: .default))
- XCTAssertEqual(result, "Hello world")
+ #expect(throws: Never.self) {
+ result = try cell.decode(String.self, context: .default)
+ }
+ #expect(result == "Hello world")
}
- func testDecodingAnOptionalString() {
+ @Test func testDecodingAnOptionalString() {
let cell = PostgresCell(
bytes: nil,
dataType: .text,
@@ -27,11 +29,13 @@ final class PostgresCellTests: XCTestCase {
)
var result: String? = "test"
- XCTAssertNoThrow(result = try cell.decode(String?.self, context: .default))
- XCTAssertNil(result)
+ #expect(throws: Never.self) {
+ result = try cell.decode(String?.self, context: .default)
+ }
+ #expect(result == nil)
}
- func testDecodingFailure() {
+ @Test func testDecodingFailure() {
let cell = PostgresCell(
bytes: ByteBuffer(string: "Hello world"),
dataType: .text,
@@ -40,19 +44,44 @@ final class PostgresCellTests: XCTestCase {
columnIndex: 1
)
- XCTAssertThrowsError(try cell.decode(Int?.self, context: .default)) {
- guard let error = $0 as? PostgresDecodingError else {
- return XCTFail("Unexpected error")
+ #if compiler(>=6.1)
+ let error = #expect(throws: PostgresDecodingError.self) {
+ try cell.decode(Int?.self, context: .default)
+ }
+ guard let error else {
+ Issue.record("Expected error at this point")
+ return
+ }
+
+ #expect(error.file == #fileID)
+ #expect(error.line == #line - 9)
+ #expect(error.code == .typeMismatch)
+ #expect(error.columnName == "hello")
+ #expect(error.columnIndex == 1)
+ let correctType = error.targetType == Int?.self
+ #expect(correctType)
+ #expect(error.postgresType == .text)
+ #expect(error.postgresFormat == .binary)
+ #else
+ do {
+ _ = try cell.decode(Int?.self, context: .default)
+ Issue.record("Expected to throw")
+ } catch {
+ guard let error = error as? PostgresDecodingError else {
+ Issue.record("Expected error at this point")
+ return
}
- XCTAssertEqual(error.file, #fileID)
- XCTAssertEqual(error.line, #line - 6)
- XCTAssertEqual(error.code, .typeMismatch)
- XCTAssertEqual(error.columnName, "hello")
- XCTAssertEqual(error.columnIndex, 1)
- XCTAssert(error.targetType == Int?.self)
- XCTAssertEqual(error.postgresType, .text)
- XCTAssertEqual(error.postgresFormat, .binary)
+ #expect(error.file == #fileID)
+ #expect(error.line == #line - 9)
+ #expect(error.code == .typeMismatch)
+ #expect(error.columnName == "hello")
+ #expect(error.columnIndex == 1)
+ let correctType = error.targetType == Int?.self
+ #expect(correctType)
+ #expect(error.postgresType == .text)
+ #expect(error.postgresFormat == .binary)
}
+ #endif
}
}
diff --git a/Tests/PostgresNIOTests/New/PostgresConnectionTests.swift b/Tests/PostgresNIOTests/New/PostgresConnectionTests.swift
index d0f8e2b0..b4658079 100644
--- a/Tests/PostgresNIOTests/New/PostgresConnectionTests.swift
+++ b/Tests/PostgresNIOTests/New/PostgresConnectionTests.swift
@@ -1,27 +1,29 @@
import NIOCore
import NIOPosix
import NIOEmbedded
-import XCTest
+import Testing
import Logging
@testable import PostgresNIO
-class PostgresConnectionTests: XCTestCase {
+@Suite struct PostgresConnectionTests {
let logger = Logger(label: "PostgresConnectionTests")
- func testConnectionFailure() {
+ @Test func testConnectionFailure() {
// We start a local server and close it immediately to ensure that the port
// number we try to connect to is not used by any other process.
- let eventLoopGroup = MultiThreadedEventLoopGroup(numberOfThreads: 1)
- defer { XCTAssertNoThrow(try eventLoopGroup.syncShutdownGracefully()) }
-
+ let eventLoopGroup = NIOSingletons.posixEventLoopGroup
+
var tempChannel: Channel?
- XCTAssertNoThrow(tempChannel = try ServerBootstrap(group: eventLoopGroup)
- .bind(to: .init(ipAddress: "127.0.0.1", port: 0)).wait())
+ #expect(throws: Never.self) {
+ tempChannel = try ServerBootstrap(group: eventLoopGroup)
+ .bind(to: .init(ipAddress: "127.0.0.1", port: 0)).wait()
+ }
let maybePort = tempChannel?.localAddress?.port
- XCTAssertNoThrow(try tempChannel?.close().wait())
+ #expect(throws: Never.self) { try tempChannel?.close().wait() }
guard let port = maybePort else {
- return XCTFail("Could not get port number from temp started server")
+ Issue.record("Could not get port number from temp started server")
+ return
}
let config = PostgresConnection.Configuration(
@@ -33,12 +35,14 @@ class PostgresConnectionTests: XCTestCase {
var logger = Logger.psqlTest
logger.logLevel = .trace
- XCTAssertThrowsError(try PostgresConnection.connect(on: eventLoopGroup.next(), configuration: config, id: 1, logger: logger).wait()) {
- XCTAssertTrue($0 is PSQLError)
+ #expect(throws: PSQLError.self) {
+ try PostgresConnection
+ .connect(on: eventLoopGroup.next(), configuration: config, id: 1, logger: logger)
+ .wait()
}
}
- func testOptionsAreSentOnTheWire() async throws {
+ @Test func testOptionsAreSentOnTheWire() async throws {
let eventLoop = NIOAsyncTestingEventLoop()
let channel = try await NIOAsyncTestingChannel(loop: eventLoop) { channel in
try channel.pipeline.syncOperations.addHandlers(ReverseByteToMessageHandler(PSQLFrontendMessageDecoder()))
@@ -71,7 +75,7 @@ class PostgresConnectionTests: XCTestCase {
async let connectionPromise = PostgresConnection.connect(on: eventLoop, configuration: configuration, id: 1, logger: .psqlTest)
let message = try await channel.waitForOutboundWrite(as: PostgresFrontendMessage.self)
- XCTAssertEqual(message, .startup(.versionThree(parameters: .init(user: "username", database: "database", options: configuration.options.additionalStartupParameters, replication: .false))))
+ #expect(message == .startup(.versionThree(parameters: .init(user: "username", database: "database", options: configuration.options.additionalStartupParameters, replication: .false))))
try await channel.writeInbound(PostgresBackendMessage.authentication(.ok))
try await channel.writeInbound(PostgresBackendMessage.backendKeyData(.init(processID: 1234, secretKey: 5678)))
try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
@@ -80,269 +84,275 @@ class PostgresConnectionTests: XCTestCase {
try await connection.close()
}
- func testSimpleListen() async throws {
- let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel()
+ @Test func testSimpleListen() async throws {
+ try await self.withAsyncTestingChannel { connection, channel in
+ try await withThrowingTaskGroup(of: Void.self) { taskGroup in
+ taskGroup.addTask {
+ let events = try await connection.listen("foo")
+ for try await event in events {
+ #expect(event.payload == "wooohooo")
+ break
+ }
+ }
- try await withThrowingTaskGroup(of: Void.self) { taskGroup in
- taskGroup.addTask {
- let events = try await connection.listen("foo")
- for try await event in events {
- XCTAssertEqual(event.payload, "wooohooo")
+ let listenMessage = try await channel.waitForUnpreparedRequest()
+ #expect(listenMessage.parse.query == #"LISTEN "foo";"#)
+
+ try await channel.writeInbound(PostgresBackendMessage.parseComplete)
+ try await channel.writeInbound(PostgresBackendMessage.parameterDescription(.init(dataTypes: [])))
+ try await channel.writeInbound(PostgresBackendMessage.noData)
+ try await channel.writeInbound(PostgresBackendMessage.bindComplete)
+ try await channel.writeInbound(PostgresBackendMessage.commandComplete("LISTEN"))
+ try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
+
+ try await channel.writeInbound(PostgresBackendMessage.notification(.init(backendPID: 12, channel: "foo", payload: "wooohooo")))
+
+ let unlistenMessage = try await channel.waitForUnpreparedRequest()
+ #expect(unlistenMessage.parse.query == #"UNLISTEN "foo";"#)
+
+ try await channel.writeInbound(PostgresBackendMessage.parseComplete)
+ try await channel.writeInbound(PostgresBackendMessage.parameterDescription(.init(dataTypes: [])))
+ try await channel.writeInbound(PostgresBackendMessage.noData)
+ try await channel.writeInbound(PostgresBackendMessage.bindComplete)
+ try await channel.writeInbound(PostgresBackendMessage.commandComplete("UNLISTEN"))
+ try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
+
+ switch await taskGroup.nextResult()! {
+ case .success:
break
+ case .failure(let failure):
+ Issue.record("Unexpected error: \(failure)")
}
}
-
- let listenMessage = try await channel.waitForUnpreparedRequest()
- XCTAssertEqual(listenMessage.parse.query, #"LISTEN "foo";"#)
-
- try await channel.writeInbound(PostgresBackendMessage.parseComplete)
- try await channel.writeInbound(PostgresBackendMessage.parameterDescription(.init(dataTypes: [])))
- try await channel.writeInbound(PostgresBackendMessage.noData)
- try await channel.writeInbound(PostgresBackendMessage.bindComplete)
- try await channel.writeInbound(PostgresBackendMessage.commandComplete("LISTEN"))
- try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
-
- try await channel.writeInbound(PostgresBackendMessage.notification(.init(backendPID: 12, channel: "foo", payload: "wooohooo")))
-
- let unlistenMessage = try await channel.waitForUnpreparedRequest()
- XCTAssertEqual(unlistenMessage.parse.query, #"UNLISTEN "foo";"#)
-
- try await channel.writeInbound(PostgresBackendMessage.parseComplete)
- try await channel.writeInbound(PostgresBackendMessage.parameterDescription(.init(dataTypes: [])))
- try await channel.writeInbound(PostgresBackendMessage.noData)
- try await channel.writeInbound(PostgresBackendMessage.bindComplete)
- try await channel.writeInbound(PostgresBackendMessage.commandComplete("UNLISTEN"))
- try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
-
- switch await taskGroup.nextResult()! {
- case .success:
- break
- case .failure(let failure):
- XCTFail("Unexpected error: \(failure)")
- }
}
}
- func testSimpleListenDoesNotUnlistenIfThereIsAnotherSubscriber() async throws {
- let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel()
+ @Test func testSimpleListenDoesNotUnlistenIfThereIsAnotherSubscriber() async throws {
+ try await self.withAsyncTestingChannel { connection, channel in
- try await withThrowingTaskGroup(of: Void.self) { taskGroup in
- taskGroup.addTask {
- let events = try await connection.listen("foo")
- for try await event in events {
- XCTAssertEqual(event.payload, "wooohooo")
- break
+ try await withThrowingTaskGroup(of: Void.self) { taskGroup in
+ taskGroup.addTask {
+ let events = try await connection.listen("foo")
+ for try await event in events {
+ #expect(event.payload == "wooohooo")
+ break
+ }
}
- }
- taskGroup.addTask {
- let events = try await connection.listen("foo")
- var counter = 0
- loop: for try await event in events {
- defer { counter += 1 }
- switch counter {
- case 0:
- XCTAssertEqual(event.payload, "wooohooo")
- case 1:
- XCTAssertEqual(event.payload, "wooohooo2")
- break loop
- default:
- XCTFail("Unexpected message: \(event)")
+ taskGroup.addTask {
+ let events = try await connection.listen("foo")
+ var counter = 0
+ loop: for try await event in events {
+ defer { counter += 1 }
+ switch counter {
+ case 0:
+ #expect(event.payload == "wooohooo")
+ case 1:
+ #expect(event.payload == "wooohooo2")
+ break loop
+ default:
+ Issue.record("Unexpected message: \(event)")
+ }
}
}
- }
- let listenMessage = try await channel.waitForUnpreparedRequest()
- XCTAssertEqual(listenMessage.parse.query, #"LISTEN "foo";"#)
-
- try await channel.writeInbound(PostgresBackendMessage.parseComplete)
- try await channel.writeInbound(PostgresBackendMessage.parameterDescription(.init(dataTypes: [])))
- try await channel.writeInbound(PostgresBackendMessage.noData)
- try await channel.writeInbound(PostgresBackendMessage.bindComplete)
- try await channel.writeInbound(PostgresBackendMessage.commandComplete("LISTEN"))
- try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
-
- try await channel.writeInbound(PostgresBackendMessage.notification(.init(backendPID: 12, channel: "foo", payload: "wooohooo")))
- try await channel.writeInbound(PostgresBackendMessage.notification(.init(backendPID: 12, channel: "foo", payload: "wooohooo2")))
-
- let unlistenMessage = try await channel.waitForUnpreparedRequest()
- XCTAssertEqual(unlistenMessage.parse.query, #"UNLISTEN "foo";"#)
-
- try await channel.writeInbound(PostgresBackendMessage.parseComplete)
- try await channel.writeInbound(PostgresBackendMessage.parameterDescription(.init(dataTypes: [])))
- try await channel.writeInbound(PostgresBackendMessage.noData)
- try await channel.writeInbound(PostgresBackendMessage.bindComplete)
- try await channel.writeInbound(PostgresBackendMessage.commandComplete("UNLISTEN"))
- try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
-
- switch await taskGroup.nextResult()! {
- case .success:
- break
- case .failure(let failure):
- XCTFail("Unexpected error: \(failure)")
- }
- }
- }
+ let listenMessage = try await channel.waitForUnpreparedRequest()
+ #expect(listenMessage.parse.query == #"LISTEN "foo";"#)
- func testSimpleListenConnectionDrops() async throws {
- let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel()
-
- try await withThrowingTaskGroup(of: Void.self) { [logger] taskGroup in
- taskGroup.addTask {
- let events = try await connection.listen("foo")
- var iterator = events.makeAsyncIterator()
- let first = try await iterator.next()
- XCTAssertEqual(first?.payload, "wooohooo")
- do {
- _ = try await iterator.next()
- XCTFail("Did not expect to not throw")
- } catch {
- logger.error("error", metadata: ["error": "\(error)"])
- }
- }
+ try await channel.writeInbound(PostgresBackendMessage.parseComplete)
+ try await channel.writeInbound(PostgresBackendMessage.parameterDescription(.init(dataTypes: [])))
+ try await channel.writeInbound(PostgresBackendMessage.noData)
+ try await channel.writeInbound(PostgresBackendMessage.bindComplete)
+ try await channel.writeInbound(PostgresBackendMessage.commandComplete("LISTEN"))
+ try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
- let listenMessage = try await channel.waitForUnpreparedRequest()
- XCTAssertEqual(listenMessage.parse.query, #"LISTEN "foo";"#)
-
- try await channel.writeInbound(PostgresBackendMessage.parseComplete)
- try await channel.writeInbound(PostgresBackendMessage.parameterDescription(.init(dataTypes: [])))
- try await channel.writeInbound(PostgresBackendMessage.noData)
- try await channel.writeInbound(PostgresBackendMessage.bindComplete)
- try await channel.writeInbound(PostgresBackendMessage.commandComplete("LISTEN"))
- try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
-
- try await channel.writeInbound(PostgresBackendMessage.notification(.init(backendPID: 12, channel: "foo", payload: "wooohooo")))
- struct MyWeirdError: Error {}
- channel.pipeline.fireErrorCaught(MyWeirdError())
-
- switch await taskGroup.nextResult()! {
- case .success:
- break
- case .failure(let failure):
- XCTFail("Unexpected error: \(failure)")
+ try await channel.writeInbound(PostgresBackendMessage.notification(.init(backendPID: 12, channel: "foo", payload: "wooohooo")))
+ try await channel.writeInbound(PostgresBackendMessage.notification(.init(backendPID: 12, channel: "foo", payload: "wooohooo2")))
+
+ let unlistenMessage = try await channel.waitForUnpreparedRequest()
+ #expect(unlistenMessage.parse.query == #"UNLISTEN "foo";"#)
+
+ try await channel.writeInbound(PostgresBackendMessage.parseComplete)
+ try await channel.writeInbound(PostgresBackendMessage.parameterDescription(.init(dataTypes: [])))
+ try await channel.writeInbound(PostgresBackendMessage.noData)
+ try await channel.writeInbound(PostgresBackendMessage.bindComplete)
+ try await channel.writeInbound(PostgresBackendMessage.commandComplete("UNLISTEN"))
+ try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
+
+ switch await taskGroup.nextResult()! {
+ case .success:
+ break
+ case .failure(let failure):
+ Issue.record("Unexpected error: \(failure)")
+ }
}
}
}
- func testCloseGracefullyClosesWhenInternalQueueIsEmpty() async throws {
- let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel()
- try await withThrowingTaskGroup(of: Void.self) { [logger] taskGroup async throws -> () in
- for _ in 1...2 {
+ @Test func testSimpleListenConnectionDrops() async throws {
+ try await self.withAsyncTestingChannel { connection, channel in
+
+ try await withThrowingTaskGroup(of: Void.self) { [logger] taskGroup in
taskGroup.addTask {
- let rows = try await connection.query("SELECT 1;", logger: logger)
- var iterator = rows.decode(Int.self).makeAsyncIterator()
+ let events = try await connection.listen("foo")
+ var iterator = events.makeAsyncIterator()
let first = try await iterator.next()
- XCTAssertEqual(first, 1)
- let second = try await iterator.next()
- XCTAssertNil(second)
+ #expect(first?.payload == "wooohooo")
+ do {
+ _ = try await iterator.next()
+ Issue.record("Did not expect to not throw")
+ } catch {
+ logger.error("error", metadata: ["error": "\(error)"])
+ }
}
- }
- for i in 0...1 {
let listenMessage = try await channel.waitForUnpreparedRequest()
- XCTAssertEqual(listenMessage.parse.query, "SELECT 1;")
-
- if i == 0 {
- taskGroup.addTask {
- try await connection.closeGracefully()
- }
- }
+ #expect(listenMessage.parse.query == #"LISTEN "foo";"#)
try await channel.writeInbound(PostgresBackendMessage.parseComplete)
try await channel.writeInbound(PostgresBackendMessage.parameterDescription(.init(dataTypes: [])))
- let intDescription = RowDescription.Column(
- name: "",
- tableOID: 0,
- columnAttributeNumber: 0,
- dataType: .int8, dataTypeSize: 8, dataTypeModifier: 0, format: .binary
- )
- try await channel.writeInbound(PostgresBackendMessage.rowDescription(.init(columns: [intDescription])))
- try await channel.testingEventLoop.executeInContext { channel.read() }
+ try await channel.writeInbound(PostgresBackendMessage.noData)
try await channel.writeInbound(PostgresBackendMessage.bindComplete)
- try await channel.testingEventLoop.executeInContext { channel.read() }
- try await channel.writeInbound(PostgresBackendMessage.dataRow([Int(1)]))
- try await channel.testingEventLoop.executeInContext { channel.read() }
- try await channel.writeInbound(PostgresBackendMessage.commandComplete("SELECT 1 1"))
- try await channel.testingEventLoop.executeInContext { channel.read() }
+ try await channel.writeInbound(PostgresBackendMessage.commandComplete("LISTEN"))
try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
- }
- let terminate = try await channel.waitForOutboundWrite(as: PostgresFrontendMessage.self)
- XCTAssertEqual(terminate, .terminate)
- try await channel.closeFuture.get()
- XCTAssertEqual(channel.isActive, false)
+ try await channel.writeInbound(PostgresBackendMessage.notification(.init(backendPID: 12, channel: "foo", payload: "wooohooo")))
+ struct MyWeirdError: Error {}
+ channel.pipeline.fireErrorCaught(MyWeirdError())
- while let taskResult = await taskGroup.nextResult() {
- switch taskResult {
+ switch await taskGroup.nextResult()! {
case .success:
break
case .failure(let failure):
- XCTFail("Unexpected error: \(failure)")
+ Issue.record("Unexpected error: \(failure)")
}
}
}
}
- func testCloseClosesImmediatly() async throws {
- let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel()
+ @Test func testCloseGracefullyClosesWhenInternalQueueIsEmpty() async throws {
+ try await self.withAsyncTestingChannel { connection, channel in
+ try await withThrowingTaskGroup(of: Void.self) { [logger] taskGroup async throws -> () in
+ for _ in 1...2 {
+ taskGroup.addTask {
+ let rows = try await connection.query("SELECT 1;", logger: logger)
+ var iterator = rows.decode(Int.self).makeAsyncIterator()
+ let first = try await iterator.next()
+ #expect(first == 1)
+ let second = try await iterator.next()
+ #expect(second == nil)
+ }
+ }
+
+ for i in 0...1 {
+ let listenMessage = try await channel.waitForUnpreparedRequest()
+ #expect(listenMessage.parse.query == "SELECT 1;")
- try await withThrowingTaskGroup(of: Void.self) { [logger] taskGroup async throws -> () in
- for _ in 1...2 {
- taskGroup.addTask {
- try await connection.query("SELECT 1;", logger: logger)
+ if i == 0 {
+ taskGroup.addTask {
+ try await connection.closeGracefully()
+ }
+ }
+
+ try await channel.writeInbound(PostgresBackendMessage.parseComplete)
+ try await channel.writeInbound(PostgresBackendMessage.parameterDescription(.init(dataTypes: [])))
+ let intDescription = RowDescription.Column(
+ name: "",
+ tableOID: 0,
+ columnAttributeNumber: 0,
+ dataType: .int8, dataTypeSize: 8, dataTypeModifier: 0, format: .binary
+ )
+ try await channel.writeInbound(PostgresBackendMessage.rowDescription(.init(columns: [intDescription])))
+ try await channel.testingEventLoop.executeInContext { channel.read() }
+ try await channel.writeInbound(PostgresBackendMessage.bindComplete)
+ try await channel.testingEventLoop.executeInContext { channel.read() }
+ try await channel.writeInbound(PostgresBackendMessage.dataRow([Int(1)]))
+ try await channel.testingEventLoop.executeInContext { channel.read() }
+ try await channel.writeInbound(PostgresBackendMessage.commandComplete("SELECT 1 1"))
+ try await channel.testingEventLoop.executeInContext { channel.read() }
+ try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
+ }
+
+ let terminate = try await channel.waitForOutboundWrite(as: PostgresFrontendMessage.self)
+ #expect(terminate == .terminate)
+ try await channel.closeFuture.get()
+ #expect(!channel.isActive)
+
+ while let taskResult = await taskGroup.nextResult() {
+ switch taskResult {
+ case .success:
+ break
+ case .failure(let failure):
+ Issue.record("Unexpected error: \(failure)")
+ }
}
}
+ }
+ }
- let listenMessage = try await channel.waitForUnpreparedRequest()
- XCTAssertEqual(listenMessage.parse.query, "SELECT 1;")
+ @Test func testCloseClosesImmediatly() async throws {
+ try await self.withAsyncTestingChannel { connection, channel in
+
+ try await withThrowingTaskGroup(of: Void.self) { [logger] taskGroup async throws -> () in
+ for _ in 1...2 {
+ taskGroup.addTask {
+ try await connection.query("SELECT 1;", logger: logger)
+ }
+ }
+
+ let listenMessage = try await channel.waitForUnpreparedRequest()
+ #expect(listenMessage.parse.query == "SELECT 1;")
- async let close: () = connection.close()
+ async let close: () = connection.close()
- try await channel.closeFuture.get()
- XCTAssertEqual(channel.isActive, false)
+ try await channel.closeFuture.get()
+ #expect(!channel.isActive)
- try await close
+ try await close
- while let taskResult = await taskGroup.nextResult() {
- switch taskResult {
- case .success:
- XCTFail("Expected queries to fail")
- case .failure(let failure):
- guard let error = failure as? PSQLError else {
- return XCTFail("Unexpected error type: \(failure)")
+ while let taskResult = await taskGroup.nextResult() {
+ switch taskResult {
+ case .success:
+ Issue.record("Expected queries to fail")
+ case .failure(let failure):
+ guard let error = failure as? PSQLError else {
+ Issue.record("Unexpected error type: \(failure)")
+ return
+ }
+ #expect(error.code == .clientClosedConnection)
}
- XCTAssertEqual(error.code, .clientClosedConnection)
}
}
}
}
- func testIfServerJustClosesTheErrorReflectsThat() async throws {
- let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel()
- let logger = self.logger
+ @Test func testIfServerJustClosesTheErrorReflectsThat() async throws {
+ try await self.withAsyncTestingChannel { connection, channel in
+ let logger = self.logger
- async let response = try await connection.query("SELECT 1;", logger: logger)
+ async let response = try await connection.query("SELECT 1;", logger: logger)
- let listenMessage = try await channel.waitForUnpreparedRequest()
- XCTAssertEqual(listenMessage.parse.query, "SELECT 1;")
+ let listenMessage = try await channel.waitForUnpreparedRequest()
+ #expect(listenMessage.parse.query == "SELECT 1;")
- try await channel.testingEventLoop.executeInContext { channel.pipeline.fireChannelInactive() }
- try await channel.testingEventLoop.executeInContext { channel.pipeline.fireChannelUnregistered() }
+ try await channel.testingEventLoop.executeInContext { channel.pipeline.fireChannelInactive() }
+ try await channel.testingEventLoop.executeInContext { channel.pipeline.fireChannelUnregistered() }
- do {
- _ = try await response
- XCTFail("Expected to throw")
- } catch {
- XCTAssertEqual((error as? PSQLError)?.code, .serverClosedConnection)
- }
+ do {
+ _ = try await response
+ Issue.record("Expected to throw")
+ } catch {
+ #expect((error as? PSQLError)?.code == .serverClosedConnection)
+ }
- // retry on same connection
+ // retry on same connection
- do {
- _ = try await connection.query("SELECT 1;", logger: self.logger)
- XCTFail("Expected to throw")
- } catch {
- XCTAssertEqual((error as? PSQLError)?.code, .serverClosedConnection)
+ do {
+ _ = try await connection.query("SELECT 1;", logger: self.logger)
+ Issue.record("Expected to throw")
+ } catch {
+ #expect((error as? PSQLError)?.code == .serverClosedConnection)
+ }
}
}
@@ -363,282 +373,287 @@ class PostgresConnectionTests: XCTestCase {
}
}
- func testPreparedStatement() async throws {
- let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel()
-
- try await withThrowingTaskGroup(of: Void.self) { taskGroup async throws -> () in
- taskGroup.addTask {
- let preparedStatement = TestPrepareStatement(state: "active")
- let result = try await connection.execute(preparedStatement, logger: .psqlTest)
- var rows = 0
- for try await database in result {
- rows += 1
- XCTAssertEqual("test_database", database)
+ @Test func testPreparedStatement() async throws {
+ try await self.withAsyncTestingChannel { connection, channel in
+
+ try await withThrowingTaskGroup(of: Void.self) { taskGroup async throws -> () in
+ taskGroup.addTask {
+ let preparedStatement = TestPrepareStatement(state: "active")
+ let result = try await connection.execute(preparedStatement, logger: .psqlTest)
+ var rows = 0
+ for try await database in result {
+ rows += 1
+ #expect("test_database" == database)
+ }
+ #expect(rows == 1)
}
- XCTAssertEqual(rows, 1)
- }
- let prepareRequest = try await channel.waitForPrepareRequest()
- XCTAssertEqual(prepareRequest.parse.query, "SELECT datname FROM pg_stat_activity WHERE state = $1")
- XCTAssertEqual(prepareRequest.parse.parameters.first, .text)
- guard case .preparedStatement(let name) = prepareRequest.describe else {
- fatalError("Describe should contain a prepared statement")
- }
- XCTAssertEqual(name, String(reflecting: TestPrepareStatement.self))
-
- try await channel.sendPrepareResponse(
- parameterDescription: .init(dataTypes: [
- PostgresDataType.text
- ]),
- rowDescription: .init(columns: [
- .init(
- name: "datname",
- tableOID: 12222,
- columnAttributeNumber: 2,
- dataType: .name,
- dataTypeSize: 64,
- dataTypeModifier: -1,
- format: .text
- )
- ])
- )
+ let prepareRequest = try await channel.waitForPrepareRequest()
+ #expect(prepareRequest.parse.query == "SELECT datname FROM pg_stat_activity WHERE state = $1")
+ #expect(prepareRequest.parse.parameters.first == .text)
+ guard case .preparedStatement(let name) = prepareRequest.describe else {
+ fatalError("Describe should contain a prepared statement")
+ }
+ #expect(name == String(reflecting: TestPrepareStatement.self))
+
+ try await channel.sendPrepareResponse(
+ parameterDescription: .init(dataTypes: [
+ PostgresDataType.text
+ ]),
+ rowDescription: .init(columns: [
+ .init(
+ name: "datname",
+ tableOID: 12222,
+ columnAttributeNumber: 2,
+ dataType: .name,
+ dataTypeSize: 64,
+ dataTypeModifier: -1,
+ format: .text
+ )
+ ])
+ )
- let preparedRequest = try await channel.waitForPreparedRequest()
- XCTAssertEqual(preparedRequest.bind.preparedStatementName, String(reflecting: TestPrepareStatement.self))
- XCTAssertEqual(preparedRequest.bind.parameters.count, 1)
- XCTAssertEqual(preparedRequest.bind.resultColumnFormats, [.binary])
+ let preparedRequest = try await channel.waitForPreparedRequest()
+ #expect(preparedRequest.bind.preparedStatementName == String(reflecting: TestPrepareStatement.self))
+ #expect(preparedRequest.bind.parameters.count == 1)
+ #expect(preparedRequest.bind.resultColumnFormats == [.binary])
- try await channel.sendPreparedResponse(
- dataRows: [
- ["test_database"]
- ],
- commandTag: TestPrepareStatement.sql
- )
+ try await channel.sendPreparedResponse(
+ dataRows: [
+ ["test_database"]
+ ],
+ commandTag: TestPrepareStatement.sql
+ )
+ }
}
}
- func testWeDontCrashOnUnexpectedChannelEvents() async throws {
- let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel()
+ @Test func testWeDontCrashOnUnexpectedChannelEvents() async throws {
+ try await self.withAsyncTestingChannel { connection, channel in
- enum MyEvent {
- case pleaseDontCrash
+ enum MyEvent {
+ case pleaseDontCrash
+ }
+ channel.pipeline.fireUserInboundEventTriggered(MyEvent.pleaseDontCrash)
+ try await connection.close()
}
- channel.pipeline.fireUserInboundEventTriggered(MyEvent.pleaseDontCrash)
- try await connection.close()
}
- func testSerialExecutionOfSamePreparedStatement() async throws {
- let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel()
-
- try await withThrowingTaskGroup(of: Void.self) { taskGroup async throws -> () in
- // Send the same prepared statement twice, but with different parameters.
- // Send one first and wait to send the other request until preparation is complete
- taskGroup.addTask {
- let preparedStatement = TestPrepareStatement(state: "active")
- let result = try await connection.execute(preparedStatement, logger: .psqlTest)
- var rows = 0
- for try await database in result {
- rows += 1
- XCTAssertEqual("test_database", database)
+ @Test func testSerialExecutionOfSamePreparedStatement() async throws {
+ try await self.withAsyncTestingChannel { connection, channel in
+
+ try await withThrowingTaskGroup(of: Void.self) { taskGroup async throws -> () in
+ // Send the same prepared statement twice, but with different parameters.
+ // Send one first and wait to send the other request until preparation is complete
+ taskGroup.addTask {
+ let preparedStatement = TestPrepareStatement(state: "active")
+ let result = try await connection.execute(preparedStatement, logger: .psqlTest)
+ var rows = 0
+ for try await database in result {
+ rows += 1
+ #expect("test_database" == database)
+ }
+ #expect(rows == 1)
}
- XCTAssertEqual(rows, 1)
- }
- let prepareRequest = try await channel.waitForPrepareRequest()
- XCTAssertEqual(prepareRequest.parse.query, "SELECT datname FROM pg_stat_activity WHERE state = $1")
- XCTAssertEqual(prepareRequest.parse.parameters.first, .text)
- guard case .preparedStatement(let name) = prepareRequest.describe else {
- fatalError("Describe should contain a prepared statement")
- }
- XCTAssertEqual(name, String(reflecting: TestPrepareStatement.self))
-
- try await channel.sendPrepareResponse(
- parameterDescription: .init(dataTypes: [
- PostgresDataType.text
- ]),
- rowDescription: .init(columns: [
- .init(
- name: "datname",
- tableOID: 12222,
- columnAttributeNumber: 2,
- dataType: .name,
- dataTypeSize: 64,
- dataTypeModifier: -1,
- format: .text
- )
- ])
- )
+ let prepareRequest = try await channel.waitForPrepareRequest()
+ #expect(prepareRequest.parse.query == "SELECT datname FROM pg_stat_activity WHERE state = $1")
+ #expect(prepareRequest.parse.parameters.first == .text)
+ guard case .preparedStatement(let name) = prepareRequest.describe else {
+ fatalError("Describe should contain a prepared statement")
+ }
+ #expect(name == String(reflecting: TestPrepareStatement.self))
+
+ try await channel.sendPrepareResponse(
+ parameterDescription: .init(dataTypes: [
+ PostgresDataType.text
+ ]),
+ rowDescription: .init(columns: [
+ .init(
+ name: "datname",
+ tableOID: 12222,
+ columnAttributeNumber: 2,
+ dataType: .name,
+ dataTypeSize: 64,
+ dataTypeModifier: -1,
+ format: .text
+ )
+ ])
+ )
- let preparedRequest1 = try await channel.waitForPreparedRequest()
- var buffer = preparedRequest1.bind.parameters[0]!
- let parameter1 = buffer.readString(length: buffer.readableBytes)!
- XCTAssertEqual(parameter1, "active")
- try await channel.sendPreparedResponse(
- dataRows: [
- ["test_database"]
- ],
- commandTag: TestPrepareStatement.sql
- )
+ let preparedRequest1 = try await channel.waitForPreparedRequest()
+ var buffer = preparedRequest1.bind.parameters[0]!
+ let parameter1 = buffer.readString(length: buffer.readableBytes)!
+ #expect(parameter1 == "active")
+ try await channel.sendPreparedResponse(
+ dataRows: [
+ ["test_database"]
+ ],
+ commandTag: TestPrepareStatement.sql
+ )
- // Now that the statement has been prepared and executed, send another request that will only get executed
- // without preparation
- taskGroup.addTask {
- let preparedStatement = TestPrepareStatement(state: "idle")
- let result = try await connection.execute(preparedStatement, logger: .psqlTest)
- var rows = 0
- for try await database in result {
- rows += 1
- XCTAssertEqual("test_database", database)
+ // Now that the statement has been prepared and executed, send another request that will only get executed
+ // without preparation
+ taskGroup.addTask {
+ let preparedStatement = TestPrepareStatement(state: "idle")
+ let result = try await connection.execute(preparedStatement, logger: .psqlTest)
+ var rows = 0
+ for try await database in result {
+ rows += 1
+ #expect("test_database" == database)
+ }
+ #expect(rows == 1)
}
- XCTAssertEqual(rows, 1)
- }
- let preparedRequest2 = try await channel.waitForPreparedRequest()
- buffer = preparedRequest2.bind.parameters[0]!
- let parameter2 = buffer.readString(length: buffer.readableBytes)!
- XCTAssertEqual(parameter2, "idle")
- try await channel.sendPreparedResponse(
- dataRows: [
- ["test_database"]
- ],
- commandTag: TestPrepareStatement.sql
- )
- // Ensure we received and responded to both the requests
- let parameters = [parameter1, parameter2]
- XCTAssert(parameters.contains("active"))
- XCTAssert(parameters.contains("idle"))
+ let preparedRequest2 = try await channel.waitForPreparedRequest()
+ buffer = preparedRequest2.bind.parameters[0]!
+ let parameter2 = buffer.readString(length: buffer.readableBytes)!
+ #expect(parameter2 == "idle")
+ try await channel.sendPreparedResponse(
+ dataRows: [
+ ["test_database"]
+ ],
+ commandTag: TestPrepareStatement.sql
+ )
+ // Ensure we received and responded to both the requests
+ let parameters = [parameter1, parameter2]
+ #expect(parameters.contains("active"))
+ #expect(parameters.contains("idle"))
+ }
}
}
- func testStatementPreparationOnlyHappensOnceWithConcurrentRequests() async throws {
- let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel()
-
- try await withThrowingTaskGroup(of: Void.self) { taskGroup async throws -> () in
- // Send the same prepared statement twice, but with different parameters.
- // Let them race to tests that requests and responses aren't mixed up
- taskGroup.addTask {
- let preparedStatement = TestPrepareStatement(state: "active")
- let result = try await connection.execute(preparedStatement, logger: .psqlTest)
- var rows = 0
- for try await database in result {
- rows += 1
- XCTAssertEqual("test_database_active", database)
+ @Test func testStatementPreparationOnlyHappensOnceWithConcurrentRequests() async throws {
+ try await self.withAsyncTestingChannel { connection, channel in
+
+ try await withThrowingTaskGroup(of: Void.self) { taskGroup async throws -> () in
+ // Send the same prepared statement twice, but with different parameters.
+ // Let them race to tests that requests and responses aren't mixed up
+ taskGroup.addTask {
+ let preparedStatement = TestPrepareStatement(state: "active")
+ let result = try await connection.execute(preparedStatement, logger: .psqlTest)
+ var rows = 0
+ for try await database in result {
+ rows += 1
+ #expect("test_database_active" == database)
+ }
+ #expect(rows == 1)
}
- XCTAssertEqual(rows, 1)
- }
- taskGroup.addTask {
- let preparedStatement = TestPrepareStatement(state: "idle")
- let result = try await connection.execute(preparedStatement, logger: .psqlTest)
- var rows = 0
- for try await database in result {
- rows += 1
- XCTAssertEqual("test_database_idle", database)
+ taskGroup.addTask {
+ let preparedStatement = TestPrepareStatement(state: "idle")
+ let result = try await connection.execute(preparedStatement, logger: .psqlTest)
+ var rows = 0
+ for try await database in result {
+ rows += 1
+ #expect("test_database_idle" == database)
+ }
+ #expect(rows == 1)
}
- XCTAssertEqual(rows, 1)
- }
- // The channel deduplicates prepare requests, we're going to see only one of them
- let prepareRequest = try await channel.waitForPrepareRequest()
- XCTAssertEqual(prepareRequest.parse.query, "SELECT datname FROM pg_stat_activity WHERE state = $1")
- XCTAssertEqual(prepareRequest.parse.parameters.first, .text)
- guard case .preparedStatement(let name) = prepareRequest.describe else {
- fatalError("Describe should contain a prepared statement")
- }
- XCTAssertEqual(name, String(reflecting: TestPrepareStatement.self))
-
- try await channel.sendPrepareResponse(
- parameterDescription: .init(dataTypes: [
- PostgresDataType.text
- ]),
- rowDescription: .init(columns: [
- .init(
- name: "datname",
- tableOID: 12222,
- columnAttributeNumber: 2,
- dataType: .name,
- dataTypeSize: 64,
- dataTypeModifier: -1,
- format: .text
- )
- ])
- )
+ // The channel deduplicates prepare requests, we're going to see only one of them
+ let prepareRequest = try await channel.waitForPrepareRequest()
+ #expect(prepareRequest.parse.query == "SELECT datname FROM pg_stat_activity WHERE state = $1")
+ #expect(prepareRequest.parse.parameters.first == .text)
+ guard case .preparedStatement(let name) = prepareRequest.describe else {
+ fatalError("Describe should contain a prepared statement")
+ }
+ #expect(name == String(reflecting: TestPrepareStatement.self))
+
+ try await channel.sendPrepareResponse(
+ parameterDescription: .init(dataTypes: [
+ PostgresDataType.text
+ ]),
+ rowDescription: .init(columns: [
+ .init(
+ name: "datname",
+ tableOID: 12222,
+ columnAttributeNumber: 2,
+ dataType: .name,
+ dataTypeSize: 64,
+ dataTypeModifier: -1,
+ format: .text
+ )
+ ])
+ )
- // Now both the tasks have their statements prepared.
- // We should see both of their execute requests coming in, the order is nondeterministic
- let preparedRequest1 = try await channel.waitForPreparedRequest()
- var buffer = preparedRequest1.bind.parameters[0]!
- let parameter1 = buffer.readString(length: buffer.readableBytes)!
- try await channel.sendPreparedResponse(
- dataRows: [
- ["test_database_\(parameter1)"]
- ],
- commandTag: TestPrepareStatement.sql
- )
- let preparedRequest2 = try await channel.waitForPreparedRequest()
- buffer = preparedRequest2.bind.parameters[0]!
- let parameter2 = buffer.readString(length: buffer.readableBytes)!
- try await channel.sendPreparedResponse(
- dataRows: [
- ["test_database_\(parameter2)"]
- ],
- commandTag: TestPrepareStatement.sql
- )
- // Ensure we received and responded to both the requests
- let parameters = [parameter1, parameter2]
- XCTAssert(parameters.contains("active"))
- XCTAssert(parameters.contains("idle"))
+ // Now both the tasks have their statements prepared.
+ // We should see both of their execute requests coming in, the order is nondeterministic
+ let preparedRequest1 = try await channel.waitForPreparedRequest()
+ var buffer = preparedRequest1.bind.parameters[0]!
+ let parameter1 = buffer.readString(length: buffer.readableBytes)!
+ try await channel.sendPreparedResponse(
+ dataRows: [
+ ["test_database_\(parameter1)"]
+ ],
+ commandTag: TestPrepareStatement.sql
+ )
+ let preparedRequest2 = try await channel.waitForPreparedRequest()
+ buffer = preparedRequest2.bind.parameters[0]!
+ let parameter2 = buffer.readString(length: buffer.readableBytes)!
+ try await channel.sendPreparedResponse(
+ dataRows: [
+ ["test_database_\(parameter2)"]
+ ],
+ commandTag: TestPrepareStatement.sql
+ )
+ // Ensure we received and responded to both the requests
+ let parameters = [parameter1, parameter2]
+ #expect(parameters.contains("active"))
+ #expect(parameters.contains("idle"))
+ }
}
}
- func testStatementPreparationFailure() async throws {
- let (connection, channel) = try await self.makeTestConnectionWithAsyncTestingChannel()
-
- try await withThrowingTaskGroup(of: Void.self) { taskGroup async throws -> () in
- // Send the same prepared statement twice, but with different parameters.
- // Send one first and wait to send the other request until preparation is complete
- taskGroup.addTask {
- let preparedStatement = TestPrepareStatement(state: "active")
- do {
- _ = try await connection.execute(preparedStatement, logger: .psqlTest)
- XCTFail("Was supposed to fail")
- } catch {
- XCTAssert(error is PSQLError)
+ @Test func testStatementPreparationFailure() async throws {
+ try await self.withAsyncTestingChannel { connection, channel in
+
+ try await withThrowingTaskGroup(of: Void.self) { taskGroup async throws -> () in
+ // Send the same prepared statement twice, but with different parameters.
+ // Send one first and wait to send the other request until preparation is complete
+ taskGroup.addTask {
+ let preparedStatement = TestPrepareStatement(state: "active")
+ do {
+ _ = try await connection.execute(preparedStatement, logger: .psqlTest)
+ Issue.record("Was supposed to fail")
+ } catch {
+ #expect(error is PSQLError)
+ }
}
- }
- let prepareRequest = try await channel.waitForPrepareRequest()
- XCTAssertEqual(prepareRequest.parse.query, "SELECT datname FROM pg_stat_activity WHERE state = $1")
- XCTAssertEqual(prepareRequest.parse.parameters.first, .text)
- guard case .preparedStatement(let name) = prepareRequest.describe else {
- fatalError("Describe should contain a prepared statement")
- }
- XCTAssertEqual(name, String(reflecting: TestPrepareStatement.self))
-
- // Respond with an error taking care to return a SQLSTATE that isn't
- // going to get the connection closed.
- try await channel.writeInbound(PostgresBackendMessage.error(.init(fields: [
- .sqlState : "26000" // invalid_sql_statement_name
- ])))
- try await channel.testingEventLoop.executeInContext { channel.read() }
- try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
- try await channel.testingEventLoop.executeInContext { channel.read() }
-
-
- // Send another requests with the same prepared statement, which should fail straight
- // away without any interaction with the server
- taskGroup.addTask {
- let preparedStatement = TestPrepareStatement(state: "idle")
- do {
- _ = try await connection.execute(preparedStatement, logger: .psqlTest)
- XCTFail("Was supposed to fail")
- } catch {
- XCTAssert(error is PSQLError)
+ let prepareRequest = try await channel.waitForPrepareRequest()
+ #expect(prepareRequest.parse.query == "SELECT datname FROM pg_stat_activity WHERE state = $1")
+ #expect(prepareRequest.parse.parameters.first == .text)
+ guard case .preparedStatement(let name) = prepareRequest.describe else {
+ fatalError("Describe should contain a prepared statement")
+ }
+ #expect(name == String(reflecting: TestPrepareStatement.self))
+
+ // Respond with an error taking care to return a SQLSTATE that isn't
+ // going to get the connection closed.
+ try await channel.writeInbound(PostgresBackendMessage.error(.init(fields: [
+ .sqlState : "26000" // invalid_sql_statement_name
+ ])))
+ try await channel.testingEventLoop.executeInContext { channel.read() }
+ try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
+ try await channel.testingEventLoop.executeInContext { channel.read() }
+
+
+ // Send another requests with the same prepared statement, which should fail straight
+ // away without any interaction with the server
+ taskGroup.addTask {
+ let preparedStatement = TestPrepareStatement(state: "idle")
+ do {
+ _ = try await connection.execute(preparedStatement, logger: .psqlTest)
+ Issue.record("Was supposed to fail")
+ } catch {
+ #expect(error is PSQLError)
+ }
}
}
}
}
- func makeTestConnectionWithAsyncTestingChannel() async throws -> (PostgresConnection, NIOAsyncTestingChannel) {
+ func withAsyncTestingChannel(_ body: (PostgresConnection, NIOAsyncTestingChannel) async throws -> ()) async throws {
let eventLoop = NIOAsyncTestingEventLoop()
let channel = try await NIOAsyncTestingChannel(loop: eventLoop) { channel in
try channel.pipeline.syncOperations.addHandlers(ReverseByteToMessageHandler(PSQLFrontendMessageDecoder()))
@@ -656,18 +671,20 @@ class PostgresConnectionTests: XCTestCase {
let logger = self.logger
async let connectionPromise = PostgresConnection.connect(on: eventLoop, configuration: configuration, id: 1, logger: logger)
let message = try await channel.waitForOutboundWrite(as: PostgresFrontendMessage.self)
- XCTAssertEqual(message, .startup(.versionThree(parameters: .init(user: "username", database: "database", options: [], replication: .false))))
+ #expect(message == .startup(.versionThree(parameters: .init(user: "username", database: "database", options: [], replication: .false))))
try await channel.writeInbound(PostgresBackendMessage.authentication(.ok))
try await channel.writeInbound(PostgresBackendMessage.backendKeyData(.init(processID: 1234, secretKey: 5678)))
try await channel.writeInbound(PostgresBackendMessage.readyForQuery(.idle))
let connection = try await connectionPromise
- self.addTeardownBlock {
- try await connection.close()
+ do {
+ try await body(connection, channel)
+ } catch {
+
}
- return (connection, channel)
+ try await connection.close()
}
}
diff --git a/Tests/PostgresNIOTests/New/PostgresRowSequenceTests.swift b/Tests/PostgresNIOTests/New/PostgresRowSequenceTests.swift
index 9d662252..54f13e96 100644
--- a/Tests/PostgresNIOTests/New/PostgresRowSequenceTests.swift
+++ b/Tests/PostgresNIOTests/New/PostgresRowSequenceTests.swift
@@ -1,15 +1,15 @@
import Atomics
import NIOEmbedded
import NIOPosix
-import XCTest
+import Testing
@testable import PostgresNIO
import NIOCore
import Logging
-final class PostgresRowSequenceTests: XCTestCase {
+@Suite struct PostgresRowSequenceTests {
let logger = Logger(label: "PSQLRowStreamTests")
- func testBackpressureWorks() async throws {
+ @Test func testBackpressureWorks() async throws {
let dataSource = MockRowDataSource()
let embeddedEventLoop = EmbeddedEventLoop()
let stream = PSQLRowStream(
@@ -24,22 +24,22 @@ final class PostgresRowSequenceTests: XCTestCase {
)
let rowSequence = stream.asyncSequence()
- XCTAssertEqual(dataSource.requestCount, 0)
+ #expect(dataSource.requestCount == 0)
let dataRow: DataRow = [ByteBuffer(integer: Int64(1))]
stream.receive([dataRow])
var iterator = rowSequence.makeAsyncIterator()
let row = try await iterator.next()
- XCTAssertEqual(dataSource.requestCount, 1)
- XCTAssertEqual(row?.data, dataRow)
+ #expect(dataSource.requestCount == 1)
+ #expect(row?.data == dataRow)
stream.receive(completion: .success("SELECT 1"))
let empty = try await iterator.next()
- XCTAssertNil(empty)
+ #expect(empty == nil)
}
- func testCancellationWorksWhileIterating() async throws {
+ @Test func testCancellationWorksWhileIterating() async throws {
let dataSource = MockRowDataSource()
let embeddedEventLoop = EmbeddedEventLoop()
let stream = PSQLRowStream(
@@ -54,13 +54,13 @@ final class PostgresRowSequenceTests: XCTestCase {
)
let rowSequence = stream.asyncSequence()
- XCTAssertEqual(dataSource.requestCount, 0)
+ #expect(dataSource.requestCount == 0)
let dataRows: [DataRow] = (0..<128).map { [ByteBuffer(integer: Int64($0))] }
stream.receive(dataRows)
var counter = 0
for try await row in rowSequence {
- XCTAssertEqual(try row.decode(Int.self), counter)
+ #expect(try row.decode(Int.self) == counter)
counter += 1
if counter == 64 {
@@ -68,10 +68,10 @@ final class PostgresRowSequenceTests: XCTestCase {
}
}
- XCTAssertEqual(dataSource.cancelCount, 1)
+ #expect(dataSource.cancelCount == 1)
}
- func testCancellationWorksBeforeIterating() async throws {
+ @Test func testCancellationWorksBeforeIterating() async throws {
let dataSource = MockRowDataSource()
let embeddedEventLoop = EmbeddedEventLoop()
let stream = PSQLRowStream(
@@ -86,18 +86,18 @@ final class PostgresRowSequenceTests: XCTestCase {
)
let rowSequence = stream.asyncSequence()
- XCTAssertEqual(dataSource.requestCount, 0)
+ #expect(dataSource.requestCount == 0)
let dataRows: [DataRow] = (0..<128).map { [ByteBuffer(integer: Int64($0))] }
stream.receive(dataRows)
var iterator: PostgresRowSequence.AsyncIterator? = rowSequence.makeAsyncIterator()
iterator = nil
- XCTAssertEqual(dataSource.cancelCount, 1)
- XCTAssertNil(iterator, "Surpress warning")
+ #expect(dataSource.cancelCount == 1)
+ #expect(iterator == nil, "Surpress warning")
}
- func testDroppingTheSequenceCancelsTheSource() async throws {
+ @Test func testDroppingTheSequenceCancelsTheSource() throws {
let dataSource = MockRowDataSource()
let embeddedEventLoop = EmbeddedEventLoop()
let stream = PSQLRowStream(
@@ -114,11 +114,11 @@ final class PostgresRowSequenceTests: XCTestCase {
var rowSequence: PostgresRowSequence? = stream.asyncSequence()
rowSequence = nil
- XCTAssertEqual(dataSource.cancelCount, 1)
- XCTAssertNil(rowSequence, "Surpress warning")
+ #expect(dataSource.cancelCount == 1)
+ #expect(rowSequence == nil, "Surpress warning")
}
- func testStreamBasedOnCompletedQuery() async throws {
+ @Test func testStreamBasedOnCompletedQuery() async throws {
let dataSource = MockRowDataSource()
let embeddedEventLoop = EmbeddedEventLoop()
let stream = PSQLRowStream(
@@ -139,14 +139,14 @@ final class PostgresRowSequenceTests: XCTestCase {
var counter = 0
for try await row in rowSequence {
- XCTAssertEqual(try row.decode(Int.self), counter)
+ #expect(try row.decode(Int.self) == counter)
counter += 1
}
- XCTAssertEqual(dataSource.cancelCount, 0)
+ #expect(dataSource.cancelCount == 0)
}
- func testStreamIfInitializedWithAllData() async throws {
+ @Test func testStreamIfInitializedWithAllData() async throws {
let dataSource = MockRowDataSource()
let embeddedEventLoop = EmbeddedEventLoop()
let stream = PSQLRowStream(
@@ -168,14 +168,14 @@ final class PostgresRowSequenceTests: XCTestCase {
var counter = 0
for try await row in rowSequence {
- XCTAssertEqual(try row.decode(Int.self), counter)
+ #expect(try row.decode(Int.self) == counter)
counter += 1
}
- XCTAssertEqual(dataSource.cancelCount, 0)
+ #expect(dataSource.cancelCount == 0)
}
- func testStreamIfInitializedWithError() async throws {
+ @Test func testStreamIfInitializedWithError() async throws {
let dataSource = MockRowDataSource()
let embeddedEventLoop = EmbeddedEventLoop()
let stream = PSQLRowStream(
@@ -198,13 +198,13 @@ final class PostgresRowSequenceTests: XCTestCase {
for try await _ in rowSequence {
counter += 1
}
- XCTFail("Expected that an error was thrown before.")
+ Issue.record("Expected that an error was thrown before.")
} catch {
- XCTAssertEqual(error as? PSQLError, .serverClosedConnection(underlying: nil))
+ #expect(error as? PSQLError == .serverClosedConnection(underlying: nil))
}
}
- func testSucceedingRowContinuationsWorks() async throws {
+ @Test func testSucceedingRowContinuationsWorks() async throws {
let dataSource = MockRowDataSource()
let eventLoop = NIOSingletons.posixEventLoopGroup.next()
let stream = PSQLRowStream(
@@ -227,17 +227,17 @@ final class PostgresRowSequenceTests: XCTestCase {
}
let row1 = try await rowIterator.next()
- XCTAssertEqual(try row1?.decode(Int.self), 0)
+ #expect(try row1?.decode(Int.self) == 0)
eventLoop.scheduleTask(in: .seconds(1)) {
stream.receive(completion: .success("SELECT 1"))
}
let row2 = try await rowIterator.next()
- XCTAssertNil(row2)
+ #expect(row2 == nil)
}
- func testFailingRowContinuationsWorks() async throws {
+ @Test func testFailingRowContinuationsWorks() async throws {
let dataSource = MockRowDataSource()
let eventLoop = NIOSingletons.posixEventLoopGroup.next()
let stream = PSQLRowStream(
@@ -260,7 +260,7 @@ final class PostgresRowSequenceTests: XCTestCase {
}
let row1 = try await rowIterator.next()
- XCTAssertEqual(try row1?.decode(Int.self), 0)
+ #expect(try row1?.decode(Int.self) == 0)
eventLoop.scheduleTask(in: .seconds(1)) {
stream.receive(completion: .failure(PSQLError.serverClosedConnection(underlying: nil)))
@@ -268,13 +268,13 @@ final class PostgresRowSequenceTests: XCTestCase {
do {
_ = try await rowIterator.next()
- XCTFail("Expected that an error was thrown before.")
+ Issue.record("Expected that an error was thrown before.")
} catch {
- XCTAssertEqual(error as? PSQLError, .serverClosedConnection(underlying: nil))
+ #expect(error as? PSQLError == .serverClosedConnection(underlying: nil))
}
}
- func testAdaptiveRowBufferShrinksAndGrows() async throws {
+ @Test func testAdaptiveRowBufferShrinksAndGrows() async throws {
let dataSource = MockRowDataSource()
let embeddedEventLoop = EmbeddedEventLoop()
let stream = PSQLRowStream(
@@ -294,20 +294,20 @@ final class PostgresRowSequenceTests: XCTestCase {
let rowSequence = stream.asyncSequence()
var rowIterator = rowSequence.makeAsyncIterator()
- XCTAssertEqual(dataSource.requestCount, 0)
+ #expect(dataSource.requestCount == 0)
_ = try await rowIterator.next() // new buffer size will be target -> don't ask for more
- XCTAssertEqual(dataSource.requestCount, 0)
+ #expect(dataSource.requestCount == 0)
_ = try await rowIterator.next() // new buffer will be (target - 1) -> ask for more
- XCTAssertEqual(dataSource.requestCount, 1)
+ #expect(dataSource.requestCount == 1)
// if the buffer gets new rows so that it has equal or more than target (the target size
// should be halved), however shrinking is only allowed AFTER the first extra rows were
// received.
let addDataRows1: [DataRow] = [[ByteBuffer(integer: Int64(0))]]
stream.receive(addDataRows1)
- XCTAssertEqual(dataSource.requestCount, 1)
+ #expect(dataSource.requestCount == 1)
_ = try await rowIterator.next() // new buffer will be (target - 1) -> ask for more
- XCTAssertEqual(dataSource.requestCount, 2)
+ #expect(dataSource.requestCount == 2)
// if the buffer gets new rows so that it has equal or more than target (the target size
// should be halved)
@@ -316,30 +316,30 @@ final class PostgresRowSequenceTests: XCTestCase {
_ = try await rowIterator.next() // new buffer will be (target - 1) -> ask for more
for _ in 0..<(AdaptiveRowBuffer.defaultBufferTarget / 2) {
_ = try await rowIterator.next() // Remove all rows until we are back at target
- XCTAssertEqual(dataSource.requestCount, 2)
+ #expect(dataSource.requestCount == 2)
}
// if we remove another row we should trigger getting new rows.
_ = try await rowIterator.next() // new buffer will be (target - 1) -> ask for more
- XCTAssertEqual(dataSource.requestCount, 3)
+ #expect(dataSource.requestCount == 3)
// remove all remaining rows... this will trigger a target size double
for _ in 0..<(AdaptiveRowBuffer.defaultBufferTarget/2 - 1) {
_ = try await rowIterator.next() // Remove all rows until we are back at target
- XCTAssertEqual(dataSource.requestCount, 3)
+ #expect(dataSource.requestCount == 3)
}
let fillBufferDataRows: [DataRow] = (0.. don't ask for more
- XCTAssertEqual(dataSource.requestCount, 3)
+ #expect(dataSource.requestCount == 3)
_ = try await rowIterator.next() // new buffer will be (target - 1) -> ask for more
- XCTAssertEqual(dataSource.requestCount, 4)
+ #expect(dataSource.requestCount == 4)
}
- func testAdaptiveRowShrinksToMin() async throws {
+ @Test func testAdaptiveRowShrinksToMin() async throws {
let dataSource = MockRowDataSource()
let embeddedEventLoop = EmbeddedEventLoop()
let stream = PSQLRowStream(
@@ -362,9 +362,9 @@ final class PostgresRowSequenceTests: XCTestCase {
var rowIterator = rowSequence.makeAsyncIterator()
// shrinking the buffer is only allowed after the first extra rows were received
- XCTAssertEqual(dataSource.requestCount, 0)
+ #expect(dataSource.requestCount == 0)
_ = try await rowIterator.next()
- XCTAssertEqual(dataSource.requestCount, 1)
+ #expect(dataSource.requestCount == 1)
stream.receive([[ByteBuffer(integer: Int64(1))]])
@@ -373,10 +373,10 @@ final class PostgresRowSequenceTests: XCTestCase {
while currentTarget > AdaptiveRowBuffer.defaultBufferMinimum {
// the buffer is filled up to currentTarget at that point, if we remove one row and add
// one row it should shrink
- XCTAssertEqual(dataSource.requestCount, expectedRequestCount)
+ #expect(dataSource.requestCount == expectedRequestCount)
_ = try await rowIterator.next()
expectedRequestCount += 1
- XCTAssertEqual(dataSource.requestCount, expectedRequestCount)
+ #expect(dataSource.requestCount == expectedRequestCount)
stream.receive([[ByteBuffer(integer: Int64(1))], [ByteBuffer(integer: Int64(1))]])
let newTarget = currentTarget / 2
@@ -385,16 +385,16 @@ final class PostgresRowSequenceTests: XCTestCase {
// consume all messages that are to much.
for _ in 0..