surpher/PactSwift

Rules defined on the mock services aren't all written to the generated pact contract.

jibeex opened this issue · 6 comments

🌎 Environment

  • Xcode: 13.1
  • Platform: MacOS
  • Version/Release: 1.0.0
  • Dependency manager: SPM

💬 Description

Rules defined on the mock services aren't all reflexed in the generated pact contract.

🦶 Reproduction Steps

Steps to reproduce the behavior:

  1. Define the mock service with the following input
        static var mockService = MockService(consumer: "iOS-app",
                                         provider: "backend",
                                         writePactTo: URL(fileURLWithPath: "/xxxx/xxxx/pact_output", isDirectory: true))
        Self.mockService
            .uponReceiving("A request for creating a new session")
            .given(ProviderState(description: "Credentials are valid",
                                 params: [
                                    Constant.keyEmail: Constant.email,
                                    Constant.keyPassword: Constant.password]))
            .withRequest(method: .POST,
                         path: "/v2/users/sessions",
                         headers: ["X-Device-Type": "ios",
                                   "X-Device": Matcher.SomethingLike("xxxxxxxx-xxxx-xxxxx-xxxxx-xxxxxx")],

                         body: [
                            "user": [
                                Constant.keyEmail: Constant.email,
                                Constant.keyPassword: Constant.password,
                            ],
                            "scope": "offline",
                        ])
            .willRespondWith(
                status: 200,
                headers: ["Content-Type": "application/json; charset=utf-8"],
                body: ["websocket_token": Matcher.SomethingLike("xxxxxx"),
                       "refresh_token": Matcher.SomethingLike("xxxxxx"),
                       "expires": Matcher.RegexLike(value: "2022-08-29T14:54:50Z", pattern: #"\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z"#),
                       "user": Matcher.SomethingLike([
                        "id": Matcher.SomethingLike("xxxxxx-xxxx-xxxx-xxxx-xxxxxxx"),
                        "email": Matcher.RegexLike(value: "admin@xxxxx.eu", pattern: #"^([-\.\w]+@[-\w]+\.)+[\w-]{2,4}$"#),
                        "phone_number": Matcher.SomethingLike("+33 2 XX XX XX 73"),
                        "first_name": Matcher.SomethingLike("Dolorès"),
                        "last_name": Matcher.SomethingLike("Koulechov"),
                        "full_name": Matcher.SomethingLike("Dolorès Koulechov"),
                        "gender": Matcher.SomethingLike("male"),
                        "birth_city": Matcher.SomethingLike("Osvaldomouth"),
                        "birth_country": Matcher.SomethingLike("SC"),
                        "birth_zip_code": Matcher.SomethingLike("30332-1282"),
                        "birthdate": Matcher.RegexLike(value: "1972-09-03", pattern: #"\d{4}-\d{2}-\d{2}"#),
                        "locale": Matcher.SomethingLike("en"),
                        "timezone": Matcher.SomethingLike("Europe/Paris"),
                        "avatar": Matcher.SomethingLike([
                            "file_url": "https://some_url"
                        ]),
                        "default_avatar": Matcher.SomethingLike([
                            "file_url": "https://some_url"
                        ]),
                        "intercom_external_id": Matcher.SomethingLike("xxxxxx-5595-4fae-ae21-5f9494e1111e"),
                       ])
                      ])
  1. generate the mocks by running: mockService.run()

🤔 Expected Results

All the define field should have its own matching rules especially the ones with regrex like

Matcher.RegexLike(value: "admin@xxxx.eu", pattern: #"^([-\.\w]+@[-\w]+\.)+[\w-]{2,4}$"#)

😲 Actual Results

The generated matching rules have only rules for a few of the properties.

"matchingRules": {
          "body": {
            "$.expires": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "regex",
                  "regex": "\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}Z"
                }
              ]
            },
            "$.refresh_token": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
            "$.user": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
            "$.websocket_token": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            }
          }
        },

🌳 Logs

NA

📄 Stack Traces

NA

🤝 Relationships

NA

Matcher.SomethingLike() isn't quite the right choice for non-"primitive" and custom Types. SomethingLike does not handle objects. It sets the rule to match the Type but then stops iterating through its structure.
I already have an idea what's going on and when I have some spare time I can have a closer look to improve that, just need to write some unit tests around it.

In the mean time, it looks like your user key-value in your specific case is not an optional, correct?

Instead of:

"user": Matcher.SomethingLike([
  "id": Matcher.SomethingLike("xxxxxx-xxxx-xxxx-xxxx-xxxxxxx"),
  ...
])

Use:

"user: [
  "id": Matcher.SomethingLike("xxxxxx-xxxx-xxxx-xxxx-xxxxxxx"),
  ...
]

Same with defining Avatar objects.

And it should register all the rest of the rules.

And a tip. For UUIDs you can use ExampleGenerator.RandomUUID(format:).

Thanks. It works better now.

Fix applied and is currently only available on main branch. Changes will be included in the next release.

v1.0.1

Great to hear that 👍.

No worries. Hope it serves you well and benefits your dev workflow.

Please raise more issues when you encounter unexpected behaviour. I haven't really used PactSwift myself in a real project in more than a year and so it is harder to uncover unexpected behaviour. And since I wrote it it is also harder to use it in an unexpected way that would surface more bugs.