Hello, We are running our consumer contract tests...
# pact-js
a
Hello, We are running our consumer contract tests using @ pact-foundation/pact version 10.1.3. The consumer app is Vue where the UI makes GQL calls to the backend GQL providers. We currently run the tests using Jest framework. We are seeing an issue recently after upgrading the Node engine on our local machines from 16.17.0 to 18.9.0. When run with Node 16.17.0, the consumer tests run successfully and a new contract is generated. However, when run under 18.9.0, the consumer test fails, where the ApolloClient that is being referenced in the consumer test is failing with a "request to http://localhost:8992/graphql failed, reason: connect ECONNREFUSED :18992" error. Here is a simplified snipped of the consumer test:
Copy code
/* eslint-env jest */

import { Pact, GraphQLInteraction, Matchers } from '@pact-foundation/pact';

import * as path from 'path'
import fetch from 'node-fetch'
import { createApolloClient } from '../../src/plugins/apollo-vue-setup'
import BusinessNeedService from '../../src/services/businessNeedService'
import { print } from 'graphql'
import { gql } from '@apollo/client/core'
const pactTestTimeout = 30000

describe('GraphQL example', () => {
  const port = 8992
  const baseUri = `<https://localhost>:${port}`
  const provider = new Pact({
    port: port,
    log: path.resolve(process.cwd(), 'logs', 'mockserver-integration.log'),
    logLevel: 'debug',
    dir: path.resolve(process.cwd(), 'pacts'),
    consumer: 'UI',
    provider: 'BusinessNeedApollo',
  });

  const accessToken = 'authToken'
  const environmentLevel = 'qa'
  const siteName = 'qa026'
  const clientSiteId = 'siteId'
  const language = 'en-US'

  const getAccessTokenAsync = () => accessToken
  const getBeelineHeaderValues = () => ({
    environmentLevel,
    siteName,
    clientSiteId,
    language
  })
  const defaultHeaders = {
    'x-beeline-environment-level': environmentLevel,
    'x-beeline-site-name': siteName,
    'x-beeline-client-site-id': clientSiteId,
    'Content-Type': 'application/json',
    'accept-language': language,
    Authorization: `bearer ${accessToken}`
  }
  const defaultRequest = {
    method: 'POST',
    path: '/graphql',
    headers: defaultHeaders
  }
  const defaultApolloClient = createApolloClient({
    baseUri,
    fetch,
    getAccessTokenAsync,
    getBeelineHeaderValues
  })
  beforeAll(() => provider.setup());
  afterAll(() => provider.finalize());

  describe('query hello on /graphql', () => {
    beforeAll(() => {
      const graphqlQuery = new GraphQLInteraction()
      .uponReceiving('A request to get requests')
      .given('user has business need requests and is authenticated')
      .withQuery(print(gql`query{
        quickRequests(skip: $skip, top: $top, search: $search, orderBy: $orderBy) {
          items {
            clientName
            cost {
              billRate
              estimatedCost
              __typename
            }
            createDate
            industryId
            jobDescription
            jobTitle
            requestId
            requestType
            workLocation
            __typename
          }
          totalCount
          __typename
          }
      }`))
        .withVariables()
        .withOperation()
        .withRequest(defaultRequest)


        .willRespondWith({
          status: 200,
          headers: {
            'Content-Type': 'application/json; charset=utf-8',
          },
          body: Matchers.like({
            data: {
              quickRequests: {
                items: [
                  {
                    clientName: 'QA027',
                    cost: {
                      billRate: '$488.75',
                      estimatedCost: '$320,317.50',
                      __typename: 'Cost'
                    },
                    createDate: '2020-04-21T19:32:31.000Z',
                    industryId: '12345',
                    jobDescription: 'This is a description',
                    jobTitle: 'Accountant (General)',
                    requestType: 'Staff Aug - Approved Budget With Position ID',
                    requestId: 'e234e7aa-54b7-4735-a170-a08ce280c411',
                    workLocation: 'Plantation, FL',
                    __typename: 'QuickRequest'
                  }
                ],
                totalCount: 100,
                __typename: 'QuickRequestPage'
              }
            }
          })
        }
        )
        return provider.addInteraction(graphqlQuery);
    });


    it('returns the correct response', async ()  => {
      const businessNeedService = new BusinessNeedService(defaultApolloClient)
      const requests = await businessNeedService.getQuickRequests();
      expect(requests.items).toHaveLength(1)
    });

    //verify with Pact, and reset expectations
    afterEach(() => provider.verify());
  });
});
I can't post the two output messages in the entire threads, so I saved each output to a text file and attached them to the thread. The major difference in the logs is the lack of reference to the tokio-runtime-worker I am seeing when the tests fails. I have spent the entire day attempting to debug this issue with no luck being able to generate the tests. I also attempted to run Node 18.9.0 with different versions of the @ pact-foundation/pact (8.2.6 and 9.17.2) with no luck! Any advice is highly appreciated!
t
I don't think this is the problem, but the part where you add the interaction would be better written in a
beforeEach
. I remember in some version of jest `beforeAll`s wouldn't run in the expected order.
At what point does the apollo client try to connect? If it connects during the
createApolloClient
call, then it will be before the provider has setup.
It could be that something changed between node versions that allowed that race condition to succeed/fail
try:
Copy code
let defaultApolloClient;

beforeAll(async () => { 
  await provider.setup() 
  defaultApolloClient = createApolloClient({
    baseUri,
    fetch,
    getAccessTokenAsync,
    getBeelineHeaderValues
  });
});
m
I seemed to recall something about default network adapters prioritising ipv6 between node 16 and 18 (because ipv4 is dead right and now ipv6 rules the world………..)
failed, reason: connect ECONNREFUSED :18992
That is an ipv6 address, but it looks like the server is starting on
127.0.0.1
(the default).. It looks like you might be calling
localhost
but if this resolves to an ipv6 adddress, it won’t work because the mock server is on
127.0.0.1
- could you please try using
127.0.0.1
in your code (there are alternatives, such as modifying your host entries, but that is obviously not ideal)
a
You are 100% correct. Shortly after I posted this, I came across this ticket on Node's Github page: https://github.com/nodejs/node/issues/40702 and after switching the baseUri to 127.0.0.1, the tests worked! Thank you so much for the help!
🎉 1
🙌 1
t
Good eye, Matt.