Skip to content

Commit

Permalink
feat: add spec for --pact-file-write-mode merge
Browse files Browse the repository at this point in the history
  • Loading branch information
bethesque committed Feb 21, 2018
1 parent 4920920 commit 5fbc230
Show file tree
Hide file tree
Showing 8 changed files with 299 additions and 7 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
tmp
osx
osx
pacts
.rspec_status
3 changes: 3 additions & 0 deletions .rspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
--color
--format documentation
--require spec_helper
11 changes: 6 additions & 5 deletions Gemfile
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
source 'https://rubygems.org'

gem 'rake'
gem 'octokit'
gem 'rubyzip'
gem 'childprocess'
gem 'rack'
gem 'rake', '~> 12.0'
gem 'octokit', '~> 4.7'
gem 'rubyzip', '~> 1.2'
gem 'childprocess', '~> 0.7'
gem 'rack', '~> 2.0'
gem 'rspec', '~> 3.7'
17 changes: 16 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ GEM
public_suffix (~> 2.0, >= 2.0.2)
childprocess (0.7.0)
ffi (~> 1.0, >= 1.0.11)
diff-lcs (1.3)
faraday (0.12.1)
multipart-post (>= 1.2, < 3)
ffi (1.9.18)
Expand All @@ -14,6 +15,19 @@ GEM
public_suffix (2.0.5)
rack (2.0.3)
rake (12.0.0)
rspec (3.7.0)
rspec-core (~> 3.7.0)
rspec-expectations (~> 3.7.0)
rspec-mocks (~> 3.7.0)
rspec-core (3.7.1)
rspec-support (~> 3.7.0)
rspec-expectations (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-mocks (3.7.0)
diff-lcs (>= 1.2.0, < 2.0)
rspec-support (~> 3.7.0)
rspec-support (3.7.1)
rubyzip (1.2.1)
sawyer (0.8.1)
addressable (>= 2.3.5, < 2.6)
Expand All @@ -27,7 +41,8 @@ DEPENDENCIES
octokit
rack
rake
rspec
rubyzip

BUNDLED WITH
1.14.6
1.15.4
4 changes: 4 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
require 'openssl'

require 'rspec/core/rake_task'

RSpec::Core::RakeTask.new(:spec)

LOCAL_PACKAGE_LOCATION = "tmp/pact.zip".freeze
# Simulate a Windows environment on Mac by giving it an empty cert_store
SSL_OPTIONS = {ca_file: 'cacert.pem', cert_store: OpenSSL::X509::Store.new}.freeze
Expand Down
68 changes: 68 additions & 0 deletions spec/mock_service_with_merge_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
require 'faraday'
require 'json'
RSpec.describe "running the mock service with the pact-file-mode merge" do

PACT_FILE_PATH = "./pacts/foo-bar.json"

let(:faraday) do
Faraday.new(:url => "http://localhost:1235") do |faraday|
faraday.adapter Faraday.default_adapter
faraday.response :logger do | logger |
def logger.debug *args; end
end
end
end

let(:mock_service_options) do
{ cli_args: ['--pact-file-write-mode', 'merge'] }
end

let(:first_interaction) do
{description: "test", providerState: nil, request: {method: 'GET', path: '/test'}, response: {status: 200} }
end

let(:second_interaction) do
{description: "another test", providerState: nil, request: {method: 'GET', path: '/another-test'}, response: {status: 200} }
end

let(:interaction_headers) do
{'X-Pact-Mock-Service' => 'true', 'Content-Type' => 'application/json'}
end

let(:admin_header) do
{'X-Pact-Mock-Service' => 'true'}
end

let(:pact_details) do
{ consumer: { name: 'Foo' }, provider: { name: 'bar' }, pact_dir: File.absolute_path('./pacts') }
end

let(:pact_hash) do
JSON.parse(File.read(PACT_FILE_PATH), symbolize_names: true)
end

it "creates a file when one does not already exist" do
FileUtils.rm_rf PACT_FILE_PATH
with_process(mock_service_process(mock_service_options)) do
wait_for_mock_service_to_start(faraday, admin_header)
expect_successful_request(faraday, :get, "/", nil, admin_header)
expect_successful_request(faraday, :post, "/interactions", first_interaction.to_json, interaction_headers)
expect_successful_request(faraday, :get, "/test")
expect_successful_request(faraday, :get, "/interactions/verification", nil, admin_header)
expect_successful_request(faraday, :post, "/pact", pact_details.to_json, admin_header)
expect(File.exists?(PACT_FILE_PATH))
end
end

it "merges the interaction into the existing file when the pact file exists" do
with_process(mock_service_process(mock_service_options)) do
wait_for_mock_service_to_start(faraday, admin_header)
expect_successful_request(faraday, :get, "/", nil, admin_header)
expect_successful_request(faraday, :post, "/interactions", second_interaction.to_json, interaction_headers)
expect_successful_request(faraday, :get, "/another-test")
expect_successful_request(faraday, :get, "/interactions/verification", nil, admin_header)
expect_successful_request(faraday, :post, "/pact", pact_details.to_json, admin_header)
expect(pact_hash[:interactions].size).to eq 2
end
end
end
12 changes: 12 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require "bundler/setup"
require "support/stuff"

RSpec.configure do |config|
config.example_status_persistence_file_path = ".rspec_status"

config.disable_monkey_patching!

config.expect_with :rspec do |c|
c.syntax = :expect
end
end
187 changes: 187 additions & 0 deletions spec/support/stuff.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
require 'openssl'

LOCAL_PACKAGE_LOCATION = "tmp/pact.zip".freeze
# Simulate a Windows environment on Mac by giving it an empty cert_store
SSL_OPTIONS = {ca_file: 'cacert.pem', cert_store: OpenSSL::X509::Store.new}.freeze

def windows?
(/cygwin|mswin|mingw|bccwin|wince|emx/ =~ RUBY_PLATFORM) != nil
end

def github_access_token
ENV.fetch('GITHUB_ACCESS_TOKEN')
end

def get_latest_release_asset_url release_asset_name_regexp
require 'octokit'
stack = Faraday::RackBuilder.new do |builder|
builder.response :logger do | logger |
logger.filter(/(Authorization: )(.*)/,'\1[REMOVED]')
def logger.debug *args; end
end
builder.use Octokit::Response::RaiseError
builder.adapter Faraday.default_adapter
end
Octokit.middleware = stack

repository_slug = 'pact-foundation/pact-ruby-standalone'

client = Octokit::Client.new(access_token: github_access_token)
client.connection_options[:ssl] = SSL_OPTIONS
release = client.latest_release repository_slug
release_assets = client.release_assets release.url
zip = release_assets.find { | release_asset | release_asset.name =~ release_asset_name_regexp }
zip.url
end

def download_release_asset url, file_path
require 'faraday'

faraday = Faraday.new(:url => url, :ssl => SSL_OPTIONS) do |faraday|
faraday.adapter Faraday.default_adapter
faraday.response :logger do | logger |
logger.filter(/(Authorization: )(.*)/,'\1[REMOVED]')
def logger.debug *args; end
end
end

response = faraday.get do | request |
request.headers['Accept'] = 'application/octet-stream'
request.headers['Authorization'] = "token #{github_access_token}"
end
raise "Expected response status 302 but got #{response.status}" unless response.status == 302

faraday = Faraday.new(:url => response.headers['Location'], :ssl => SSL_OPTIONS)
response = faraday.get
raise "Error downloading release" unless response.status == 200

puts "Writing file #{file_path}"
File.open(file_path, "wb") { |file| file << response.body }
puts "Finished writing file #{file_path}"
end

def unzip_package path
require 'zip'
require 'pathname'

puts "Unzipping #{path}"
Zip::File.open(path) do |zip_file|
zip_file.each do |entry|
entry.extract(File.join("tmp", entry.name))
end
end
puts "Finished unzipping #{path}"
end

def build_process cmd_parts, cwd = nil
require 'childprocess'
logger = Logger.new($stdout)
logger.level = Logger::INFO
ChildProcess.logger = logger
process = ChildProcess.build(*cmd_parts)
process.cwd = cwd if cwd
process.leader = true if windows? # not sure if we need this
process.io.inherit!
process
end

def mock_service_process options = {}
port = options[:port] || "1235"
cli_args = options[:cli_args] || []
if windows?
build_process ["cmd.exe", "/c","pact-mock-service.bat", "service", "-p", port] + cli_args, "tmp/pact/bin"
else
# Manually downloaded and extracted, for local testing
build_process ["./pact-mock-service", "service", "-p", port] + cli_args, "osx/pact/bin"
end
end

def test_provider_process
if windows?
build_process ["cmd.exe", "/c","bundle", "exec", "rackup", "-p", "1236", "test/config.ru"]
else
build_process ["ruby", "-S", "bundle", "exec", "rackup", "-p", "1236", "test/config.ru"]
end
end

def pact_verifier_command
suffix = "verify #{File.absolute_path("test/pact.json")} --provider-base-url http://localhost:1236"
if windows?
"cd tmp/pact/bin && cmd.exe /c pact-provider-verifier.bat #{suffix}"
else
# Manually downloaded and extracted, for local testing
"cd osx/pact/bin && ./pact-provider-verifier #{suffix}"
end
end

def with_process process, clean_env = true
if clean_env
Bundler.with_clean_env do
process.start
end
else
process.start
end
yield
ensure
process.stop if process && process.alive?
end

def expect_successful_request faraday, http_method, path, body = nil, headers = {}
response = faraday.send(http_method, path, body, headers)
raise "#{response.status} #{response.body}" unless response.status == 200
response
end

def test_mock_service
require 'faraday'
require 'json'

with_process(mock_service_process) do
sleep 5
admin_header = {'X-Pact-Mock-Service' => 'true'}
pact_details = {consumer: {name: 'Foo'}, provider: {name: 'bar'}, pact_dir: '.\pacts'}
interaction = {description: "test", providerState: nil, request: {method: 'GET', path: '/test'}, response: {status: 200} }

faraday = Faraday.new(:url => "http://localhost:1235") do |faraday|
faraday.adapter Faraday.default_adapter
faraday.response :logger do | logger |
def logger.debug *args; end
end
end

expect_successful_request(faraday, :get, "/", nil, admin_header)
expect_successful_request(faraday, :post, "/interactions", interaction.to_json, {'X-Pact-Mock-Service' => 'true', 'Content-Type' => 'application/json'})
expect_successful_request(faraday, :get, "/test")
expect_successful_request(faraday, :get, "/interactions/verification", nil, admin_header)
expect_successful_request(faraday, :post, "/pact", pact_details.to_json, admin_header)
end
end

def test_verifier
require 'faraday'
with_process(test_provider_process, false) do
sleep 2

Bundler.with_clean_env do
output = `#{pact_verifier_command}`
puts output
raise "pact-provider-verifier did not run as expected" unless output.include?("1 interaction, 0 failures")
end
end
end

def wait_for_mock_service_to_start faraday, admin_headers
i = 0
while true
begin
faraday.get("/", nil, admin_headers)
return true
rescue Faraday::ConnectionFailed => e
i += 1
sleep 1
retry if i < 15
raise e
end
end
end

0 comments on commit 5fbc230

Please sign in to comment.