pact 0.1.28
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +28 -0
- data/.rspec +2 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +83 -0
- data/LICENSE.txt +22 -0
- data/README.md +238 -0
- data/Rakefile +33 -0
- data/bin/pact +4 -0
- data/lib/pact/app.rb +32 -0
- data/lib/pact/configuration.rb +54 -0
- data/lib/pact/consumer/app_manager.rb +177 -0
- data/lib/pact/consumer/configuration_dsl.rb +71 -0
- data/lib/pact/consumer/consumer_contract_builder.rb +79 -0
- data/lib/pact/consumer/consumer_contract_builders.rb +10 -0
- data/lib/pact/consumer/dsl.rb +98 -0
- data/lib/pact/consumer/interaction.rb +70 -0
- data/lib/pact/consumer/mock_service.rb +340 -0
- data/lib/pact/consumer/rspec.rb +43 -0
- data/lib/pact/consumer/run_condor.rb +4 -0
- data/lib/pact/consumer/run_mock_contract_service.rb +13 -0
- data/lib/pact/consumer/service_consumer.rb +22 -0
- data/lib/pact/consumer/service_producer.rb +23 -0
- data/lib/pact/consumer.rb +7 -0
- data/lib/pact/consumer_contract.rb +110 -0
- data/lib/pact/json_warning.rb +23 -0
- data/lib/pact/logging.rb +14 -0
- data/lib/pact/matchers/matchers.rb +85 -0
- data/lib/pact/matchers.rb +1 -0
- data/lib/pact/producer/configuration_dsl.rb +62 -0
- data/lib/pact/producer/matchers.rb +22 -0
- data/lib/pact/producer/pact_spec_runner.rb +57 -0
- data/lib/pact/producer/producer_state.rb +81 -0
- data/lib/pact/producer/rspec.rb +127 -0
- data/lib/pact/producer/test_methods.rb +89 -0
- data/lib/pact/producer.rb +1 -0
- data/lib/pact/rake_task.rb +64 -0
- data/lib/pact/reification.rb +26 -0
- data/lib/pact/request.rb +109 -0
- data/lib/pact/term.rb +40 -0
- data/lib/pact/verification_task.rb +57 -0
- data/lib/pact/version.rb +3 -0
- data/lib/pact.rb +5 -0
- data/lib/tasks/pact.rake +6 -0
- data/pact.gemspec +36 -0
- data/scratchpad.txt +36 -0
- data/spec/features/consumption_spec.rb +146 -0
- data/spec/features/producer_states/zebras.rb +28 -0
- data/spec/features/production_spec.rb +160 -0
- data/spec/integration/pact/configuration_spec.rb +65 -0
- data/spec/lib/pact/configuration_spec.rb +35 -0
- data/spec/lib/pact/consumer/app_manager_spec.rb +41 -0
- data/spec/lib/pact/consumer/consumer_contract_builder_spec.rb +87 -0
- data/spec/lib/pact/consumer/dsl_spec.rb +52 -0
- data/spec/lib/pact/consumer/interaction_spec.rb +108 -0
- data/spec/lib/pact/consumer/mock_service_spec.rb +147 -0
- data/spec/lib/pact/consumer/service_consumer_spec.rb +11 -0
- data/spec/lib/pact/consumer_contract_spec.rb +125 -0
- data/spec/lib/pact/matchers/matchers_spec.rb +354 -0
- data/spec/lib/pact/producer/configuration_dsl_spec.rb +101 -0
- data/spec/lib/pact/producer/producer_state_spec.rb +103 -0
- data/spec/lib/pact/producer/rspec_spec.rb +48 -0
- data/spec/lib/pact/reification_spec.rb +43 -0
- data/spec/lib/pact/request_spec.rb +316 -0
- data/spec/lib/pact/term_spec.rb +36 -0
- data/spec/lib/pact/verification_task_spec.rb +64 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/a_consumer-a_producer.json +34 -0
- data/spec/support/pact_rake_support.rb +41 -0
- data/spec/support/test_app_fail.json +22 -0
- data/spec/support/test_app_pass.json +21 -0
- data/tasks/pact-test.rake +19 -0
- metadata +381 -0
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'open-uri'
|
2
|
+
require 'pact/consumer_contract'
|
3
|
+
require 'pact/json_warning'
|
4
|
+
require_relative 'matchers'
|
5
|
+
require_relative 'test_methods'
|
6
|
+
require_relative 'configuration_dsl'
|
7
|
+
|
8
|
+
module Pact
|
9
|
+
module Producer
|
10
|
+
module RSpec
|
11
|
+
|
12
|
+
module InstanceMethods
|
13
|
+
def app
|
14
|
+
Pact.configuration.producer.app
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module ClassMethods
|
19
|
+
|
20
|
+
include Pact::JsonWarning
|
21
|
+
|
22
|
+
def honour_pactfile pactfile_uri, options = {}
|
23
|
+
describe "Pact in #{pactfile_uri}" do
|
24
|
+
consumer_contract = Pact::ConsumerContract.from_json(read_pact_from(pactfile_uri, options))
|
25
|
+
honour_consumer_contract consumer_contract, options
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def honour_consumer_contract consumer_contract, options = {}
|
30
|
+
check_for_active_support_json
|
31
|
+
describe_consumer_contract consumer_contract, options.merge({:consumer => consumer_contract.consumer.name})
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def describe_consumer_contract consumer_contract, options
|
37
|
+
consumer_contract.interactions.each do |interaction|
|
38
|
+
describe_interaction_with_producer_state interaction, options
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def describe_interaction_with_producer_state interaction, options
|
43
|
+
if interaction.producer_state
|
44
|
+
describe "Given #{interaction.producer_state}" do
|
45
|
+
describe_interaction interaction, options
|
46
|
+
end
|
47
|
+
else
|
48
|
+
describe_interaction interaction, options
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def describe_interaction interaction, options
|
53
|
+
|
54
|
+
describe description_for(interaction) do
|
55
|
+
|
56
|
+
before do
|
57
|
+
set_up_producer_state interaction.producer_state, options[:consumer]
|
58
|
+
replay_interaction interaction
|
59
|
+
end
|
60
|
+
|
61
|
+
after do
|
62
|
+
tear_down_producer_state interaction.producer_state, options[:consumer]
|
63
|
+
end
|
64
|
+
|
65
|
+
describe_response interaction.response
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
def describe_response response
|
71
|
+
describe "returns a response which" do
|
72
|
+
if response['status']
|
73
|
+
it "has status code #{response['status']}" do
|
74
|
+
expect(last_response.status).to eql response['status']
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
if response['headers']
|
79
|
+
describe "includes headers" do
|
80
|
+
response['headers'].each do |name, value|
|
81
|
+
it "\"#{name}\" with value \"#{value}\"" do
|
82
|
+
expect(last_response.headers[name]).to match_term value
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
if response['body']
|
89
|
+
it "has a matching body" do
|
90
|
+
logger.debug "Response body is #{last_response.body}"
|
91
|
+
expect(parse_entity_from_response(last_response)).to match_term response['body']
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def description_for interaction
|
98
|
+
"#{interaction.description} to #{interaction.request.path}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def read_pact_from uri, options = {}
|
102
|
+
pact = open(uri) { | file | file.read }
|
103
|
+
if options[:save_pactfile_to_tmp]
|
104
|
+
save_pactfile_to_tmp pact, File.basename(uri)
|
105
|
+
end
|
106
|
+
pact
|
107
|
+
rescue StandardError => e
|
108
|
+
$stderr.puts "Error reading file from #{uri}"
|
109
|
+
$stderr.puts "#{e.to_s} #{e.backtrace.join("\n")}"
|
110
|
+
raise e
|
111
|
+
end
|
112
|
+
|
113
|
+
def save_pactfile_to_tmp pact, name
|
114
|
+
File.open(Pact.configuration.tmp_dir + "/#{name}", "w") { |file| file << pact}
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
RSpec.configure do |config|
|
123
|
+
config.extend Pact::Producer::RSpec::ClassMethods
|
124
|
+
config.include Pact::Producer::RSpec::InstanceMethods
|
125
|
+
config.include Pact::Producer::TestMethods
|
126
|
+
config.include Pact::Producer::TestMethods
|
127
|
+
end
|
@@ -0,0 +1,89 @@
|
|
1
|
+
require 'pact/logging'
|
2
|
+
require 'rack/test'
|
3
|
+
require 'pact/reification'
|
4
|
+
require 'pact/producer/producer_state'
|
5
|
+
|
6
|
+
module Pact
|
7
|
+
module Producer
|
8
|
+
module TestMethods
|
9
|
+
|
10
|
+
include Pact::Logging
|
11
|
+
include Rack::Test::Methods
|
12
|
+
|
13
|
+
def parse_entity_from_response response
|
14
|
+
case response.headers['Content-Type']
|
15
|
+
when /json/
|
16
|
+
JSON.load(response.body)
|
17
|
+
else
|
18
|
+
response.body
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def set_up_producer_state producer_state_name, consumer
|
23
|
+
if producer_state_name
|
24
|
+
get_producer_state(producer_state_name, consumer).set_up
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def tear_down_producer_state producer_state_name, consumer
|
29
|
+
if producer_state_name
|
30
|
+
get_producer_state(producer_state_name, consumer).tear_down
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def replay_interaction interaction
|
35
|
+
request = interaction.request
|
36
|
+
path = request_path(request)
|
37
|
+
args = [path, request_body(request)]
|
38
|
+
|
39
|
+
if !request.headers.nil?
|
40
|
+
args << request_headers(request)
|
41
|
+
end
|
42
|
+
|
43
|
+
logger.debug "Sending #{request.method} with #{args}"
|
44
|
+
self.send(request.method, *args)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def request_headers request
|
50
|
+
request_headers = {}
|
51
|
+
request.headers.each do |key, value|
|
52
|
+
key = key.upcase
|
53
|
+
if key.match(/CONTENT.TYPE/)
|
54
|
+
request_headers['CONTENT_TYPE'] = value
|
55
|
+
else
|
56
|
+
request_headers['HTTP_' + key.to_s] = value
|
57
|
+
end
|
58
|
+
end
|
59
|
+
request_headers
|
60
|
+
end
|
61
|
+
|
62
|
+
def request_path request
|
63
|
+
path = request.path
|
64
|
+
query = request.query
|
65
|
+
if query && !query.empty?
|
66
|
+
path += "?" + request.query
|
67
|
+
end
|
68
|
+
path
|
69
|
+
end
|
70
|
+
|
71
|
+
def request_body request
|
72
|
+
body = request.body
|
73
|
+
if body
|
74
|
+
body = JSON.dump(Pact::Reification.from_term(body))
|
75
|
+
else
|
76
|
+
body = ""
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
def get_producer_state producer_state_name, consumer
|
81
|
+
unless producer_state = ProducerState.get(producer_state_name, :for => consumer)
|
82
|
+
extra = consumer ? " for consumer \"#{consumer}\"" : ""
|
83
|
+
raise "Could not find a producer state defined for \"#{producer_state_name}\"#{extra}. Have you required the producer state file in your spec?"
|
84
|
+
end
|
85
|
+
producer_state
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
#TODO delete this file after checking if any projects load it
|
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'rake/tasklib'
|
2
|
+
|
3
|
+
|
4
|
+
module Pact
|
5
|
+
|
6
|
+
##
|
7
|
+
# To enable `rake pact`, put something like this in your Rakefile:
|
8
|
+
#
|
9
|
+
# ```
|
10
|
+
# require 'pact/rake_task'
|
11
|
+
#
|
12
|
+
# Pact::RakeTask.new do |pact|
|
13
|
+
# pact.file 'spec/pacts/some-pact.json',
|
14
|
+
# from_url: 'http://example.com/some-pact.json'
|
15
|
+
# pact.file 'spec/pacts/other-pact.json',
|
16
|
+
# from_url: 'http://example.com/other-pact.json'
|
17
|
+
# end
|
18
|
+
# ```
|
19
|
+
class RakeTask < ::Rake::TaskLib
|
20
|
+
attr_reader :connections
|
21
|
+
|
22
|
+
def initialize(name = :pact)
|
23
|
+
@connections = []
|
24
|
+
|
25
|
+
yield self
|
26
|
+
|
27
|
+
namespace name do
|
28
|
+
desc "Update integration pacts from external sources"
|
29
|
+
task :pull do
|
30
|
+
connections.each do |conn|
|
31
|
+
body = fetch conn[:url]
|
32
|
+
File.open(conn[:file], 'w') {|f| f.write body }
|
33
|
+
puts "Wrote #{conn[:url]} to #{conn[:file]}"
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
task name => "#{name}:pull" # default task for the namespace
|
38
|
+
end
|
39
|
+
|
40
|
+
def file(filename, options = {})
|
41
|
+
url = options.fetch(:from_url)
|
42
|
+
@connections << {file: filename, url: url}
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
# written with plain Net::HTTP to avoid bringing in extra dependencies
|
47
|
+
def fetch(url_string, redirection_limit = 5)
|
48
|
+
raise 'Too many HTTP redirects' if redirection_limit == 0
|
49
|
+
url = URI.parse(url_string)
|
50
|
+
response = Net::HTTP.get_response(url)
|
51
|
+
case response
|
52
|
+
when Net::HTTPSuccess then
|
53
|
+
response.body
|
54
|
+
when Net::HTTPRedirection then
|
55
|
+
location = response['Location']
|
56
|
+
fetch(location, redirection_limit - 1)
|
57
|
+
else
|
58
|
+
response.value # raise the appropriate error
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
|
64
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'randexp'
|
2
|
+
|
3
|
+
module Pact
|
4
|
+
module Reification
|
5
|
+
|
6
|
+
def self.from_term(term)
|
7
|
+
case
|
8
|
+
when term.respond_to?(:generate)
|
9
|
+
term.generate
|
10
|
+
when term.is_a?(Hash)
|
11
|
+
term.inject({}) do |mem, (key,term)|
|
12
|
+
mem[key] = from_term(term)
|
13
|
+
mem
|
14
|
+
end
|
15
|
+
when term.is_a?(Array)
|
16
|
+
term.inject([]) do |mem, term|
|
17
|
+
mem << from_term(term)
|
18
|
+
mem
|
19
|
+
end
|
20
|
+
else
|
21
|
+
term
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
data/lib/pact/request.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
require 'pact/matchers'
|
2
|
+
|
3
|
+
module Pact
|
4
|
+
|
5
|
+
module Request
|
6
|
+
|
7
|
+
class NullExpectation
|
8
|
+
def to_s
|
9
|
+
"<No expectation>"
|
10
|
+
end
|
11
|
+
|
12
|
+
def ==(other_object)
|
13
|
+
other_object.is_a? NullExpectation
|
14
|
+
end
|
15
|
+
|
16
|
+
def ===(other_object)
|
17
|
+
other_object.is_a? NullExpectation
|
18
|
+
end
|
19
|
+
|
20
|
+
def eql?(other_object)
|
21
|
+
self == other_object
|
22
|
+
end
|
23
|
+
|
24
|
+
def hash
|
25
|
+
2934820948209428748274238642672
|
26
|
+
end
|
27
|
+
|
28
|
+
def empty?
|
29
|
+
true
|
30
|
+
end
|
31
|
+
|
32
|
+
def nil?
|
33
|
+
true
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class Base
|
38
|
+
include Pact::Matchers
|
39
|
+
extend Pact::Matchers
|
40
|
+
|
41
|
+
NULL_EXPECTATION = NullExpectation.new
|
42
|
+
|
43
|
+
attr_reader :method, :path, :headers, :body, :query
|
44
|
+
|
45
|
+
def self.from_hash(hash)
|
46
|
+
sym_hash = hash.inject({}) { |memo, (k,v)| memo[k.to_sym] = v; memo }
|
47
|
+
method = sym_hash.fetch(:method)
|
48
|
+
path = sym_hash.fetch(:path)
|
49
|
+
query = sym_hash.fetch(:query, NULL_EXPECTATION)
|
50
|
+
headers = sym_hash.fetch(:headers, NULL_EXPECTATION)
|
51
|
+
body = sym_hash.fetch(:body, NULL_EXPECTATION)
|
52
|
+
new(method, path, headers, body, query)
|
53
|
+
end
|
54
|
+
|
55
|
+
def initialize(method, path, headers, body, query)
|
56
|
+
@method = method.to_s
|
57
|
+
@path = path.chomp('/')
|
58
|
+
@headers = headers
|
59
|
+
@body = body
|
60
|
+
@query = query
|
61
|
+
end
|
62
|
+
|
63
|
+
def to_json(options = {})
|
64
|
+
as_json.to_json(options)
|
65
|
+
end
|
66
|
+
|
67
|
+
def as_json
|
68
|
+
base_json = {
|
69
|
+
method: method,
|
70
|
+
path: path,
|
71
|
+
}
|
72
|
+
|
73
|
+
base_json.merge!(body: body) unless body.is_a? NullExpectation
|
74
|
+
base_json.merge!(headers: headers) unless headers.is_a? NullExpectation
|
75
|
+
base_json.merge!(query: query) unless query.is_a? NullExpectation
|
76
|
+
base_json
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
class Expected < Base
|
82
|
+
|
83
|
+
attr_accessor :description
|
84
|
+
|
85
|
+
def self.from_hash(hash)
|
86
|
+
request = super
|
87
|
+
request.description = hash.fetch(:description, nil)
|
88
|
+
request
|
89
|
+
end
|
90
|
+
|
91
|
+
def match(actual_request)
|
92
|
+
difference(actual_request).empty?
|
93
|
+
end
|
94
|
+
|
95
|
+
def matches_route? actual_request
|
96
|
+
diff({:method => method, :path => path}, {:method => actual_request.method, :path => actual_request.path}).empty?
|
97
|
+
end
|
98
|
+
|
99
|
+
def difference(actual_request)
|
100
|
+
diff(as_json, actual_request.as_json)
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
class Actual < Base
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
end
|
data/lib/pact/term.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
module Pact
|
2
|
+
class Term
|
3
|
+
|
4
|
+
attr_reader :generate, :matcher
|
5
|
+
|
6
|
+
def self.json_create(obj)
|
7
|
+
new(generate: obj['data']['generate'], matcher: obj['data']['matcher'])
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(attributes = {})
|
11
|
+
@generate = attributes[:generate]
|
12
|
+
@matcher = attributes[:matcher]
|
13
|
+
end
|
14
|
+
|
15
|
+
def to_json(options = {})
|
16
|
+
{ json_class: self.class.name, data: { generate: generate, matcher: matcher} }.to_json(options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def match(literal)
|
20
|
+
literal.respond_to?(:to_s) ? matcher.match(literal.to_s) : nil
|
21
|
+
end
|
22
|
+
|
23
|
+
def ==(other)
|
24
|
+
return false unless other.respond_to?(:generate) && other.respond_to?(:matcher)
|
25
|
+
generate == other.generate && matcher == other.matcher
|
26
|
+
end
|
27
|
+
|
28
|
+
def to_s
|
29
|
+
"Pact::Term matcher: #{matcher.to_s}" + (generate.nil? ? "" : " generate: \"#{generate}\"")
|
30
|
+
end
|
31
|
+
|
32
|
+
def diff_with_actual(actual)
|
33
|
+
match(actual) ? nil : {
|
34
|
+
expected: self,
|
35
|
+
actual: actual
|
36
|
+
}
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'rake/tasklib'
|
2
|
+
require 'pact/producer/pact_spec_runner'
|
3
|
+
|
4
|
+
=begin
|
5
|
+
To create a rake pact:verify:<something> task
|
6
|
+
|
7
|
+
Pact::VerificationTask.new(:head) do | pact |
|
8
|
+
pact.uri 'http://master.cd.vpc.realestate.com.au/browse/BIQ-MAS/latestSuccessful/artifact/JOB2/Pacts/mas-contract_transaction_service.json',
|
9
|
+
support_file: './spec/consumers/pact_helper'
|
10
|
+
pact.uri 'http://master.cd.vpc.realestate.com.au/browse/BIQ-IMAGINARY-CONSUMER/latestSuccessful/artifact/JOB2/Pacts/imaginary_consumer-contract_transaction_service.json',
|
11
|
+
support_file: './spec/consumers/pact_helper'
|
12
|
+
end
|
13
|
+
|
14
|
+
The pact.uri may be a local file system path or a remote URL.
|
15
|
+
The support_file should include code that makes your rack app available for the rack testing framework.
|
16
|
+
Eg.
|
17
|
+
|
18
|
+
Pact.configure do | config |
|
19
|
+
config.producer do
|
20
|
+
name "My Producer"
|
21
|
+
app { TestApp.new }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
It should also load all your app's dependencies (eg by calling out to spec_helper)
|
26
|
+
|
27
|
+
=end
|
28
|
+
|
29
|
+
module Pact
|
30
|
+
class VerificationTask < ::Rake::TaskLib
|
31
|
+
attr_reader :pact_spec_config
|
32
|
+
|
33
|
+
def initialize(name)
|
34
|
+
@pact_spec_config = []
|
35
|
+
|
36
|
+
yield self
|
37
|
+
|
38
|
+
namespace :pact do
|
39
|
+
desc "Verify producer against the consumer pacts for #{name}"
|
40
|
+
task "verify:#{name}" do
|
41
|
+
exit_status = Producer::PactSpecRunner.run(pact_spec_config)
|
42
|
+
fail failure_message if exit_status != 0
|
43
|
+
end
|
44
|
+
|
45
|
+
def failure_message
|
46
|
+
"\n* * * * * * * * * * * * * * * * * * *\n" +
|
47
|
+
"Producer did not honour pact file.\nSee\n * #{Pact.configuration.log_path}\n * #{Pact.configuration.tmp_dir}\nfor logs and pact files." +
|
48
|
+
"\n* * * * * * * * * * * * * * * * * * *\n\n"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def uri(uri, options)
|
54
|
+
@pact_spec_config << {uri: uri, support_file: options[:support_file], consumer: options[:consumer]}
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
data/lib/pact/version.rb
ADDED
data/lib/pact.rb
ADDED
data/lib/tasks/pact.rake
ADDED
data/pact.gemspec
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'pact/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "pact"
|
8
|
+
gem.version = Pact::VERSION
|
9
|
+
gem.authors = ["James Fraser", "Sergei Matheson", "Brent Snook", "Ronald Holshausen", "Bethany Skurrie"]
|
10
|
+
gem.email = ["james.fraser@alumni.swinburne.edu", "sergei.matheson@gmail.com", "brent@fuglylogic.com", "uglyog@gmail.com", "bskurrie@dius.com.au"]
|
11
|
+
gem.description = %q{Define a pact between service consumers and providers}
|
12
|
+
gem.summary = %q{Define a pact between service consumers and providers}
|
13
|
+
gem.homepage = "https://github.com/uglyog/pact.git"
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
gem.license = 'MIT'
|
20
|
+
|
21
|
+
gem.add_runtime_dependency 'randexp', '~> 0.1.7'
|
22
|
+
gem.add_runtime_dependency 'hashie', '~> 2.0.5'
|
23
|
+
gem.add_runtime_dependency 'rspec', '~> 2.12'
|
24
|
+
gem.add_runtime_dependency 'find_a_port', '~> 1.0.1'
|
25
|
+
gem.add_runtime_dependency 'rack-test', '~> 0.6.2'
|
26
|
+
gem.add_runtime_dependency 'awesome_print', '~> 1.1.0'
|
27
|
+
gem.add_runtime_dependency 'capybara', '~> 2.1.0'
|
28
|
+
gem.add_runtime_dependency 'thor'
|
29
|
+
gem.add_runtime_dependency 'thin'
|
30
|
+
gem.add_runtime_dependency 'json' #Not locking down a version because buncher gem requires 1.6, while other projects use 1.7.
|
31
|
+
|
32
|
+
gem.add_development_dependency 'geminabox-client'
|
33
|
+
gem.add_development_dependency 'rake', '~> 10.0.3'
|
34
|
+
gem.add_development_dependency 'webmock', '~> 1.9.3'
|
35
|
+
gem.add_development_dependency 'pry'
|
36
|
+
end
|
data/scratchpad.txt
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
Ideas for expectation DSL
|
2
|
+
|
3
|
+
Unfortunately RSpec has already stolen the method "example".
|
4
|
+
Could use:
|
5
|
+
eg
|
6
|
+
ex
|
7
|
+
|
8
|
+
my_producer.
|
9
|
+
given("a thing exists").
|
10
|
+
upon_receiving("a request for a thing").with({:method => 'get', :path => '/thing'})
|
11
|
+
will_respond_with({:body => {
|
12
|
+
name: ex("Fred"),
|
13
|
+
age: eg 29,
|
14
|
+
mobile: eg("0415 134 234", /\d{4} \d{3} \d{3}/),
|
15
|
+
dob: eg("1983-02-28", match: /\d\d\d\d-\d\d-\d\d/),
|
16
|
+
driver_licence_number: eg(12345678, size: 8),
|
17
|
+
children: eg([{name: 'Mary'}])
|
18
|
+
}})
|
19
|
+
|
20
|
+
eg([ {name: 'Mary'} ]) should match any array where every element has a name String
|
21
|
+
eg([ {name: example('Mary', size: 4) } ]) should match any array where every element has a 4 letter name
|
22
|
+
|
23
|
+
eg("Fred") could return Pact::Term.new(:matcher => /.+/, :generate => 'Fred')
|
24
|
+
eg(29) could return Pact::Term.new(:matcher => /\d+/, :generate => 29)
|
25
|
+
|
26
|
+
Need a way to specify a literal empty hash, rather than a hash that matches anything as {} currently does.
|
27
|
+
|
28
|
+
{:something => literal({}) }
|
29
|
+
{:something => actual({}) }
|
30
|
+
{:something => empty_hash }
|
31
|
+
|
32
|
+
Slightly unintuitive behaviour: {} matches any hash, but [] only matches an empty array (or does now we've changed the code). Should [] match any array? How do we then specify an empty array?
|
33
|
+
|
34
|
+
{:something => literal([]) }
|
35
|
+
{:something => actual([]) }
|
36
|
+
{:something => empty_array }
|