Files
Nathan Harris 249999851e Rework SortedSet and List range APIs
Motivation:

The SortedSet and List range commands (LTRIM, LRANGE, ZRANGE, etc.) are stringly-based and not flexible with Swift syntax.

Modifications:

- Add overloads of LTRIM that support the gambit of Range Standard Library types
- Rework LRANGE to mirror LTRIM method signatures
- Rework ZScore Range based commands to be more type-safe with `RedisZScoreBound` enum
- Rework ZLex Range based commands to be more type-safe with `RedisZLexBound` enum
- Rework ZCOUNT, ZLEXCOUNT, ZRANGE, ZREVRANGE, ZREMRANGEBYLEX, ZREMRANGEBYRANK, ZREMRANGEBYSCORE methods to be more type-safe and support Swift Range syntax

Result:

Working with SortedSet ranges should be much more type safe, and expressive with Swift's Range syntax.
2019-12-30 17:17:25 -08:00

277 lines
10 KiB
Swift

//===----------------------------------------------------------------------===//
//
// This source file is part of the RediStack open source project
//
// Copyright (c) 2019 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 ListCommandsTests: RediStackIntegrationTestCase {
func test_llen() throws {
var length = try connection.llen(of: #function).wait()
XCTAssertEqual(length, 0)
_ = try connection.lpush([30], into: #function).wait()
length = try connection.llen(of: #function).wait()
XCTAssertEqual(length, 1)
}
func test_lindex() throws {
var element = try connection.lindex(0, from: #function).wait()
XCTAssertTrue(element.isNull)
_ = try connection.lpush([10], into: #function).wait()
element = try connection.lindex(0, from: #function).wait()
XCTAssertFalse(element.isNull)
XCTAssertEqual(Int(fromRESP: element), 10)
}
func test_lset() throws {
XCTAssertThrowsError(try connection.lset(index: 0, to: 30, in: #function).wait())
_ = try connection.lpush([10], into: #function).wait()
XCTAssertNoThrow(try connection.lset(index: 0, to: 30, in: #function).wait())
let element = try connection.lindex(0, from: #function).wait()
XCTAssertEqual(Int(fromRESP: element), 30)
}
func test_lrem() throws {
_ = try connection.lpush([10, 10, 20, 30, 10], into: #function).wait()
var count = try connection.lrem(10, from: #function, count: 2).wait()
XCTAssertEqual(count, 2)
count = try connection.lrem(10, from: #function, count: 2).wait()
XCTAssertEqual(count, 1)
}
func test_lrange() throws {
var elements = try connection.lrange(from: #function, indices: 0...10).wait()
XCTAssertEqual(elements.count, 0)
_ = try connection.lpush([5, 4, 3, 2, 1], into: #function).wait()
elements = try connection.lrange(from: #function, throughIndex: 4).wait()
XCTAssertEqual(elements.count, 5)
XCTAssertEqual(Int(fromRESP: elements[0]), 1)
XCTAssertEqual(Int(fromRESP: elements[4]), 5)
elements = try connection.lrange(from: #function, fromIndex: 1).wait()
XCTAssertEqual(elements.count, 4)
elements = try connection.lrange(from: #function, fromIndex: -3).wait()
XCTAssertEqual(elements.count, 3)
elements = try connection.lrange(from: #function, firstIndex: 2, lastIndex: 0).wait()
XCTAssertEqual(elements.count, 0)
elements = try connection.lrange(from: #function, indices: 4...5).wait()
XCTAssertEqual(elements.count, 1)
elements = try connection.lrange(from: #function, upToIndex: -3).wait()
XCTAssertEqual(elements.count, 2)
}
func test_rpoplpush() throws {
_ = try connection.lpush([10], into: "first").wait()
_ = try connection.lpush([30], into: "second").wait()
var element = try connection.rpoplpush(from: "first", to: "second").wait()
XCTAssertEqual(Int(fromRESP: element), 10)
XCTAssertEqual(try connection.llen(of: "first").wait(), 0)
XCTAssertEqual(try connection.llen(of: "second").wait(), 2)
element = try connection.rpoplpush(from: "second", to: "first").wait()
XCTAssertEqual(Int(fromRESP: element), 30)
XCTAssertEqual(try connection.llen(of: "second").wait(), 1)
}
func test_brpoplpush() throws {
_ = try connection.lpush([10], into: "first").wait()
let element = try connection.brpoplpush(from: "first", to: "second").wait() ?? .null
XCTAssertEqual(Int(fromRESP: element), 10)
let blockingConnection = try self.makeNewConnection()
let expectation = XCTestExpectation(description: "brpoplpush should never return")
_ = blockingConnection.bzpopmin(from: #function)
.always { _ in
expectation.fulfill()
blockingConnection.close()
}
let result = XCTWaiter.wait(for: [expectation], timeout: 1)
XCTAssertEqual(result, .timedOut)
}
func test_linsert() throws {
_ = try connection.lpush([10], into: #function).wait()
_ = try connection.linsert(20, into: #function, after: 10).wait()
var elements = try connection.lrange(from: #function, throughIndex: 1)
.map { response in response.compactMap { Int(fromRESP: $0) } }
.wait()
XCTAssertEqual(elements, [10, 20])
_ = try connection.linsert(30, into: #function, before: 10).wait()
elements = try connection.lrange(from: #function, throughIndex: 2)
.map { response in response.compactMap { Int(fromRESP: $0) } }
.wait()
XCTAssertEqual(elements, [30, 10, 20])
}
func test_lpop() throws {
var element = try connection.lpop(from: #function).wait()
XCTAssertTrue(element.isNull)
_ = try connection.lpush([10, 20, 30], into: #function).wait()
element = try connection.lpop(from: #function).wait()
XCTAssertFalse(element.isNull)
XCTAssertEqual(Int(fromRESP: element), 30)
}
func test_blpop() throws {
let nilPop = try connection.blpop(from: #function, timeout: .seconds(1)).wait()
XCTAssertNil(nilPop)
_ = try connection.lpush([10, 20, 30], into: "first").wait()
let pop1 = try connection.blpop(from: "first").wait() ?? .null
XCTAssertEqual(Int(fromRESP: pop1), 30)
let pop2 = try connection.blpop(from: "fake", "first").wait()
XCTAssertEqual(pop2?.0, "first")
let blockingConnection = try self.makeNewConnection()
let expectation = XCTestExpectation(description: "blpop should never return")
_ = blockingConnection.bzpopmin(from: #function)
.always { _ in
expectation.fulfill()
blockingConnection.close()
}
let result = XCTWaiter.wait(for: [expectation], timeout: 1)
XCTAssertEqual(result, .timedOut)
}
func test_lpush() throws {
_ = try connection.rpush([10, 20, 30], into: #function).wait()
let size = try connection.lpush(100, into: #function).wait()
let element = try connection.lindex(0, from: #function).wait()
XCTAssertEqual(size, 4)
XCTAssertEqual(Int(fromRESP: element), 100)
}
func test_lpushx() throws {
var size = try connection.lpushx(10, into: #function).wait()
XCTAssertEqual(size, 0)
_ = try connection.lpush([10], into: #function).wait()
size = try connection.lpushx(30, into: #function).wait()
XCTAssertEqual(size, 2)
let element = try connection.rpop(from: #function)
.map { return Int(fromRESP: $0) }
.wait()
XCTAssertEqual(element, 10)
}
func test_rpop() throws {
_ = try connection.lpush([10, 20, 30], into: #function).wait()
let element = try connection.rpop(from: #function).wait()
XCTAssertNotNil(element)
XCTAssertEqual(Int(fromRESP: element), 10)
_ = try connection.delete([#function]).wait()
let result = try connection.rpop(from: #function).wait()
XCTAssertTrue(result.isNull)
}
func test_brpop() throws {
let nilPop = try connection.brpop(from: #function, timeout: .seconds(1)).wait()
XCTAssertNil(nilPop)
_ = try connection.lpush([10, 20, 30], into: "first").wait()
let pop1 = try connection.brpop(from: "first").wait() ?? .null
XCTAssertEqual(Int(fromRESP: pop1), 10)
let pop2 = try connection.brpop(from: "fake", "first").wait()
XCTAssertEqual(pop2?.0, "first")
let blockingConnection = try self.makeNewConnection()
let expectation = XCTestExpectation(description: "brpop should never return")
_ = blockingConnection.bzpopmin(from: #function)
.always { _ in
expectation.fulfill()
blockingConnection.close()
}
let result = XCTWaiter.wait(for: [expectation], timeout: 1)
XCTAssertEqual(result, .timedOut)
}
func test_rpush() throws {
_ = try connection.lpush([10, 20, 30], into: #function).wait()
let size = try connection.rpush(100, into: #function).wait()
let element = try connection.lindex(3, from: #function).wait()
XCTAssertEqual(size, 4)
XCTAssertEqual(Int(fromRESP: element), 100)
}
func test_rpushx() throws {
var size = try connection.rpushx(10, into: #function).wait()
XCTAssertEqual(size, 0)
_ = try connection.rpush([10], into: #function).wait()
size = try connection.rpushx(30, into: #function).wait()
XCTAssertEqual(size, 2)
let element = try connection.lpop(from: #function)
.map { return Int(fromRESP: $0) }
.wait()
XCTAssertEqual(element, 10)
}
func test_ltrim() throws {
let setup = {
_ = try self.connection.delete(#function).wait()
_ = try self.connection.lpush([5, 4, 3, 2, 1], into: #function).wait()
}
let getElements = { return try self.connection.lrange(from: #function, fromIndex: 0).wait() }
try setup()
XCTAssertNoThrow(try connection.ltrim(#function, before: 1, after: 3).wait())
XCTAssertNoThrow(try connection.ltrim(#function, keepingIndices: 0...1).wait())
var elements = try getElements()
XCTAssertEqual(elements.count, 2)
try setup()
XCTAssertNoThrow(try connection.ltrim(#function, keepingIndices: (-3)...).wait())
elements = try getElements()
XCTAssertEqual(elements.count, 3)
try setup()
XCTAssertNoThrow(try connection.ltrim(#function, keepingIndices: ...(-4)).wait())
elements = try getElements()
XCTAssertEqual(elements.count, 2)
try setup()
XCTAssertNoThrow(try connection.ltrim(#function, keepingIndices: ..<(-2)).wait())
elements = try getElements()
XCTAssertEqual(elements.count, 3)
}
}