mirror of
https://github.com/swift-server/RediStack.git
synced 2026-06-02 07:37:33 +00:00
Motivation: When `RedisConnection.allowSubscriptions` is set to `false`, the connection could still be in a subscription state leaving further commands to fail slowly from a full roundtrip to Redis, rather than succeeding as expected. This changes the implementation so that it triggers a full unsubscribe from patterns and channels when set to `false`. Modifications: - Change: `RedisConnection.allowSubscriptions` to call `unsubscribe()` and `punsubscribe()` when set to `false` - Change: `RedisPubSubHandler` to prefix storage of all dictionary keys to avoid name clashes between pattern and channel subscriptions Result: Developers should now have more deterministic and unsurprising behavior with PubSub in regards to subscription management and connection state.
84 lines
2.8 KiB
Swift
84 lines
2.8 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the RediStack open source project
|
|
//
|
|
// Copyright (c) 2019-2020 RediStack project authors
|
|
// Licensed under Apache License v2.0
|
|
//
|
|
// See LICENSE.txt for license information
|
|
// See CONTRIBUTORS.txt for the list of RediStack project authors
|
|
//
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
@testable import RediStack
|
|
import RediStackTestUtils
|
|
import XCTest
|
|
|
|
final class RedisConnectionTests: RediStackIntegrationTestCase {
|
|
func test_unexpectedChannelClose() throws {
|
|
XCTAssertTrue(self.connection.isConnected)
|
|
try self.connection.channel.close().wait()
|
|
XCTAssertFalse(self.connection.isConnected)
|
|
}
|
|
|
|
func test_callingCloseMultipleTimes() throws {
|
|
let first = self.connection.close()
|
|
let second = self.connection.close()
|
|
XCTAssertNotEqual(first, self.connection.channel.closeFuture)
|
|
XCTAssertEqual(second, self.connection.channel.closeFuture)
|
|
}
|
|
|
|
func test_sendingCommandAfterClosing() throws {
|
|
self.connection.close()
|
|
do {
|
|
_ = try self.connection.ping().wait()
|
|
XCTFail("ping() should throw when connection is closed.")
|
|
} catch {
|
|
XCTAssertTrue(error is RedisClientError)
|
|
}
|
|
}
|
|
}
|
|
|
|
// MARK: PubSub permissions
|
|
|
|
extension RedisConnectionTests {
|
|
func test_subscriptionNotAllowedFails() throws {
|
|
self.connection.allowSubscriptions = false
|
|
let subscription = self.connection.subscribe(to: #function) { (_, _) in }
|
|
|
|
XCTAssertThrowsError(try subscription.wait()) {
|
|
guard let error = $0 as? RedisClientError else {
|
|
XCTFail("unexpected error type: \(type(of: $0))")
|
|
return
|
|
}
|
|
XCTAssertEqual(error, .pubsubNotAllowed)
|
|
}
|
|
}
|
|
|
|
func test_subscriptionPermissionsChanged_endsSubscriptions() throws {
|
|
let connection = try self.makeNewConnection()
|
|
|
|
let subscriptionClosedExpectation = self.expectation(description: "subscription was closed")
|
|
subscriptionClosedExpectation.expectedFulfillmentCount = 2
|
|
|
|
_ = try connection.subscribe(
|
|
to: #function,
|
|
messageReceiver: { _, _ in },
|
|
onSubscribe: nil,
|
|
onUnsubscribe: { _, _ in subscriptionClosedExpectation.fulfill() }
|
|
).wait()
|
|
_ = try connection.psubscribe(
|
|
to: #function,
|
|
messageReceiver: { _, _ in },
|
|
onSubscribe: nil,
|
|
onUnsubscribe: { _, _ in subscriptionClosedExpectation.fulfill() }
|
|
).wait()
|
|
|
|
connection.allowSubscriptions = false
|
|
|
|
self.waitForExpectations(timeout: 1)
|
|
}
|
|
}
|