pact-foundation / pact-reference

Reference implementations for the pact specifications
https://pact.io
MIT License
93 stars 46 forks source link

Using ProviderStateGenerator should not allow mock server to match any request #447

Closed mefellows closed 5 months ago

mefellows commented 5 months ago

Via: https://github.com/pact-foundation/pact-js/issues/1088

Summary

When I add interaction like this:

pact.addInteraction({
        states: [{ description: 'A user' }],
        uponReceiving: 'get one user',
        withRequest: {
            method: 'GET',
            path: fromProviderState('${iri}', '/api/users/14f6626f-c51e-4311-ac52-182c8f2a7634')
        },
        willRespondWith: {
            status: 200,
            body: {
                id: '14f6626f-c51e-4311-ac52-182c8f2a7634'
            },
        },
    });

I expect mock server should return 404 for this request GET /any, but it return 200 with body {"id": "14f6626f-c51e-4311-ac52-182c8f2a7634"}

Expected behaviour

The request GET /any should return 404, and mock server should report the request GET /api/users/14f6626f-c51e-4311-ac52-182c8f2a7634 is expected but not received.

Actual behaviour

The request GET /any return 200 with json body that didn't belong to it.

Steps to reproduce

Here is the demo project https://github.com/tienvx/test-from-provider-state-generator

Relevant log files

[21:12:21.618] INFO (27131): 0.4.7: pact native library successfully found, and the correct version
2023-08-11T14:12:21.624175Z DEBUG ThreadId(01) pact_ffi::mock_server::handles: detected pact:matcher:type, will configure a matcher
2023-08-11T14:12:21.624206Z DEBUG ThreadId(01) pact_ffi::mock_server::handles: detected pact:generator:type, will configure a generators
2023-08-11T14:12:21.624683Z DEBUG ThreadId(01) pact_plugin_driver::catalogue_manager: Updated catalogue entries:
core/transport/http
core/transport/https
2023-08-11T14:12:21.624703Z DEBUG ThreadId(01) pact_plugin_driver::catalogue_manager: Updated catalogue entries:
core/content-generator/binary
core/content-generator/json
core/content-matcher/json
core/content-matcher/multipart-form-data
core/content-matcher/text
core/content-matcher/xml
2023-08-11T14:12:21.624722Z DEBUG ThreadId(01) pact_plugin_driver::catalogue_manager: Updated catalogue entries:
core/matcher/v1-equality
core/matcher/v2-max-type
core/matcher/v2-min-type
core/matcher/v2-minmax-type
core/matcher/v2-regex
core/matcher/v2-type
core/matcher/v3-content-type
core/matcher/v3-date
core/matcher/v3-datetime
core/matcher/v3-decimal-type
core/matcher/v3-includes
core/matcher/v3-integer-type
core/matcher/v3-null
core/matcher/v3-number-type
core/matcher/v3-time
core/matcher/v4-array-contains
core/matcher/v4-equals-ignore-order
core/matcher/v4-max-equals-ignore-order
core/matcher/v4-min-equals-ignore-order
core/matcher/v4-minmax-equals-ignore-order
core/matcher/v4-not-empty
core/matcher/v4-semver
2023-08-11T14:12:21.624852Z DEBUG ThreadId(01) pact_mock_server::mock_server: Started mock server on 127.0.0.1:36287
*
2023-08-11T14:12:21.641024Z DEBUG tokio-runtime-worker hyper::proto::h1::io: parsed 7 headers
2023-08-11T14:12:21.641032Z DEBUG tokio-runtime-worker hyper::proto::h1::conn: incoming body is empty
2023-08-11T14:12:21.641047Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: Creating pact request from hyper request
2023-08-11T14:12:21.641051Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: Extracting query from uri /any
2023-08-11T14:12:21.641067Z  INFO tokio-runtime-worker pact_mock_server::hyper_server: Received request GET /any
2023-08-11T14:12:21.641071Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: 
      ----------------------------------------------------------------------------------------
       method: GET
       path: /any
       query: None
       headers: Some({"host": ["127.0.0.1:36287"], "accept": ["*/*"], "accept-language": ["*"], "sec-fetch-mode": ["cors"], "accept-encoding": ["gzip", "deflate"], "connection": ["keep-alive"], "user-agent": ["undici"]})
       body: Empty
      ----------------------------------------------------------------------------------------

2023-08-11T14:12:21.641104Z  INFO tokio-runtime-worker pact_matching: comparing to expected HTTP Request ( method: GET, path: /api/users/14f6626f-c51e-4311-ac52-182c8f2a7634, query: None, headers: None, body: Missing )
2023-08-11T14:12:21.641108Z DEBUG tokio-runtime-worker pact_matching:      body: ''
2023-08-11T14:12:21.641109Z DEBUG tokio-runtime-worker pact_matching:      matching_rules: MatchingRules { rules: {PATH: MatchingRuleCategory { name: PATH, rules: {DocPath { path_tokens: [Root], expr: "$" }: RuleList { rules: [Type], rule_logic: And, cascaded: false }} }} }
2023-08-11T14:12:21.641116Z DEBUG tokio-runtime-worker pact_matching:      generators: Generators { categories: {PATH: {DocPath { path_tokens: [Root], expr: "$" }: ProviderStateGenerator("${iri}", None)}} }
2023-08-11T14:12:21.641131Z DEBUG tokio-runtime-worker pact_matching::matchers: String -> String: comparing '/api/users/14f6626f-c51e-4311-ac52-182c8f2a7634' to '/any' ==> true cascaded=false matcher=Type
2023-08-11T14:12:21.641139Z DEBUG tokio-runtime-worker pact_matching: expected content type = '*/*', actual content type = '*/*'
2023-08-11T14:12:21.641173Z DEBUG tokio-runtime-worker pact_matching: content type header matcher = 'RuleList { rules: [], rule_logic: And, cascaded: false }'
2023-08-11T14:12:21.641178Z DEBUG tokio-runtime-worker pact_matching: --> Mismatches: []
2023-08-11T14:12:21.641197Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: Test context = {"mockServer": Object {"port": Number(36287), "url": String("http://127.0.0.1:36287")}}
2023-08-11T14:12:21.641204Z  INFO tokio-runtime-worker pact_mock_server::hyper_server: Request matched, sending response
2023-08-11T14:12:21.641206Z DEBUG tokio-runtime-worker pact_mock_server::hyper_server: 
          ----------------------------------------------------------------------------------------
           status: 200
           headers: Some({"Content-Type": ["application/json"]})
           body: Present(45 bytes, application/json) '{"id":"14f6626f-c51e-4311-ac52-182c8f2a7634"}'
          ----------------------------------------------------------------------------------------

2023-08-11T14:12:21.641243Z DEBUG tokio-runtime-worker hyper::proto::h1::io: flushed 397 bytes
2023-08-11T14:12:21.646194Z DEBUG ThreadId(01) pact_matching::metrics: Could not get the tokio runtime, will not send metrics - there is no reactor running, must be called from the context of a Tokio 1.x runtime
2023-08-11T14:12:21.646203Z DEBUG ThreadId(01) pact_mock_server::server_manager: Shutting down mock server with ID 804f0cc0-02ce-47b5-ac0f-8c84f595b26c - MockServerMetrics { requests: 1, requests_by_path: {"/any": 1} }
2023-08-11T14:12:21.646209Z DEBUG ThreadId(01) pact_mock_server::mock_server: Mock server 804f0cc0-02ce-47b5-ac0f-8c84f595b26c shutdown - MockServerMetrics { requests: 1, requests_by_path: {"/any": 1} }
github-actions[bot] commented 5 months ago

🤖 Great news! We've labeled this issue as smartbear-supported and created a tracking ticket in PactFlow's Jira (PACT-2100). We'll keep work public and post updates here. Meanwhile, feel free to check out our docs. Thanks for your patience!

rholshausen commented 5 months ago

The interaction has been misconfigured with a type matcher for the path. This is a Pact-JS issue.

matching_rules: MatchingRules { rules: {PATH: MatchingRuleCategory { name: PATH, rules: {DocPath { path_tokens: [Root], expr: "$" }: RuleList { rules: [Type], rule_logic: And, cascaded: false }} }} }

mefellows commented 5 months ago

Ah, good spot! So I need to think about how this function should be defined. It's currently both a matcher and a generator - I think it should not be a matcher itself, but potentially could also accept a matcher or expression in the example

e.g. matcher

    withRequest: {
        method: 'GET',
        path: fromProviderState('${iri}', regex(/..../, '/api/users/14f6626f-c51e-4311-ac52-182c8f2a7634'))
    },

or expression

    withRequest: {
        method: 'GET',
        path: fromProviderState('${iri}', "matching(regex, '\\xyz', '/api/users/14f6626f-c51e-4311-ac52-182c8f2a7634')")
    },

Which one (if any) is the right approach here?

rholshausen commented 5 months ago

Pact-JVJM only sets the generator: https://github.com/search?q=repo%3Apact-foundation%2Fpact-jvm%20pathFromProviderState&type=code

mefellows commented 5 months ago

ok cool, thanks. Is it true that any expression can be used in the second part, or only expressions (of another kind) that contain the dynamically replaceable values?

rholshausen commented 5 months ago

I don't know if that is true. It could be. It could also be false.

mefellows commented 5 months ago

Closing, Pact JS issue (introduced in https://github.com/pact-foundation/pact-js/commit/5fdf7eb8284d73030049e1abf19c34445981510b#diff-884c1c7cbdaafa772a7a428e0ca83ce1fc58f03972dfa0fabcb89106d6ac35f5R409).