Skip to content

Commit b709236

Browse files
committed
Merge branch 'more-resptranslator-tests' into 'master'
Add more test coverage of `RESPTranslator` See merge request Mordil/swift-redi-stack!82
2 parents 8c3bdba + b9b7030 commit b709236

File tree

3 files changed

+64
-8
lines changed

3 files changed

+64
-8
lines changed

Sources/RediStack/RESP/RESPTranslator.swift

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,15 @@ extension RESPTranslator {
102102
/// [https://www.gitlab.com/mordil/swift-redis-nio-client/issues](https://www.gitlab.com/mordil/swift-redis-nio-client/issues)
103103
public enum ParsingError: LocalizedError {
104104
case invalidToken
105+
case invalidBulkStringSize
106+
case bulkStringSizeMismatch
105107

106108
/// See `LocalizedError.errorDescription`
107109
public var errorDescription: String? {
108110
switch self {
109111
case .invalidToken: return "Cannot parse RESP: Invalid Token"
112+
case .invalidBulkStringSize: return "Cannot parse RESP Bulk String: Received invalid size."
113+
case .bulkStringSizeMismatch: return "Cannot parse RESP Bulk String: Declared Size and Content Size do not match."
110114
}
111115
}
112116
}
@@ -133,7 +137,7 @@ extension RESPTranslator {
133137
result = .integer(value)
134138

135139
case .dollar:
136-
result = self.parseBulkString(from: &copy)
140+
result = try self.parseBulkString(from: &copy)
137141
break
138142

139143
case .asterisk:
@@ -187,8 +191,19 @@ extension RESPTranslator {
187191
}
188192

189193
/// See [https://redis.io/topics/protocol#resp-bulk-strings](https://redis.io/topics/protocol#resp-bulk-strings)
190-
internal func parseBulkString(from buffer: inout ByteBuffer) -> RESPValue? {
191-
guard let size = self.parseInteger(from: &buffer) else { return nil }
194+
internal func parseBulkString(from buffer: inout ByteBuffer) throws -> RESPValue? {
195+
let startingReaderIndex = buffer.readerIndex
196+
197+
guard let size = self.parseInteger(from: &buffer) else {
198+
// if the reader index changed, that means that a valid string parse happened, but it's not a number.
199+
guard startingReaderIndex == buffer.readerIndex else {
200+
throw ParsingError.invalidBulkStringSize
201+
}
202+
return nil
203+
}
204+
205+
// only -1 is the only valid negative value for a size
206+
guard size >= -1 else { throw ParsingError.invalidBulkStringSize }
192207

193208
// Redis sends '$-1\r\n' to represent a null bulk string
194209
guard size > -1 else { return .null }
@@ -199,8 +214,10 @@ extension RESPTranslator {
199214
let expectedRemainingMessageSize = size + 2
200215
guard buffer.readableBytes >= expectedRemainingMessageSize else { return nil }
201216

202-
// sanity check assert that the position of the newline ending is the remaining size
203-
assert(buffer.getBytes(at: expectedRemainingMessageSize + buffer.readerIndex - 1, length: 1)?.first == .newline)
217+
// sanity check that the declared content size matches the actual size.
218+
guard
219+
buffer.viewBytes(at: buffer.readerIndex + expectedRemainingMessageSize - 1, length: 1)?.first == .newline
220+
else { throw ParsingError.bulkStringSizeMismatch }
204221

205222
// empty content bulk strings are different from null, and represented as .bulkString(nil)
206223
guard size > 0 else {

Tests/RediStackTests/RESPTranslatorTests.swift

Lines changed: 38 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,14 @@ fileprivate extension ByteBuffer {
104104
// MARK: Parse
105105

106106
extension RESPTranslatorTests {
107+
func testParsing_invalidToken() {
108+
var buffer = self.allocator.buffer(capacity: 128)
109+
buffer.writeString("!!!!")
110+
XCTAssertThrowsError(try self.parser.parseBytes(from: &buffer)) { error in
111+
XCTAssertEqual(error as? RESPTranslator.ParsingError, .invalidToken)
112+
}
113+
}
114+
107115
func testParsing_invalidSymbols() {
108116
let testRESP = "&3\r\n"
109117
var buffer = allocator.buffer(capacity: testRESP.count)
@@ -337,6 +345,33 @@ extension RESPTranslatorTests {
337345
// MARK: Bulk Strings
338346

339347
extension RESPTranslatorTests {
348+
func testParsing_bulkString_sizeMismatch() {
349+
var buffer = self.allocator.buffer(capacity: 128)
350+
buffer.writeString("$2\r\ntoo long\r\n")
351+
buffer.mimicTokenParse()
352+
XCTAssertThrowsError(try self.parser.parseBulkString(from: &buffer)) { error in
353+
XCTAssertEqual(error as? RESPTranslator.ParsingError, .bulkStringSizeMismatch)
354+
}
355+
}
356+
357+
func testParsing_bulkString_invalidNegativeSize() {
358+
var buffer = self.allocator.buffer(capacity: 128)
359+
buffer.writeString("$-4\r\nwhat\r\n")
360+
buffer.mimicTokenParse()
361+
XCTAssertThrowsError(try self.parser.parseBulkString(from: &buffer)) { error in
362+
XCTAssertEqual(error as? RESPTranslator.ParsingError, .invalidBulkStringSize)
363+
}
364+
}
365+
366+
func testParsing_bulkString_SizeIsNaN() {
367+
var buffer = self.allocator.buffer(capacity: 128)
368+
buffer.writeString("$FOO\r\nwhat\r\n")
369+
buffer.mimicTokenParse()
370+
XCTAssertThrowsError(try self.parser.parseBulkString(from: &buffer)) { error in
371+
XCTAssertEqual(error as? RESPTranslator.ParsingError, .invalidBulkStringSize)
372+
}
373+
}
374+
340375
func testParsing_bulkString_missingEndings() {
341376
for message in ["$6", "$6\r\n", "$6\r\nabcdef", "$0\r\n"] {
342377
XCTAssertNil(bulkStringParseTest(inputRESP: message))
@@ -374,11 +409,11 @@ extension RESPTranslatorTests {
374409
buffer.writeString(testString)
375410

376411
buffer.mimicTokenParse()
377-
let first = parser.parseBulkString(from: &buffer)
412+
let first = try? parser.parseBulkString(from: &buffer)
378413
XCTAssertEqual(buffer.readerIndex, 6) // position of the 2nd '$'
379414

380415
buffer.mimicTokenParse()
381-
let second = parser.parseBulkString(from: &buffer)
416+
let second = try? parser.parseBulkString(from: &buffer)
382417
XCTAssertEqual(buffer.readerIndex, 17)
383418

384419
XCTAssertEqual(first?.string, "")
@@ -395,7 +430,7 @@ extension RESPTranslatorTests {
395430

396431
buffer.mimicTokenParse()
397432

398-
guard let result = parser.parseBulkString(from: &buffer) else { return nil }
433+
guard let result = try? parser.parseBulkString(from: &buffer) else { return nil }
399434
return result
400435
}
401436
}

Tests/RediStackTests/XCTestManifests.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,10 +14,13 @@ extension RESPTranslatorTests {
1414
("testParsing_array_withNullElements", testParsing_array_withNullElements),
1515
("testParsing_arrays_chunked", testParsing_arrays_chunked),
1616
("testParsing_arrays", testParsing_arrays),
17+
("testParsing_bulkString_invalidNegativeSize", testParsing_bulkString_invalidNegativeSize),
1718
("testParsing_bulkString_missingEndings", testParsing_bulkString_missingEndings),
1819
("testParsing_bulkString_null", testParsing_bulkString_null),
1920
("testParsing_bulkString_rawBytes", testParsing_bulkString_rawBytes),
2021
("testParsing_bulkString_recursively", testParsing_bulkString_recursively),
22+
("testParsing_bulkString_SizeIsNaN", testParsing_bulkString_SizeIsNaN),
23+
("testParsing_bulkString_sizeMismatch", testParsing_bulkString_sizeMismatch),
2124
("testParsing_bulkString_withContent", testParsing_bulkString_withContent),
2225
("testParsing_bulkString_withNoSize", testParsing_bulkString_withNoSize),
2326
("testParsing_bulkStrings_chunked", testParsing_bulkStrings_chunked),
@@ -29,6 +32,7 @@ extension RESPTranslatorTests {
2932
("testParsing_integer_withAllBytes", testParsing_integer_withAllBytes),
3033
("testParsing_integer", testParsing_integer),
3134
("testParsing_invalidSymbols", testParsing_invalidSymbols),
35+
("testParsing_invalidToken", testParsing_invalidToken),
3236
("testParsing_simpleString_chunked", testParsing_simpleString_chunked),
3337
("testParsing_simpleString_handlesRecursion", testParsing_simpleString_handlesRecursion),
3438
("testParsing_simpleString_missingNewline", testParsing_simpleString_missingNewline),

0 commit comments

Comments
 (0)