pact-foundation / pact-ruby

Enables consumer driven contract testing, providing a mock service and DSL for the consumer project, and interaction playback and verification for the service provider project.
https://pact.io
MIT License
2.17k stars 216 forks source link

Service Provider and Message Provider seem incompatible in pact_helper #260

Closed victor-vineti closed 3 months ago

victor-vineti commented 2 years ago

We're having an issue configuring a Pact.service_provider and a Pact.message_provider

1. The relevant versions of the gems you are using.:

1. The steps to recreate your issue.

  1. Configuredpact_helper.rb in the service_consumers folder
  2. Uploaded consumer contracts to Pact and are pulling.
  3. Run bundle exec rails pact:verify

Code example:

require 'pact/provider/rspec'
require 'pact/message'
require 'pact'
require 'rails_helper'
#requiring provider_states
require_relative '../concepts/permission_authz_events/authz_message_provider'
require_relative '../concepts/notification/webhook_message_provider'
require_relative '../service_consumers/provider_state_for_webhook_consumer'

CONFIG = {
  **AuthZMessageProvider::CONFIG, //These follow the message_provider examples
  **WebhookMessageProvider::CONFIG,
  **EmailMessageProvider::CONFIG,
  **SystemEventMessageProvider::CONFIG,
}.freeze

Pact.message_provider "Vineti Platform" do
  honours_pacts_from_pact_broker do
    pact_broker_base_url 'https://vineti.pactflow.io', { token: ENV["PACT_TOKEN"] }
    consumer_version_tags ENV["PACT_CONSUMER_VERSION_TAGS"].to_s.split(/,/)
    publish_verification_results ENV["PACT_PUBLISH"] == "true"
    app_version_tags ENV["PACT_VERSION_TAGS"].to_s.split(/,/)
    app_version ENV["PACT_VERSION"]
    enable_pending true
  end

  builder do |message_description|
    CONFIG[message_description.to_sym].call
  end
end

Pact.service_provider 'Ordering API' do
  honours_pacts_from_pact_broker do
    pact_broker_base_url 'https://vineti.pactflow.io', { token: ENV["PACT_TOKEN"] }
    consumer_version_tags 'SS-PR-874'
    publish_verification_results ENV["PACT_PUBLISH"] == "true"
    app_version_tags ENV["PACT_VERSION_TAGS"].to_s.split(/,/)
    app_version ENV["PACT_VERSION"]
    enable_pending true
  end
end

The issue is that Pact executes either the service_provider block correctly or the message_provider block in isolation but not both.

If the service_provider block is above the message_provider it runs the builder do block for the service provider. This produces this error:

 Failure/Error: CONFIG[message_description.to_sym].call

      NoMethodError:
        undefined method `to_sym' for nil:NilClass

I don't see why the service_provider should run a builder function as that isn't part of its configuration.

If the message_provider is ran before the service_provider, then this is the error.

      Failure/Error: @app.call(env)

      ActionController::RoutingError:
        No route matches [POST] "/"

I think the issue is because of the inheritance and this line: https://github.com/pact-foundation/pact-ruby/blob/bb0ddca700f9aeb05c9f78d214cdbe2962a50786/lib/pact/provider/configuration/message_provider_dsl.rb#L52-L54.

How should we configure this for both http requests and message requests?

bethesque commented 2 years ago

That's a good question. You must be the first person to try it. I don't think I considered this when I wrote it. I'll have to look into it for you.

You may have some other issues because there is limited support for applications that are both message and http pact consumers/providers. What language and pact specification are you using to generate your pacts?

victor-vineti commented 2 years ago

We are using Ruby for the language.

I am not quite sure how to look up the specific Pact specification. Gemfile.lock gives these if this can be mapped to the Pact specification:

pact-message (0.11.1)
      pact-mock_service (~> 3.1)
      pact-support (~> 1.8)
      thor (>= 0.20, < 2.0)
    pact-mock_service (3.9.1)
      filelock (~> 1.1)
      find_a_port (~> 1.0.1)
      json
      pact-support (~> 1.16, >= 1.16.4)
      rack (~> 2.0)
      rspec (>= 2.14)
      term-ansicolor (~> 1.0)
      thor (>= 0.19, < 2.0)
      webrick (~> 1.3)
    pact-support (1.16.7)
      awesome_print (~> 1.1)
      diff-lcs (~> 1.4)
      randexp (~> 0.1.7)
      term-ansicolor (~> 1.0)

The site says this: Pactflow Version: c63147d72.

Jason-Wang-vineti commented 2 years ago

@bethesque is there an available workaround? We were thinking of having two pact_helper.rb files, one for message pacts and one for http pacts, and separate verify commands for each as a potential workaround.

bethesque commented 2 years ago

Hi Jason, yes there is a workaround. Make two separate pact_helper files, but make sure the second one isn't called "pact_helper.rb" or it will be automatically loaded.

Make a new pact verification task, and set the pact_helper file manually. Here are the docs for the custom task

https://docs.pact.io/implementation_guides/ruby/verifying_pacts#using-a-custom-pactverify-task

Pact::VerificationTask.new(:message) do | task |
  task.pact_helper '/path/to/your/message/pact/helper'
end
Jason-Wang-vineti commented 2 years ago

This workaround worked! Thank you @bethesque, appreciate it.

If the consumer describes both HTTP and message based interactions for the provider, then running the custom pact verification task and the default one will both have pending failures because they are isolated to either HTTP or message. Does this mean I'll have to break out the consumer into two separate tests?

Before:

describe 'My Provider', pact: true do
  context 'HTTP based interactions' do
    ...
  end

  context 'message based interactions' do
    ...
  end
end

After:

describe 'My Provider HTTP', pact: true do
  ...
end

describe 'My Provider message', pact: true do
  ...
end
bethesque commented 2 years ago

The spec tag for message pact is actually pact: :message. Does that make a difference?

Jason-Wang-vineti commented 2 years ago

Sorry for the late follow up, but that didn't work for us. For now, we're splitting our HTTP and message interactions within a service.

YOU54F commented 3 months ago

Message pact was introduced in pact V3 specification. one of the limitations was that a pact file could not contain both http and message pact interactions.

You may have some other issues because there is limited support for applications that are both message and http pact consumers/providers.

Which means this is still an issue with the pact v3 spec.

Pact v4 spec introduced multiple interaction types in a single pact file, mitigating this limitation. This should be considered during #319.

For now, if you are using pact-message-ruby for consumers, or pact-ruby for verifying providers, name http/message providers differently, and create 1 distinct helpers and 2 verification tasks, 1 for each type (http & message).

A useful interim measure would be to have a warning if a user has configured both a service_provider and message_provider in the same file.