From 666acf86a406c7a264ce3325aa9a80caeb14b20c Mon Sep 17 00:00:00 2001 From: Calvin Cestari Date: Thu, 7 Mar 2024 16:31:50 -0800 Subject: [PATCH 1/2] Expand multipart subscription test suite --- ...ipartResponseSubscriptionParserTests.swift | 212 +++++++++++++++++- .../MultipartResponseSubscriptionParser.swift | 43 ++-- 2 files changed, 226 insertions(+), 29 deletions(-) diff --git a/Tests/ApolloTests/Interceptors/MultipartResponseSubscriptionParserTests.swift b/Tests/ApolloTests/Interceptors/MultipartResponseSubscriptionParserTests.swift index c960bf5f1..bccc18ec5 100644 --- a/Tests/ApolloTests/Interceptors/MultipartResponseSubscriptionParserTests.swift +++ b/Tests/ApolloTests/Interceptors/MultipartResponseSubscriptionParserTests.swift @@ -10,7 +10,7 @@ final class MultipartResponseSubscriptionParserTests: XCTestCase { // MARK: - Error tests - func test__error__givenChunk_withIncorrectContentType_shouldReturnError() throws { + func test__error__givenIncorrectContentType_shouldReturnError() throws { let subject = InterceptorTester(interceptor: MultipartResponseParsingInterceptor()) let expectation = expectation(description: "Received callback") @@ -46,7 +46,7 @@ final class MultipartResponseSubscriptionParserTests: XCTestCase { wait(for: [expectation], timeout: defaultTimeout) } - func test__error__givenChunk_withTransportError_shouldReturnError() throws { + func test__error__givenTransportError_shouldReturnError() throws { let subject = InterceptorTester(interceptor: MultipartResponseParsingInterceptor()) let expectation = expectation(description: "Received callback") @@ -84,7 +84,7 @@ final class MultipartResponseSubscriptionParserTests: XCTestCase { wait(for: [expectation], timeout: defaultTimeout) } - func test__error__givenUnrecognizableChunk_shouldReturnError() throws { + func test__error__givenTransportErrorWithNullPayload_shouldReturnError() throws { let subject = InterceptorTester(interceptor: MultipartResponseParsingInterceptor()) let expectation = expectation(description: "Received callback") @@ -97,7 +97,14 @@ final class MultipartResponseSubscriptionParserTests: XCTestCase { --graphql content-type: application/json - not_a_valid_json_object + { + "payload": null, + "errors" : [ + { + "message" : "forced test failure!" + } + ] + } --graphql """.crlfFormattedData() ) @@ -108,7 +115,51 @@ final class MultipartResponseSubscriptionParserTests: XCTestCase { expect(result).to(beFailure { error in expect(error).to( - matchError(MultipartResponseSubscriptionParser.ParsingError.cannotParseChunkData) + matchError(MultipartResponseSubscriptionParser.ParsingError.irrecoverableError(message: "forced test failure!")) + ) + }) + } + + wait(for: [expectation], timeout: defaultTimeout) + } + + func test__error__givenTransportErrorWithValidPayload_errorShouldTakePrecendenceAndReturnError() throws { + let subject = InterceptorTester(interceptor: MultipartResponseParsingInterceptor()) + + let expectation = expectation(description: "Received callback") + + subject.intercept( + request: .mock(operation: MockSubscription.mock()), + response: .mock( + headerFields: ["Content-Type": "multipart/mixed;boundary=graphql;subscriptionSpec=1.0"], + data: """ + --graphql + content-type: application/json + + { + "payload": { + "data": { + "__typename": "Time", + "ticker": 1 + } + }, + "errors" : [ + { + "message" : "forced test failure!" + } + ] + } + --graphql + """.crlfFormattedData() + ) + ) { result in + defer { + expectation.fulfill() + } + + expect(result).to(beFailure { error in + expect(error).to( + matchError(MultipartResponseSubscriptionParser.ParsingError.irrecoverableError(message: "forced test failure!")) ) }) } @@ -116,7 +167,7 @@ final class MultipartResponseSubscriptionParserTests: XCTestCase { wait(for: [expectation], timeout: defaultTimeout) } - func test__error__givenChunk_withMissingPayload_shouldReturnError() throws { + func test__error__givenTransportErrorIncludingUnknownKeys_shouldReturnErrorWithMessageOnly() throws { let subject = InterceptorTester(interceptor: MultipartResponseParsingInterceptor()) let expectation = expectation(description: "Received callback") @@ -130,7 +181,15 @@ final class MultipartResponseSubscriptionParserTests: XCTestCase { content-type: application/json { - "key": "value" + "errors" : [ + { + "message" : "forced test failure!", + "path": [ + "hello" + ], + "foo": "bar" + } + ] } --graphql """.crlfFormattedData() @@ -142,7 +201,39 @@ final class MultipartResponseSubscriptionParserTests: XCTestCase { expect(result).to(beFailure { error in expect(error).to( - matchError(MultipartResponseSubscriptionParser.ParsingError.cannotParsePayloadData) + matchError(MultipartResponseSubscriptionParser.ParsingError.irrecoverableError(message: "forced test failure!")) + ) + }) + } + + wait(for: [expectation], timeout: defaultTimeout) + } + + func test__error__givenMalformedJSON_shouldReturnError() throws { + let subject = InterceptorTester(interceptor: MultipartResponseParsingInterceptor()) + + let expectation = expectation(description: "Received callback") + + subject.intercept( + request: .mock(operation: MockSubscription.mock()), + response: .mock( + headerFields: ["Content-Type": "multipart/mixed;boundary=graphql;subscriptionSpec=1.0"], + data: """ + --graphql + content-type: application/json + + not_a_valid_json_object + --graphql + """.crlfFormattedData() + ) + ) { result in + defer { + expectation.fulfill() + } + + expect(result).to(beFailure { error in + expect(error).to( + matchError(MultipartResponseSubscriptionParser.ParsingError.cannotParseChunkData) ) }) } @@ -203,7 +294,7 @@ final class MultipartResponseSubscriptionParserTests: XCTestCase { wait(for: [expectation], timeout: defaultTimeout) } - func test__parsing__givenPayloadNull_shouldIgnore() throws { + func test__parsing__givenNullPayload_shouldIgnore() throws { let network = buildNetworkTransport(responseData: """ --graphql content-type: application/json @@ -215,7 +306,29 @@ final class MultipartResponseSubscriptionParserTests: XCTestCase { """.crlfFormattedData() ) - let expectation = expectation(description: "Payload (null) ignored") + let expectation = expectation(description: "Null payload ignored") + expectation.isInverted = true + + _ = network.send(operation: MockSubscription