Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat([PoC]): Contract for sequence of messages #1

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
3.0.4
3.0.3
6 changes: 4 additions & 2 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,9 @@ group :development, :test do

gem 'pact'

gem 'pact-message'
gem 'pact-message', git: 'https://github.com/Mifrill/pact-message-ruby', branch: 'feature/messages'

gem 'rake'
end

gem 'byebug'
end
20 changes: 14 additions & 6 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
GIT
remote: https://github.com/Mifrill/pact-message-ruby
revision: 1b55bec934ed37896f4e8860b63ae1eed0070589
branch: feature/messages
specs:
pact-message (0.11.1)
pact-mock_service (~> 3.1)
pact-support (~> 1.8)
thor (>= 0.20, < 2.0)

GEM
remote: https://rubygems.org/
specs:
awesome_print (1.9.2)
byebug (11.1.3)
coderay (1.1.3)
diff-lcs (1.5.0)
expgen (0.1.1)
Expand All @@ -18,10 +29,6 @@ GEM
term-ansicolor (~> 1.0)
thor (>= 0.20, < 2.0)
webrick (~> 1.3)
pact-message (0.11.1)
pact-mock_service (~> 3.1)
pact-support (~> 1.8)
thor (>= 0.20, < 2.0)
pact-mock_service (3.10.0)
filelock (~> 1.1)
find_a_port (~> 1.0.1)
Expand Down Expand Up @@ -71,11 +78,12 @@ PLATFORMS
x86_64-darwin-21

DEPENDENCIES
byebug
pact
pact-message
pact-message!
pry
rake
rspec

BUNDLED WITH
2.2.33
2.3.26
6 changes: 1 addition & 5 deletions app/consumers/test_message_consumer.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,5 @@
class TestMessageConsumer

def consume_message(message)
puts "Message consumed"
puts message.to_json
message
end

end
end
21 changes: 11 additions & 10 deletions app/producers/test_message_producer.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
class TestMessageProducer

def publish_message()
message = {
"email": "[email protected]",
"title": "Miss",
"first_name": "Jane",
"surname": "Doe"
}
def publish_message
[
{
"first_name": "John",
"last_name": "Doe"
},
{
"hello": "world"
}
]
end
end

end
9 changes: 9 additions & 0 deletions log/pact.log
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Logfile created on 2023-02-09 19:42:49 +0000 by logger.rb/v1.4.3
I, [2023-02-09T19:42:49.857092 #48830] INFO -- : Running example 'Verifying a pact between Test Message Consumer and Test Message Producer Given Class1 updated has matching content'
I, [2023-02-09T19:42:49.857543 #48830] INFO -- : Sending POST request to path: "/" with headers: {"CONTENT_TYPE"=>"application/json"}, see debug logs for body
D, [2023-02-09T19:42:49.857570 #48830] DEBUG -- : body :{"description":"updated","providerStates":[{"name":"Class1","params":{}}],"metadata":null}
I, [2023-02-09T19:45:49.705091 #53882] INFO -- : Running example 'Verifying a pact between Test Message Consumer and Test Message Producer Given Class1 created;updated has matching content'
I, [2023-02-09T19:45:49.705755 #53882] INFO -- : Sending POST request to path: "/" with headers: {"CONTENT_TYPE"=>"application/json"}, see debug logs for body
D, [2023-02-09T19:45:49.705791 #53882] DEBUG -- : body :{"description":"created;updated","providerStates":[{"name":"Class1","params":{}}],"metadata":null}
I, [2023-02-09T19:45:49.711473 #53882] INFO -- : Received response with status: 200, headers: {"Content-Type"=>"application/json"}, see debug logs for body
D, [2023-02-09T19:45:49.711504 #53882] DEBUG -- : body: {"contents":[{"first_name":"John","last_name":"Doe"},{"hello":"world"}]}
25 changes: 25 additions & 0 deletions reports/pacts/help.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# For assistance debugging failures

* The pact files have been stored locally in the following temp directory:
/Users/alex.strizhak/Downloads/pact-ruby-demo/tmp/pacts

* The requests and responses are logged in the following log file:
/Users/alex.strizhak/Downloads/pact-ruby-demo/log/pact.log

* Add BACKTRACE=true to the `rake pact:verify` command to see the full backtrace

* If the diff output is confusing, try using another diff formatter.
The options are :unix, :embedded and :list

Pact.configure do | config |
config.diff_formatter = :embedded
end

See https://github.com/pact-foundation/pact-ruby/blob/master/documentation/configuration.md#diff_formatter for examples and more information.

* Check out https://github.com/pact-foundation/pact-ruby/wiki/Troubleshooting

* Ask a question on stackoverflow and tag it `pact-ruby`


Tried to retrieve diff with previous pact, but received error Pact::Hal::RelationNotFoundError Could not find relation 'pb:diff-previous-distinct' in resource at spec/pacts/test_message_consumer-test_message_producer.json.
69 changes: 28 additions & 41 deletions spec/consumer_spec.rb
Original file line number Diff line number Diff line change
@@ -1,52 +1,39 @@
require 'pact/message/consumer/rspec'
require 'support/pact_spec_helper.rb'
require_relative '../app/consumers/test_message_consumer.rb'

describe TestMessageConsumer, pact: true do

subject(:consumer) {TestMessageConsumer.new}

# Notice that the expected message payload has fields which are different to that of the actual producer.
# The TestMessageProducer actually sends a message with an additional 'title' field and a renamed 'surname' field.
# See app/producers/test_message_producer.rb.
require_relative '../app/consumers/test_message_consumer.rb'

expected_payload = {
"email": "[email protected]",
"first_name": "Jane",
"last_name": "Doe"
describe TestMessageConsumer do
let(:expected_payload_1) {
{
"first_name": "John",
"last_name": "Doe"
}
}

# Notice the new pact: :message decorator/metadata
let(:expected_payload_2) {
{
"hello": "world"
}
}
let(:consumer) { described_class.new }

describe "Test Message Consumer", pact: :message do
before do

# Here we are calling test_message_producer, which is mocking the actual TestMessageProducer defined in app/producers/test_message_producer.rb.
# In pact-message we use mocked providers in consumer side tests. These are defined in a similar way to mocked APIs/service providers in standard HTTP CDCT.
# See spec/support/pact_spec_helper.rb.

test_message_producer.given("A customer is created")
.is_expected_to_send("a customer created message")
# .with_metadata()
.with_content(expected_payload)

test_message_producer.send_message_string do | content_string |
@message = consumer.consume_message(content_string)
it "generates contract" do
test_message_producer
.given("Class1")
.is_expected_to_send("created")
.with_content(expected_payload_1)
test_message_producer.send_message_string do |content_string|
expect(consumer.consume_message(content_string)).to eq(expected_payload_1.to_json)
end

test_message_producer
.given("Class2")
.is_expected_to_send("updated")
.with_content(expected_payload_2)
test_message_producer.send_message_string do |content_string|
expect(consumer.consume_message(content_string)).to eq(expected_payload_2.to_json)
end
end

# This test is a bit redundant, it's essentially marking our own homework and will always pass.
# However IRL the consumer would probably be doing something more complex which we could assert on.
# See spec/pacts/test_message_consumer-test_message_producer.json for the generated contract file.
# Note that this contract does not match what the producer outputs in app/producers/test_message_producer.rb..
# If we were to run producer side verification on this contract, it should fail.
# This failure would indicate a mismatch between the consumers expectations of the message format and what the producer actually sends.

it "Successfully consumes the message and creates a pact contract file" do
expect(@message).to eq(expected_payload.to_json)
end

end

end
end
23 changes: 16 additions & 7 deletions spec/pacts/test_message_consumer-test_message_producer.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,28 @@
},
"messages": [
{
"description": "a customer created message",
"description": "created;updated",
"providerStates": [
{
"name": "A customer is created",
"name": "Class1",
"params": {
}
},
{
"name": "Class2",
"params": {
}
}
],
"contents": [
{
"first_name": "John",
"last_name": "Doe"
},
{
"hello": "world"
}
],
"contents": {
"email": "[email protected]",
"first_name": "Jane",
"last_name": "Doe"
},
"matchingRules": {
}
}
Expand Down
33 changes: 16 additions & 17 deletions spec/service_consumers/pact_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,36 +9,35 @@
require 'pact/message'

Pact.message_provider "Test Message Provider" do

# Explictly creates an instance of the producer.
# This might be different to how we create instances of rails/rack APIs in HTTP Pact.
Producer = TestMessageProducer.new

# This matches the usage in HTTP Pact.
# This matches the usage in HTTP Pact.
honours_pact_with "Test Message Consumer" do
pact_uri "spec/pacts/test_message_consumer-test_message_producer.json"
end

# Create a hash of all the messages the producer needs to publish to meet the preconditions of the contract files it is referenced in.
# This is the recommended approach in the pact-message-ruby documentation. See https://github.com/pact-foundation/pact-message-ruby
MESSAGES = {
"a customer created message" => lambda { Producer.publish_message }
}

# Builder which populates the hash.
# Calls the lambda above, which calls the Producer.publish_message method.
# See app/producers/test_message_producer.rb.
# The producer publishes a message (json).
# The message is used in the dynamically generated RSpec tests to verify the contract provided by the consumer.
# The producer publishes a message (json).
# The message is used in the dynamically generated RSpec tests to verify the contract provided by the consumer.
builder do |message_description|
MESSAGES[message_description].call
# Explictly creates an instance of the producer.
# This might be different to how we create instances of rails/rack APIs in HTTP Pact.
producer = TestMessageProducer.new

# Create a hash of all the messages the producer needs to publish to meet the preconditions of the contract files it is referenced in.
# This is the recommended approach in the pact-message-ruby documentation. See https://github.com/pact-foundation/pact-message-ruby
messages = {
"created;updated" => lambda { producer.publish_message },
}

messages[message_description].call
end

#Execute the following to run the provider side verification on the contract files in spec/pacts:
#Execute the following to run the provider side verification on the contract files in spec/pacts:
# bundle exec rake pact:verify
# The pact verification tool will do the following:
# 1. Read the contract files (pact files) in spec/pacts.
# 2. Setup the required pre-requiste test state (provider states) referenced in the contract files.
# 3. Dynamically generate and execute RSpec tests based on the content of the contract files.

end
end
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
Pact.provider_states_for "Test Message Consumer" do

provider_state "A customer is created" do
provider_state "Class1" do
set_up do
# no_op
# Pre-requiste test setup code to create this state would go here
# Pre-requests test setup code to create this state would go here
end
end

end
provider_state "Class2" do
set_up do
end
end
end