Hello everyone, how are you? I have generated a co...
# pact-js
c
Hello everyone, how are you? I have generated a contract using pact-js in my consumer for a GraphQL API, but I’m getting an error when I try to verify this contract with pact-very or with pact-python in my provider, I believe this is an error with my contract the error I’m getting when I try to verify this contract is
GraphQL operations must contain a non-empty query or a persistedQuery
anyone knows how to solve this? I’m sharing my contract bellow and the code I’m using to generate the contract
follow my contract bellow:
Copy code
{
  "consumer": {
    "name": "courier-phoenix"
  },
  "provider": {
    "name": "courier-api"
  },
  "interactions": [
    {
      "description": "A request with a new show case",
      "request": {
        "method": "POST",
        "path": "/public-api",
        "headers": {
          "content-type": "application/json"
        },
        "body": {
          "operationName": "loadNewShowCase",
          "query": "query loadNewShowCase($filter: ShowCaseFilter) {\n  loadNewShowCase(filter: $filter) {\n    messages {\n      category\n      target\n      key\n      message\n    }\n    showCase {\n      images\n      shelves {\n        id\n        type\n        displayName\n        hasNextItems\n        items {\n          id\n          type\n          images\n          displayName\n          applicableDiscount {\n            presentedDiscountValue\n            discountType\n            finalValue\n          }\n          category {\n            id\n            displayName\n          }\n          brand {\n            id\n            displayName\n          }\n          price {\n            min\n            max\n          }\n        }\n      }\n    }\n  }\n}\n",
          "variables": {}
        },
        "matchingRules": {
          "$.body.query": {
            "match": "regex",
            "regex": "query\\s*loadNewShowCase\\(\\$filter:\\s*ShowCaseFilter\\)\\s*\\{\\s*loadNewShowCase\\(filter:\\s*\\$filter\\)\\s*\\{\\s*messages\\s*\\{\\s*category\\s*target\\s*key\\s*message\\s*\\}\\s*showCase\\s*\\{\\s*images\\s*shelves\\s*\\{\\s*id\\s*type\\s*displayName\\s*hasNextItems\\s*items\\s*\\{\\s*id\\s*type\\s*images\\s*displayName\\s*applicableDiscount\\s*\\{\\s*presentedDiscountValue\\s*discountType\\s*finalValue\\s*\\}\\s*category\\s*\\{\\s*id\\s*displayName\\s*\\}\\s*brand\\s*\\{\\s*id\\s*displayName\\s*\\}\\s*price\\s*\\{\\s*min\\s*max\\s*\\}\\s*\\}\\s*\\}\\s*\\}\\s*\\}\\s*\\}\\s*"
          }
        }
      },
      "response": {
        "status": 200,
        "headers": {
          "Content-Type": "application/json; charset=utf-8"
        },
        "body": {
          "data": {
            "loadNewShowCase": {
              "messages": null,
              "showCase": {
                "images": [],
                "shelves": [
                  {
                    "id": "31239bf8-e8d8-4893-a3de-7e5316a8e6fd",
                    "type": "BILLBOARD",
                    "displayName": "Destaques",
                    "hasNextItems": false,
                    "items": [
                      {
                        "id": "b7566f41-db87-4b03-bef2-4531a5b6de50",
                        "type": "BANNER",
                        "images": [
                          "<https://courier-images-frontrelease.imgix.net/banner/b7566f41-db87-4b03-bef2-4531a5b6de50_180cab5b-ae61-4b28-9b6e-2124815cf974.jpg>"
                        ],
                        "displayName": "TESTEQA10",
                        "applicableDiscount": null,
                        "category": null,
                        "brand": null,
                        "price": null
                      }
                    ]
                  }
                ]
              }
            }
          },
          "default": {
            "data": {
              "loadNewShowCase": {
                "messages": null,
                "showCase": {
                  "images": [],
                  "shelves": [
                    {
                      "id": "31239bf8-e8d8-4893-a3de-7e5316a8e6fd",
                      "type": "BILLBOARD",
                      "displayName": "Destaques",
                      "hasNextItems": false,
                      "items": [
                        {
                          "id": "b7566f41-db87-4b03-bef2-4531a5b6de50",
                          "type": "BANNER",
                          "images": [
                            "<https://courier-images-frontrelease.imgix.net/banner/b7566f41-db87-4b03-bef2-4531a5b6de50_180cab5b-ae61-4b28-9b6e-2124815cf974.jpg>"
                          ],
                          "displayName": "TESTEQA10",
                          "applicableDiscount": null,
                          "category": null,
                          "brand": null,
                          "price": null
                        }
                      ]
                    }
                  ]
                }
              }
            }
          }
        },
        "matchingRules": {
          "$.body": {
            "match": "type"
          }
        }
      }
    }
  ],
  "metadata": {
    "pactSpecification": {
      "version": "2.0.0"
    }
  }
}
And the code to generate the contract is:
Copy code
import { ApolloGraphQLInteraction, Matchers, Pact } from '@pact-foundation/pact'
import { ApolloClient, gql, HttpLink, InMemoryCache } from '@apollo/client'
import * as loadNewShowCaseExpectedResponse from './mocks/loadNewShowCase.json';
import { showCase } from '../../../src/services/api/showcase';
import { print } from 'graphql'
import fetch from 'cross-fetch'
import path from 'path';

jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;

const PORT = 20002;

describe("Show Case", () => {

    const provider = new Pact({
      port: PORT,
      log: path.resolve(process.cwd(), "aut-tests/contract/logs", "mockserver-integration.log"),
      dir: path.resolve(process.cwd(), "aut-tests/contract/pacts"),
      spec: 2,
      logLevel: 'DEBUG',
      pactfileWriteMode: "overwrite",
      consumer: "courier-phoenix",
      provider: "courier-api",
    })
    
    beforeAll(() => provider.setup());
    afterAll(() => provider.finalize());

    describe("Load New Show Case", () => {
        beforeEach(() => {
          const graphqlQueryInteraction = new ApolloGraphQLInteraction()
            .uponReceiving("A request with a new show case")
            .withRequest({
              path: "/public-api",
              method: "POST",
            })
            .withOperation("loadNewShowCase")
            .withQuery(print(gql`query loadNewShowCase($filter: ShowCaseFilter) {${showCase}}`))
            .withVariables({})
            .willRespondWith({
              status: 200,
              headers: {
                "Content-Type": "application/json; charset=utf-8",
              },
              body: Matchers.like(loadNewShowCaseExpectedResponse)
            });

          return provider.addInteraction(graphqlQueryInteraction)
      })

      test("returns correct body, header and statusCode", async() => {
        const client = new ApolloClient({
          cache: new InMemoryCache({
            addTypename: false
          }),
          link: new HttpLink({
            uri: `${provider.mockService.baseUrl}/public-api`,
            fetch
          }),
        })

        let response = client
        .query({
          query: gql`query loadNewShowCase($filter: ShowCaseFilter) {
            ${showCase}
          }`
        })
       
        response = await response.then((result: any) => {
          result.data
          expect(result.data).toEqual(loadNewShowCaseExpectedResponse)
        })
          .catch( error => {
            return error
          })
     })

     afterEach(() => provider.verify())
    })
})
u
hi @Caíque Coelho for your graphql api provider, is the actual query string being sent when you try to verify the pact? I wrote a consumer test and I’m trying to setup a provider for graphql queries but when investigating the response to my local provider endpoint, It seems pact doesn’t actually send the graphql query string? not sure if it’s me or that’s intended behaviour
At the end of the day it’s just a post request with data but the data doesn’t seem to contain the actual query string from the pact file
an example interaction in the pact file:
Copy code
"interactions": [
    {
      "description": "blah blah",
      "providerState": "blah blah",
      "request": {
        "method": "post",
        "path": "/v1/graphql/",
        "headers": {
          "Content-Type": "application/json",
          "Authorization": "eyJ..."
        },
        "body": {
          "query": "some query" <--- this isn't being sent to the provider when attempting to verify with provider
        }
      },
did you ever encounter this issue? @Caíque Coelho
update: I realised it sends a second request to the real provider endpoint with the body in that data there in addition I’ve opted to have my pact tests completely ignore my authorization middleware those are handled by other integration tests