Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Question: Is it possible to use generators in requests? #104

Open
vkaramov opened this issue Feb 3, 2023 · 4 comments
Open

Question: Is it possible to use generators in requests? #104

vkaramov opened this issue Feb 3, 2023 · 4 comments
Labels
question Further information is requested

Comments

@vkaramov
Copy link

vkaramov commented Feb 3, 2023

❔ Question

I want to use generators in requests like this:

    func testSubscription() throws {
        
        mockService
            .uponReceiving("Dx subscription")
            .given("good state")
            .withRequest(
                method: PactHTTPMethod.POST,
                path: "/api/v1/subscription",
                headers: [
                    "Content-Type": "application/json"
                ],
                body: [
                    "application": ExampleGenerator.RandomString(size: 20),
                    "token": ExampleGenerator.RandomString(size: 20),
                    "applicationVersion": ExampleGenerator.RandomString(size: 20),
                    "deviceName": ExampleGenerator.RandomString(size: 20),
                    "platform": ExampleGenerator.RandomString(size: 20),
                    "platformVersion": ExampleGenerator.RandomString(size: 20),
                    "language": ExampleGenerator.RandomString(size: 20),
                ]
            )
            .willRespondWith(
                status: 200,
                headers: [
                    "Content-Type": "application/json"
                ],
                body: Matcher.EachLike(
                    [
                        "errors": Matcher.MatchNull(),
                        "success": Matcher.SomethingLike(true)
                    ],
                    min: 1
                )
            )
        
        let request = DxSubscriptionRequest(
            application: "com.our.company",
            token: "some_token",
            applicationVersion: "1.0.0",
            deviceName: "iPhone 14 pro",
            platform: "iOS",
            platformVersion: "16.1",
            language: "en"
        )
        
        mockService.run { baseURL, done in
                        
            let config = APIConfiguration(
                baseURL: baseURL,
                channel: "",
                userAgent: "",
                appVersion: request.applicationVersion,
                referer: "",
                xchannel: "MOBILE_NATIVE_IOS",
                token: "",
                accountId: "")
            
            let subscription = DxSubscription.request(r: request, config: config)
            
            // Run POST /api/v1/subscription
            NetworkClient()
                .request(endpoint: subscription) { (response: [DxSubscriptionResponse]?, error) in
                    guard error == nil else {
                        XCTFail("Failed with \(String(describing: error))")
                        done()
                        return
                    }
                    
                    do {
                        let dxResponse = try XCTUnwrap(response)
                        
                        XCTAssertNil(dxResponse.first?.errors)
                        XCTAssertTrue(dxResponse.first?.success ?? false)
                    } catch {
                        XCTFail("Expected DxSubscriptionResponse")
                    }
                    
                    done()
                }
        }
    }

but the test fails with errors like:

Expected 'RGpibFlTHTnV1ywjejkA' to be equal to 'com.our.company' - Body does not match the expected body definition

To fix that I had to replace

                body: [
                    "application": ExampleGenerator.RandomString(size: 20),
                    "token": ExampleGenerator.RandomString(size: 20),
                    "applicationVersion": ExampleGenerator.RandomString(size: 20),
                    "deviceName": ExampleGenerator.RandomString(size: 20),
                    "platform": ExampleGenerator.RandomString(size: 20),
                    "platformVersion": ExampleGenerator.RandomString(size: 20),
                    "language": ExampleGenerator.RandomString(size: 20),
                ]

with

                    "application": Matcher.SomethingLike("string"),
                    "token": Matcher.SomethingLike("string"),
                    "applicationVersion": Matcher.SomethingLike("string"),
                    "deviceName": Matcher.SomethingLike("string"),
                    "platform": Matcher.SomethingLike("string"),
                    "platformVersion": Matcher.SomethingLike("string"),
                    "language": Matcher.SomethingLike("string"),

Is it possible to use generators like in JVM version?

@ExtendWith(PactConsumerTestExt::class)
@PactTestFor(providerName = "dx_provider", pactVersion = PactSpecVersion.V3)
class DxTest {
    @Pact(provider = "dx_provider", consumer = "dx_android_consumer")
    fun httpInteraction(builder: PactDslWithProvider): RequestResponsePact {
        return builder
            .given("good state")
            .uponReceiving("Dx subscription")
            .path("/api/v1/subscription")
            .method("POST")
            .headers(mapOf("Content-Type" to "application/json"))
            .body(
                LambdaDsl.newJsonBody { b ->
                    b.stringType("application")
                    b.stringType("token")
                    b.stringType("applicationVersion")
                    b.stringType("deviceName")
                    b.stringType("platform")
                    b.stringType("platformVersion")
                    b.stringType("language")
                }.build()
            )
            .willRespondWith()
            .status(200)
            .headers(mapOf("Content-Type" to "application/json"))
            .body(
                LambdaDsl.newJsonBody { b ->
                    b.nullValue("errors")
                    b.booleanType("success", true)
                }.build()
            )
            .toPact()
    }

Compare generated pacts:

PactSwift:

      "request": {
        "body": {
          "application": "string",
          "applicationVersion": "string",
          "deviceName": "string",
          "language": "string",
          "platform": "string",
          "platformVersion": "string",
          "token": "string"
        },
        "headers": {
          "Content-Type": "application/json"
        },
        "matchingRules": {
          "body": {
            "$.application": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
...
        },

JVM:

     "request": {
        "body": {
          "application": "string",
          "applicationVersion": "string",
          "deviceName": "string",
          "language": "string",
          "platform": "string",
          "platformVersion": "string",
          "token": "string"
        },
        "generators": {
          "body": {
            "$.application": {
              "size": 20,
              "type": "RandomString"
            },
            "$.applicationVersion": {
              "size": 20,
              "type": "RandomString"
            },
            "$.deviceName": {
              "size": 20,
              "type": "RandomString"
            },
            "$.language": {
              "size": 20,
              "type": "RandomString"
            },
            "$.platform": {
              "size": 20,
              "type": "RandomString"
            },
            "$.platformVersion": {
              "size": 20,
              "type": "RandomString"
            },
            "$.token": {
              "size": 20,
              "type": "RandomString"
            }
          }
        },
        "headers": {
          "Content-Type": "application/json"
        },
        "matchingRules": {
          "body": {
            "$.application": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
...

💬 Context

Xcode 14.2

@vkaramov vkaramov added the question Further information is requested label Feb 3, 2023
@surpher
Copy link
Owner

surpher commented Feb 7, 2023

What version of PactJVM are you using?

Had a look at what the PactSwift's pact structure looks like using your example.
The structure and all seems correct when sending the pact model to pact_ffi.
Must be an issue in pact_ffi verifier not honouring the generators object when verifying the request.

            "request": {
                "path": "/api/species",
                "body": {
                    "deviceName": "WppxFhJaX3TAhzTKZakW",
                    "platform": "pqZtJuwTcArF7uGe6L1d",
                    "platformVersion": "QSb5cMfpGXo8ul3HCpXK",
                    "language": "ZAjcMhbPXh9LDf9SsE1r",
                    "application": "JE4FtUcRH4uBDl0x5wu6",
                    "applicationVersion": "9qrmoscqbYd2cGcUq5FD",
                    "token": "XcunuysQYixQhGYn1Xdd"
                },
                "method": "post",
                "headers": {
                    "Content-Type": "application/json"
                },
                "generators": {
                    "body": {
                        "$.platform": {
                            "type": "RandomString",
                            "size": 20
                        },
                        "$.deviceName": {
                            "type": "RandomString",
                            "size": 20
                        },
                        "$.token": {
                            "type": "RandomString",
                            "size": 20
                        },
                        "$.language": {
                            "type": "RandomString",
                            "size": 20
                        },
                        "$.applicationVersion": {
                            "type": "RandomString",
                            "size": 20
                        },
                        "$.application": {
                            "type": "RandomString",
                            "size": 20
                        },
                        "$.platformVersion": {
                            "type": "RandomString",
                            "size": 20
                        }
                    }
                }
            },
            "response": {
...

@vkaramov
Copy link
Author

vkaramov commented Feb 7, 2023

Thank you, I'm using Pact JVM 4.3.18.

@surpher
Copy link
Owner

surpher commented Feb 7, 2023

I'll ask the team working on pact_ffi about it, but can't necessarily promise anything.
This "might" be fixed with the new PactSwift version that's currently in the works on a fork.

It will take advantage of Apple's concurrency and will slightly change how the DSL works. As a bit of a background v1 creates a pact model and sends it to pact_ffi (the Pact business logic/verifier/mock server written in Rust and shared across language implementations). The new PactSwift v2 will send each of the interactions into pact_ffi via a handler. V2 will essentially shift responsibility into pact_ffi. So not sure if that will help you with your project.

No timelines for when it will be available though!

@vkaramov
Copy link
Author

vkaramov commented Feb 8, 2023

@surpher Great, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

2 participants