surpher/PactSwift

Bug: Matcher in request body crashes at `PactSwift.PactBuilder.process(element)`

surpher opened this issue ยท 3 comments

๐ŸŒŽ Environment

  • Xcode: 12
  • Platform: macOS libpact_mock_server_ffi.dylib and iOS using libpact_mock_server.a
  • Version/Release: any
  • Dependency manager: carthage

๐Ÿ’ฌ Description

When writing a pact test with a matcher in a request body, the PactSwift framework throws (and crashes) stating body contains non-encodable element. Crashes when setting up the interaction, specifically .withRequest(body: [...])

๐Ÿฆถ Reproduction Steps

Steps to reproduce the behavior:

  1. Install rust brew install rust (required to compile libpact_mock_server_ffi.dylib at carthage update step)
  2. Clone surpher/pact-swift-examples repo
  3. Change directory into pact-swift-examples/Pact-macOS-Example
  4. Update Cartfile to point to `github "surpher/PactSwift" "tech/macos-dylib"
  5. Run carthage update
  6. Open Pact-macOS-Example project in Xcode
  7. Run unit test: testCreateUser_WithBodyThatMatchesAType()

๐Ÿค” Expected Results

Pact test runs and asserts the expected statusCode is 201

๐Ÿ˜ฒ Actual Results

Pact test runs and crashes with Can not instantiate a Request with non-encodable body.

๐ŸŒณ Logs

2020-10-14 16:38:52.568071+1100 Pact-macOS-Example[34770:14528492] ApplePersistenceIgnoreState: Existing state will not be touched. New state will be written to (null)
2020-10-14 16:38:54.350601+1100 Pact-macOS-Example[34770:14528492] NSDocumentController Info.plist warning: The values of CFBundleTypeRole entries must be 'Editor', 'Viewer', 'None', or 'Shell'.
Test Suite 'Selected tests' started at 2020-10-14 16:38:54.721
Test Suite 'Pact-macOS-ExampleTests.xctest' started at 2020-10-14 16:38:54.722
Test Suite 'Pact_macOS_Example_CarthageTests' started at 2020-10-14 16:38:54.722
Test Case '-[Pact_macOS_ExampleTests.Pact_macOS_Example_CarthageTests testCreateUser_WithBodyThatMatchesAType]' started.
2020-10-14 16:38:54.797824+1100 Pact-macOS-Example[34770:14528985] [Arbitration] starting DTServiceHub child handshake.0 (send: 0x800b, receive: 0xab07)
2020-10-14 16:38:54.798057+1100 Pact-macOS-Example[34770:14528492] [Arbitration] attempting connection to singleton: 34747 with send port: 0x15103
2020-10-14 16:38:54.798301+1100 Pact-macOS-Example[34770:14528492] [Arbitration] handshake SUCCESSFUL (child: 34773 -> singleton: 34747)
2020-10-14 16:38:54.802973+1100 Pact-macOS-Example[34770:14528492] PactSwift: Error casting '{
    name = "PactSwift.Matcher.SomethingLike(value: \"Julia\", rules: [[\"match\": PactSwift.AnyEncodable(_encode: (Function))]])";
}' to a JSON safe Type: String, Int, Double, Decimal, Bool, Dictionary<String, Encodable>, Array<Encodable>):
Fatal error: Can not instantiate a `Request` with non-encodable `body`.: file /Users/marko/Developer/pact-foundation/pact-swift-examples/Pact-macOS-Example/Carthage/Checkouts/PactSwift/Sources/Definitions/Request.swift, line 74
2020-10-14 16:38:54.803340+1100 Pact-macOS-Example[34770:14528492] Fatal error: Can not instantiate a `Request` with non-encodable `body`.: file /Users/marko/Developer/pact-foundation/pact-swift-examples/Pact-macOS-Example/Carthage/Checkouts/PactSwift/Sources/Definitions/Request.swift, line 74
(lldb) 

๐Ÿ“„ Stack Traces

n/a

๐Ÿค Relationships

This is doing my head in. The request body goes through the same processing as response body, yet when setting matchers in a response body, everything is all good and dandy. It's not when setting matchers in request body. Odd.

Narrowed it down to body only containing one dict element or more elements of only one type of matcher.

The following is OK โœ…:

[
  "name": Matcher.SomethingLike("Foo"),
  "age": Matcher.IntegerLike(42) 
]

The following is NOT OK โŒ:

[
  "name": Matcher.SomethingLike("Foo"),
  "age": Matcher.SomethingLike(42) 
]

// or just
[
  "name": Matcher.SomethingLike("Foo")
]

A test case for these was not written at MockServiceTests in PactSwift and PactBuilder is failing to process it properly. Oddly enough, PactBuilderTests do cover testing single Matchers and the tests pass.

It appears Swift doesn't consider any of PactSwift's matchers that conform to MatcherRuleExpressible as a type of MatcherRuleExpressible if there's only one in the dictionary for body in either Request or Response. Same if Request or Response contain multiple matchers (or example generators) of the same type.

This only happens if Response or Request is set post Interaction init (eg: Interaction(...).withRequest(_:)) If Response and Request with same body as described above are passed into the Interaction initializer (eg: Interaction(_:response:request:)) then matchers that conform to MatcherRuleExpressible are considered of type MatcherRuleExpressible and all is hunky dory. ๐Ÿคฆโ€โ™‚๏ธ

Fixed with 7c7c96c