Hi all! I’m trying to fix this test that’s throwi...
# pact-js
q
Hi all! I’m trying to fix this test that’s throwing an “Actual map is missing the following keys” error. I’ll post the test in a reply to this thread. the problem I think lies with the
eachLike
function I’m using. It seems to me like
eachLike
doesn’t like to match on interpolated strings…. I could use some help tracking this down!
here’s the test
Copy code
const { pactWith } = require("jest-pact/dist/v3");
const { MatchersV3 } = require("@pact-foundation/pact");
const mockPactServerClient = require("../../utility/mockPactServer");

const { string, eachLike, like } = MatchersV3;

pactWith(
  {
    consumer: "my-consumer",
    provider: "redacted",
    logLevel: LOG_LEVEL,
    port: PACT_PORT,
  },
  (interaction) => {
    const recordTypeId = "Rt65676980b281d2386e2e8754";

    const recordId_connectedProject = "F65676adcb281d2386e2e875b";
    const recordId_name = "F65676980b281d2386e2e8755";
    const recordId_displayName = "F6543dc7094464d273c367a4f";

    interaction("Get redacted records", ({ provider, execute }) => {
      beforeEach(() =>
        provider
          .given("A valid customer")
          .given("A redacted user")
          .uponReceiving(
            "A request for a redacted records for a given record type"
          )
          .withRequest({
            method: "GET",
            path: "/records",
            headers: {
              authorization: string("iloveorange"),
              "x-customer-id": string("1234ASDF"),
            },
            query: {
              recordTypeId,
            },
          })
          .willRespondWith({
            status: 200,
            headers: {
              "Content-Type": "application/json",
            },
            body: eachLike({
              id: string("Rc65676980a90fb861eb1710fc"),
              createdBy: string("61b7b12700124450b9e7abe3fcba1b2b"),
              createdAt: string("2023-11-29T16:40:32.551Z"),
              updatedBy: string("61b7b12700124450b9e7abe3fcba1b2b"),
              updatedAt: string("2023-11-29T16:47:04.449Z"),
              customerId: string("017674f7c6864185b2714799fe634be7"),
              recordTypeId: string("Rt65676980b281d2386e2e8754"),
              data: like({
                [recordId_name]: string("Record 1"),
                [recordId_connectedProject]: eachLike({
                  externalId: string("626079010000a2689c2449406b41d8be"),
                  id: string("Rc65676b08a90fb861eb1710fe"),
                  [recordId_displayName]: string(
                    "Do Not Touch (Contract Testing Project)"
                  ),
                }),
              }),
            }),
          })
      );

      execute("A request for a redacted records for a given record type", () => {
        return mockPactServerClient.get(`/records`, {
          headers: {
            authorization: "iloveorange",
            "x-customer-id": "1234ASDF",
          },
          params: {
            recordTypeId: "Rt65676980b281d2386e2e8754",
          },
        });
      });
    });
  }
);
here’s the provider:
Copy code
const { Verifier } = require("@pact-foundation/pact");
const getPactContracts = require("../../utility/get-pact-contracts");
const getToken = require("../redacted/get-token");

describe("redacted Provider Verification", () => {
  let token;
  let customerId;
  const provider = "redacted";
  const pactContractOptions = getPactContracts(provider);
  const providerBaseUrl = process.env.ENVIRONMENT
    ? "<https://redacted.com/redacted/api/v0.1>"
    : process.env.redacted_BASE_URL;

  const opts = {
    ...pactContractOptions,
    provider,
    providerBaseUrl,
    stateHandlers: {
      "A valid customer": {
        setup: () => {
          customerId = "017674f7c6864185b2714799fe634be7";
        },
      },
      "A user": {
        setup: async () => {
          token = await getToken(["system", "redacted.readonly"]);
        },
      },
    },
    requestFilter: (req, res, next) => {
      if (req.headers["authorization"]) {
        req.headers["authorization"] = `Bearer ${token}`;
      }
      if (req.headers["x-customer-id"]) {
        req.headers["x-customer-id"] = customerId;
      }
      next();
    },
    // logLevel: "debug"
  };

  it("should validate redacted Provider", async () => {
    await new Verifier(opts).verifyProvider();
  });
});
m
Can you please share the error? It’s hard to see what the problem is from this (but thanks for sharing the code)
Ideally, logs from the verifier (that show the expected vs actual body in the response) would help
q
here’s the error: I’ll try to find some relevant logs
Copy code
1) Verifying a pact between provider and redacted Given A valid customer And A redacted user - A request for a redacted records for a given record type
    1.1) has a matching body
           $[0].data.F65676adcb281d2386e2e875b[0] -> Actual map is missing the following keys: F6543dc7094464d273c367a4f
thankyou 1
here’s some logs I found:
Copy code
2024-02-22T23:36:36.778816Z  INFO ThreadId(11) pact_verifier: Running teardown provider state change handler 'A redacted user' for 'A request for a redacted records for a given record type'
2024-02-22T23:36:36.791301Z  INFO ThreadId(11) pact_verifier::provider_client: Received response: HTTP Response ( status: 200, headers: Some({"sltcategory": ["View"], "username": ["<mailto:b061243c-ffb5-4775-ad46-70f3e3b23fde@techacct.redacted.com|b061243c-ffb5-4775-ad46-70f3e3b23fde@techacct.redacted.com>"], "x-redacted-pod": ["devtest-stg-1"], "x-redacted-geo": ["us-west-2"], "x-kong-upstream-status": ["200"], "customername": ["redacted"], "cache-control": ["no-store"], "x-kong-proxy-latency": ["10"], "x-redacted-performancetarget": ["UserAction"], "x-redacted-productgroup": ["Orion"], "x-redacted-traffictype": ["REST_INTERNAL"], "x-xss-protection": ["1; mode=block"], "transfer-encoding": ["chunked"], "content-type": ["application/json; charset=UTF-8"], "userid": ["7f2fbf649514435998a666a35c1cf7d8"], "expires": ["Wed, 22 Feb 2023 23:36:36 GMT"], "appserver": [".7.28"], "date": ["Thu, 22 Feb 2024 23:36:36 GMT"], "last-modified": ["Thu, 22 Feb 2024 23:36:36 GMT"], "x-powered-by": ["Express"], "via": ["kong/2.4.1"], "set-cookie": ["wf-node=9bcc68eb-a713-46de-9ea9-ac1b28971bc5; Path=/; Same-Site=Lax; HttpOnly"], "strict-transport-security": ["max-age=31536000; includeSubDomains"], "vary": ["Accept-Encoding", "Origin"], "userfullname": ["Admin User"], "x-kong-upstream-latency": ["27"], "x-robots-tag": ["noindex"], "category": ["searchStream_DOCCFG"], "requestid": ["65d7da8400000c1c69940db5f0a0d869"], "x-kong-request-id": ["8e77864ef60bbd158736e341cd999111"], "referrer-policy": ["strict-origin-when-cross-origin"], "x-redacted-applicationarea": ["API"], "x-redacted-criticality": ["IsCritical"], "connection": ["close"], "x-redacted-host": [""], "pragma": ["no-cache"], "x-redacted-useractivity": ["Performs an object search"], "x-content-type-options": ["nosniff"], "customerid": ["017674f7c6864185b2714799fe634be7"], "userlicensetype": ["F"]}), body: Present(12983 bytes, application/json;charset=utf-8) )
2024-02-22T23:36:36.791344Z  INFO ThreadId(11) pact_matching: comparing to expected response: HTTP Response ( status: 200, headers: Some({"Content-Type": ["application/json"]}), body: Present(676 bytes) )
m
Thanks. Can you please set it to
DEBUG
, you should see the full request/response body also
So the error looks like it can’t find this key:
F6543dc7094464d273c367a4f
which is at this level of the tree:
Copy code
body: eachLike({
              id: string("Rc65676980a90fb861eb1710fc"),
              ... 
              data: like({
                ...
                [recordId_connectedProject]: eachLike({
                  ....  
                  [recordId_displayName]: ... // <---- can't find this key
                }),
              }),
            }),
q
yeah, but shouldn’t it be able to find that key since it’s in the tree? The
like
matcher is there
here’s some logs I found for the test after enabling debugging mode. the files are the same logs, I just don’t know if your computer can handle .rtf files… stupid macs
it looks to me like the value of the interpolated string at the 2nd level isn’t getting set properly…
"F65676adcb281d2386e2e875b": Array [Object {"externalId": String("626079010000a2689c2449406b41d8be"), "id": String("Rc65676b08a90fb861eb1710fe"), "value": String("Do Not Touch (Contract Testing Project)")}]
the
"value"
part of this should be an interpolated string “F6543dc7094464d273c367a4f” instead of
"value"
according to the test. Is there a fix for this?
Copy code
2024-02-23T16:47:03.528816Z DEBUG ThreadId(02) verify_interaction{interaction="A request for a Maestro records for a given record type"}: pact_matching::json: compare_maps: Comparing maps at $[0].data.F65676adcb281d2386e2e875b[0]: {"F6543dc7094464d273c367a4f": String("Do Not Touch (Contract Testing Project)"), "externalId": String("626079010000a2689c2449406b41d8be"), "id": String("Rc65676b08a90fb861eb1710fe")} -> {"externalId": String("626079010000a2689c2449406b41d8be"), "id": String("Rc65676b08a90fb861eb1710fe"), "value": String("Do Not Touch (Contract Testing Project)")}
yeah, for some reason, it’s trying to compare the actual key to value.
m
hmm you might be right, could you please share the pact file that’s getting generated? Might need to convert this conversation into an issue otherwise
q
Copy code
{
      "description": "A search request for redacted record types",
      "providerStates": [
        {
          "name": "A valid customer"
        },
        {
          "name": "A redacted user"
        }
      ],
      "request": {
        "body": {
          "connection": "redacted",
          "externalIds": [
            "626079010000a2689c2449406b41d8be"
          ],
          "object": "PROJ",
          "recordTypeIds": [
            "Rt65676980b281d2386e2e8754"
          ]
        },
        "headers": {
          "Content-Type": "application/json",
          "authorization": "iloveorange",
          "x-customer-id": "1234ASDF"
        },
        "matchingRules": {
          "body": {},
          "header": {
            "$.authorization[0]": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
            "$['x-customer-id'][0]": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            }
          }
        },
        "method": "POST",
        "path": "/records/external/search"
      },
      "response": {
        "body": [
          {
            "createdAt": "2023-11-29T16:40:32.551Z",
            "createdBy": "61b7b12700124450b9e7abe3fcba1b2b",
            "customerId": "017674f7c6864185b2714799fe634be7",
            "data": {
              "F65676980b281d2386e2e8755": "Record 1",
              "F65676adcb281d2386e2e875b": [
                {
                  "externalId": "626079010000a2689c2449406b41d8be",
                  "id": "Rc65676b08a90fb861eb1710fe",
                  "value": "Do Not Touch (Contract Testing Project)"
                }
              ],
              "F6568f6a2c473b073258398dd": [
                "Lmym2OBv_joCZPaAIIJEz"
              ]
            },
            "id": "Rc65676980a90fb861eb1710fc",
            "recordTypeId": "Rt65676980b281d2386e2e8754",
            "updatedAt": "2023-11-29T16:47:04.449Z",
            "updatedBy": "61b7b12700124450b9e7abe3fcba1b2b"
          }
        ],
        "headers": {
          "Content-Type": "application/json"
        },
        "matchingRules": {
          "body": {
            "$": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type",
                  "min": 1
                }
              ]
            },
            "$[*].createdAt": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
            "$[*].createdBy": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
            "$[*].customerId": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
            "$[*].data": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
            "$[*].data.F65676980b281d2386e2e8755": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
            "$[*].data.F65676adcb281d2386e2e875b": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type",
                  "min": 1
                }
              ]
            },
            "$[*].data.F65676adcb281d2386e2e875b[*].externalId": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
            "$[*].data.F65676adcb281d2386e2e875b[*].id": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
            "$[*].data.F65676adcb281d2386e2e875b[*].value": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
            "$[*].data.F6568f6a2c473b073258398dd": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type",
                  "min": 1
                }
              ]
            },
            "$[*].id": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
            "$[*].recordTypeId": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
            "$[*].updatedAt": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            },
            "$[*].updatedBy": {
              "combine": "AND",
              "matchers": [
                {
                  "match": "type"
                }
              ]
            }
          },
          "header": {}
        },
        "status": 200
      }
    }
  ],
  "metadata": {
    "pact-js": {
      "version": "12.1.0"
    },
    "pactRust": {
      "ffi": "0.4.6",
      "models": "1.1.8"
    },
    "pactSpecification": {
      "version": "3.0.0"
    }
  },
  "provider": {
    "name": "redacted"
  }
m
hmmm as expected, this body looks incorrect:
Copy code
"body": [
          {
            "createdAt": "2023-11-29T16:40:32.551Z",
            "createdBy": "61b7b12700124450b9e7abe3fcba1b2b",
            "customerId": "017674f7c6864185b2714799fe634be7",
            "data": {
              "F65676980b281d2386e2e8755": "Record 1",
              "F65676adcb281d2386e2e875b": [
                {
                  "externalId": "626079010000a2689c2449406b41d8be",
                  "id": "Rc65676b08a90fb861eb1710fe",
                  "value": "Do Not Touch (Contract Testing Project)" # <--------- should be"F6543dc7094464d273c367a4f"
                }
              ],
              "F6568f6a2c473b073258398dd": [
                "Lmym2OBv_joCZPaAIIJEz"
              ]
            },
            "id": "Rc65676980a90fb861eb1710fc",
            "recordTypeId": "Rt65676980b281d2386e2e8754",
            "updatedAt": "2023-11-29T16:47:04.449Z",
            "updatedBy": "61b7b12700124450b9e7abe3fcba1b2b"
          }
        ],
I can’t see why that’s happening though, the other fields look correct!
q
so is this a pact issue?
m
I suspect so, but don't know. I really need a minimal repro (howtorepro)