hs-pact-support 1.17.1

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.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +620 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +5 -0
  5. data/lib/pact/array_like.rb +49 -0
  6. data/lib/pact/configuration.rb +193 -0
  7. data/lib/pact/consumer/request.rb +27 -0
  8. data/lib/pact/consumer_contract/consumer_contract.rb +97 -0
  9. data/lib/pact/consumer_contract/file_name.rb +22 -0
  10. data/lib/pact/consumer_contract/headers.rb +51 -0
  11. data/lib/pact/consumer_contract/http_consumer_contract_parser.rb +37 -0
  12. data/lib/pact/consumer_contract/interaction.rb +81 -0
  13. data/lib/pact/consumer_contract/interaction_parser.rb +23 -0
  14. data/lib/pact/consumer_contract/interaction_v2_parser.rb +57 -0
  15. data/lib/pact/consumer_contract/interaction_v3_parser.rb +92 -0
  16. data/lib/pact/consumer_contract/pact_file.rb +157 -0
  17. data/lib/pact/consumer_contract/provider_state.rb +34 -0
  18. data/lib/pact/consumer_contract/query.rb +138 -0
  19. data/lib/pact/consumer_contract/query_hash.rb +89 -0
  20. data/lib/pact/consumer_contract/query_string.rb +51 -0
  21. data/lib/pact/consumer_contract/request.rb +83 -0
  22. data/lib/pact/consumer_contract/response.rb +58 -0
  23. data/lib/pact/consumer_contract/service_consumer.rb +28 -0
  24. data/lib/pact/consumer_contract/service_provider.rb +28 -0
  25. data/lib/pact/consumer_contract/string_with_matching_rules.rb +17 -0
  26. data/lib/pact/consumer_contract.rb +1 -0
  27. data/lib/pact/errors.rb +21 -0
  28. data/lib/pact/helpers.rb +60 -0
  29. data/lib/pact/http/authorization_header_redactor.rb +32 -0
  30. data/lib/pact/logging.rb +14 -0
  31. data/lib/pact/matchers/actual_type.rb +16 -0
  32. data/lib/pact/matchers/base_difference.rb +39 -0
  33. data/lib/pact/matchers/differ.rb +153 -0
  34. data/lib/pact/matchers/difference.rb +13 -0
  35. data/lib/pact/matchers/difference_indicator.rb +26 -0
  36. data/lib/pact/matchers/embedded_diff_formatter.rb +60 -0
  37. data/lib/pact/matchers/expected_type.rb +35 -0
  38. data/lib/pact/matchers/extract_diff_messages.rb +76 -0
  39. data/lib/pact/matchers/index_not_found.rb +15 -0
  40. data/lib/pact/matchers/list_diff_formatter.rb +103 -0
  41. data/lib/pact/matchers/matchers.rb +285 -0
  42. data/lib/pact/matchers/multipart_form_diff_formatter.rb +41 -0
  43. data/lib/pact/matchers/no_diff_at_index.rb +18 -0
  44. data/lib/pact/matchers/regexp_difference.rb +13 -0
  45. data/lib/pact/matchers/type_difference.rb +16 -0
  46. data/lib/pact/matchers/unexpected_index.rb +11 -0
  47. data/lib/pact/matchers/unexpected_key.rb +11 -0
  48. data/lib/pact/matchers/unix_diff_formatter.rb +157 -0
  49. data/lib/pact/matchers.rb +1 -0
  50. data/lib/pact/matching_rules/extract.rb +91 -0
  51. data/lib/pact/matching_rules/jsonpath.rb +58 -0
  52. data/lib/pact/matching_rules/merge.rb +125 -0
  53. data/lib/pact/matching_rules/v3/extract.rb +94 -0
  54. data/lib/pact/matching_rules/v3/merge.rb +141 -0
  55. data/lib/pact/matching_rules.rb +30 -0
  56. data/lib/pact/reification.rb +56 -0
  57. data/lib/pact/rspec.rb +51 -0
  58. data/lib/pact/shared/active_support_support.rb +65 -0
  59. data/lib/pact/shared/dsl.rb +76 -0
  60. data/lib/pact/shared/form_differ.rb +32 -0
  61. data/lib/pact/shared/jruby_support.rb +18 -0
  62. data/lib/pact/shared/json_differ.rb +10 -0
  63. data/lib/pact/shared/key_not_found.rb +15 -0
  64. data/lib/pact/shared/multipart_form_differ.rb +16 -0
  65. data/lib/pact/shared/null_expectation.rb +31 -0
  66. data/lib/pact/shared/request.rb +106 -0
  67. data/lib/pact/shared/text_differ.rb +11 -0
  68. data/lib/pact/something_like.rb +49 -0
  69. data/lib/pact/specification_version.rb +18 -0
  70. data/lib/pact/support/version.rb +5 -0
  71. data/lib/pact/support.rb +12 -0
  72. data/lib/pact/symbolize_keys.rb +13 -0
  73. data/lib/pact/term.rb +85 -0
  74. data/lib/tasks/pact.rake +29 -0
  75. metadata +327 -0
@@ -0,0 +1,28 @@
1
+ require 'pact/symbolize_keys'
2
+
3
+ module Pact
4
+ class ServiceConsumer
5
+ include SymbolizeKeys
6
+
7
+ attr_accessor :name
8
+ def initialize options
9
+ @name = options[:name]
10
+ end
11
+
12
+ def to_s
13
+ name
14
+ end
15
+
16
+ def to_hash
17
+ {name: name}
18
+ end
19
+
20
+ def as_json options = {}
21
+ to_hash
22
+ end
23
+
24
+ def self.from_hash hash
25
+ new(symbolize_keys(hash))
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,28 @@
1
+ require 'pact/symbolize_keys'
2
+
3
+ module Pact
4
+ class ServiceProvider
5
+ include SymbolizeKeys
6
+
7
+ attr_accessor :name
8
+ def initialize options
9
+ @name = options[:name] || '[provider name unknown - please update the pact gem in the consumer project to the latest version and regenerate the pacts]'
10
+ end
11
+
12
+ def to_s
13
+ name
14
+ end
15
+
16
+ def to_hash
17
+ {name: name}
18
+ end
19
+
20
+ def as_json options = {}
21
+ to_hash
22
+ end
23
+
24
+ def self.from_hash hash
25
+ new(symbolize_keys(hash))
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,17 @@
1
+ module Pact
2
+ class StringWithMatchingRules < String
3
+ attr_reader :matching_rules
4
+ attr_reader :pact_specification_version
5
+
6
+ def initialize string, pact_specification_version, matching_rules = {}
7
+ super(string)
8
+ @matching_rules = matching_rules
9
+ @pact_specification_version = pact_specification_version
10
+ end
11
+
12
+ # How can we show the matching rules too?
13
+ def to_s
14
+ super
15
+ end
16
+ end
17
+ end
@@ -0,0 +1 @@
1
+ require 'pact/consumer_contract/consumer_contract'
@@ -0,0 +1,21 @@
1
+ module Pact
2
+ class Error < ::StandardError
3
+ end
4
+
5
+ # Raised when the interaction is not defined correctly
6
+ class InvalidInteractionError < Error
7
+ def initialize(interaction)
8
+ super(build_message(interaction))
9
+ end
10
+
11
+ private
12
+
13
+ def build_message(interaction)
14
+ missing_attributes = []
15
+ missing_attributes << :description unless interaction.description
16
+ missing_attributes << :request unless interaction.request
17
+ missing_attributes << :response unless interaction.response
18
+ "Missing attributes: #{missing_attributes}"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,60 @@
1
+ require 'pact/something_like'
2
+ require 'pact/term'
3
+ require 'pact/array_like'
4
+
5
+ # Protected, exposed through Pact.term and Pact.like, and included in Pact::Consumer::RSpec
6
+
7
+ module Pact
8
+ module Helpers
9
+
10
+ def self.included(base)
11
+ base.extend(self)
12
+ end
13
+
14
+ def term arg1, arg2 = nil
15
+ case arg1
16
+ when Hash then Pact::Term.new(arg1)
17
+ when Regexp then Pact::Term.new(matcher: arg1, generate: arg2)
18
+ when String then Pact::Term.new(matcher: arg2, generate: arg1)
19
+ else
20
+ raise ArgumentError, "Cannot create a Pact::Term from arguments #{arg1.inspect} and #{arg2.inspect}. Please provide a Regexp and a String."
21
+ end
22
+ end
23
+
24
+ def like content
25
+ Pact::SomethingLike.new(content)
26
+ end
27
+
28
+ def each_like content, options = {}
29
+ Pact::ArrayLike.new(content, options)
30
+ end
31
+
32
+ def like_uuid uuid
33
+ Pact::Term.new(generate: uuid, matcher: /^[0-9a-f]{8}(-[0-9a-f]{4}){3}-[0-9a-f]{12}$/)
34
+ end
35
+
36
+ def like_datetime datetime
37
+ Pact::Term.new(generate: datetime, matcher: /^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)$/)
38
+ end
39
+
40
+ def like_datetime_with_milliseconds datetime
41
+ Pact::Term.new(generate: datetime, matcher: /^\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d{3}([+-][0-2]\d:[0-5]\d|Z)$/)
42
+ end
43
+
44
+ alias_method :like_datetime_with_miliseconds, :like_datetime_with_milliseconds
45
+
46
+ def like_date date
47
+ Pact::Term.new(generate: date, matcher: /^\d{4}-[01]\d-[0-3]\d$/)
48
+ end
49
+
50
+ def like_datetime_rfc822 datetime
51
+ Pact::Term.new(
52
+ generate: datetime,
53
+ matcher: /(?x)(Mon|Tue|Wed|Thu|Fri|Sat|Sun),
54
+ \s\d{2}\s
55
+ (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)
56
+ \s\d{4}\s\d{2}:\d{2}:\d{2}\s(\+|-)\d{4}/
57
+ )
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,32 @@
1
+ require "delegate"
2
+
3
+ module Pact
4
+ module Http
5
+ class AuthorizationHeaderRedactor < SimpleDelegator
6
+ def puts(*args)
7
+ __getobj__().puts(*redact_args(args))
8
+ end
9
+
10
+ def print(*args)
11
+ __getobj__().puts(*redact_args(args))
12
+ end
13
+
14
+ def <<(*args)
15
+ __getobj__().send(:<<, *redact_args(args))
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :redactions
21
+
22
+ def redact_args(args)
23
+ args.collect{ | s| redact(s) }
24
+ end
25
+
26
+ def redact(string)
27
+ return string unless string.is_a?(String)
28
+ string.gsub(/Authorization: .*\\r\\n/, "Authorization: [redacted]\\r\\n")
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,14 @@
1
+ require 'logger'
2
+ require 'pact/configuration'
3
+
4
+ module Pact
5
+ module Logging
6
+ def self.included(base)
7
+ base.extend(self)
8
+ end
9
+
10
+ def logger
11
+ Pact.configuration.logger
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ require 'pact/matchers/expected_type'
2
+
3
+
4
+ module Pact
5
+ class ActualType < Pact::ExpectedType
6
+
7
+ def initialize value
8
+ @value = value
9
+ end
10
+
11
+ def to_s
12
+ type
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,39 @@
1
+ require 'pact/matchers/expected_type'
2
+ require 'pact/matchers/actual_type'
3
+
4
+ module Pact
5
+ module Matchers
6
+ class BaseDifference
7
+
8
+ attr_reader :expected, :actual, :message
9
+ attr_writer :message
10
+
11
+ def initialize expected, actual, message = nil
12
+ @expected = expected
13
+ @actual = actual
14
+ @message = message
15
+ end
16
+
17
+ def any?
18
+ true
19
+ end
20
+
21
+ def empty?
22
+ false
23
+ end
24
+
25
+ def to_json options = {}
26
+ as_json.to_json(options)
27
+ end
28
+
29
+ def to_s
30
+ as_json.to_s
31
+ end
32
+
33
+ def == other
34
+ other.class == self.class && other.expected == expected && other.actual == actual
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,153 @@
1
+ # Ripped from RSpec::Expectations::Differ in rspec/expectations/differ.rb in rspec-expectations 2.14.3
2
+ # Thank you kindly to the original author.
3
+ # Needed to be able to turn the colour off, but can't set RSpec.configuration.color to false
4
+ # once it has been set to true due to a "if bool" at the start of the color= method
5
+
6
+ require 'diff/lcs'
7
+ require 'diff/lcs/hunk'
8
+ require 'pp'
9
+
10
+ module Pact
11
+ module Matchers
12
+ class Differ
13
+
14
+ def initialize(color = false)
15
+ @color = color
16
+ end
17
+
18
+ # This is snagged from diff/lcs/ldiff.rb (which is a commandline tool)
19
+ def diff_as_string(input_data_new, input_data_old)
20
+ output = matching_encoding("", input_data_old)
21
+ data_old = input_data_old.split(matching_encoding("\n", input_data_old)).map! { |e| e.chomp }
22
+ data_new = input_data_new.split(matching_encoding("\n", input_data_new)).map! { |e| e.chomp }
23
+ diffs = Diff::LCS.diff(data_old, data_new)
24
+ return output if diffs.empty?
25
+ oldhunk = hunk = nil
26
+ file_length_difference = 0
27
+ diffs.each do |piece|
28
+ begin
29
+ hunk = Diff::LCS::Hunk.new(
30
+ data_old, data_new, piece, context_lines, file_length_difference
31
+ )
32
+ file_length_difference = hunk.file_length_difference
33
+ next unless oldhunk
34
+ # Hunks may overlap, which is why we need to be careful when our
35
+ # diff includes lines of context. Otherwise, we might print
36
+ # redundant lines.
37
+ if (context_lines > 0) and hunk.overlaps?(oldhunk)
38
+ if hunk.respond_to?(:merge)
39
+ # diff-lcs 1.2.x
40
+ hunk.merge(oldhunk)
41
+ else
42
+ # diff-lcs 1.1.3
43
+ hunk.unshift(oldhunk)
44
+ end
45
+ else
46
+ output << matching_encoding(oldhunk.diff(format).to_s, output)
47
+ end
48
+ ensure
49
+ oldhunk = hunk
50
+ output << matching_encoding("\n", output)
51
+ end
52
+ end
53
+ #Handle the last remaining hunk
54
+ output << matching_encoding(oldhunk.diff(format).to_s, output)
55
+ output << matching_encoding("\n", output)
56
+ color_diff output
57
+ rescue Encoding::CompatibilityError
58
+ if input_data_new.encoding != input_data_old.encoding
59
+ "Could not produce a diff because the encoding of the actual string (#{input_data_old.encoding}) "+
60
+ "differs from the encoding of the expected string (#{input_data_new.encoding})"
61
+ else
62
+ "Could not produce a diff because of the encoding of the string (#{input_data_old.encoding})"
63
+ end
64
+ end
65
+
66
+ def diff_as_object(actual, expected)
67
+ actual_as_string = object_to_string(actual)
68
+ expected_as_string = object_to_string(expected)
69
+ if diff = diff_as_string(actual_as_string, expected_as_string)
70
+ color_diff diff
71
+ end
72
+ end
73
+
74
+ def red(text)
75
+ return text unless @color
76
+ color(text, 31)
77
+ end
78
+
79
+ def green(text)
80
+ return text unless @color
81
+ color(text, 32)
82
+ end
83
+
84
+ protected
85
+
86
+ def format
87
+ :unified
88
+ end
89
+
90
+ def context_lines
91
+ 3
92
+ end
93
+
94
+ def color(text, color_code)
95
+ "\e[#{color_code}m#{text}\e[0m"
96
+ end
97
+
98
+
99
+ def blue(text)
100
+ color(text, 34)
101
+ end
102
+
103
+ def color_diff(diff)
104
+ return diff unless @color
105
+
106
+ diff.lines.map { |line|
107
+ case line[0].chr
108
+ when "+"
109
+ green line
110
+ when "-"
111
+ red line
112
+ when "@"
113
+ line[1].chr == "@" ? blue(line) : line
114
+ else
115
+ line
116
+ end
117
+ }.join
118
+ end
119
+
120
+ def object_to_string(object)
121
+ case object
122
+ when Hash
123
+ object.keys.sort_by { |k| k.to_s }.map do |key|
124
+ pp_key = PP.singleline_pp(key, "")
125
+ pp_value = PP.singleline_pp(object[key], "")
126
+
127
+ # on 1.9.3 PP seems to minimise to US-ASCII, ensure we're matching source encoding
128
+ #
129
+ # note, PP is used to ensure the ordering of the internal values of key/value e.g.
130
+ # <# a: b: c:> not <# c: a: b:>
131
+ matching_encoding("#{pp_key} => #{pp_value}", key.to_s)
132
+ end.join(",\n")
133
+ when String
134
+ object =~ /\n/ ? object : object.inspect
135
+ else
136
+ PP.pp(object,"")
137
+ end
138
+ end
139
+
140
+ if String.method_defined?(:encoding)
141
+ def matching_encoding(string, source)
142
+ string.encode(source.encoding)
143
+ end
144
+ else
145
+ def matching_encoding(string, source)
146
+ string
147
+ end
148
+ end
149
+ end
150
+
151
+ end
152
+ end
153
+
@@ -0,0 +1,13 @@
1
+ require 'pact/matchers/base_difference'
2
+
3
+ module Pact
4
+ module Matchers
5
+ class Difference < BaseDifference
6
+
7
+ def as_json options = {}
8
+ {:EXPECTED => expected, :ACTUAL => actual}
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,26 @@
1
+ require 'pact/shared/active_support_support'
2
+
3
+ module Pact
4
+ class DifferenceIndicator
5
+
6
+ include ActiveSupportSupport
7
+
8
+ def == other
9
+ other.class == self.class
10
+ end
11
+
12
+ def eql? other
13
+ self == other
14
+ end
15
+
16
+ def to_json options = {}
17
+ remove_unicode as_json.to_json(options)
18
+ end
19
+
20
+ def as_json options = {}
21
+ to_s
22
+ end
23
+
24
+ end
25
+
26
+ end
@@ -0,0 +1,60 @@
1
+ require 'pact/shared/active_support_support'
2
+ require 'rainbow'
3
+
4
+ module Pact
5
+ module Matchers
6
+ class EmbeddedDiffFormatter
7
+
8
+ include Pact::ActiveSupportSupport
9
+
10
+ EXPECTED = /"EXPECTED([A-Z_]*)":/
11
+
12
+ ACTUAL = /"ACTUAL([A-Z_]*)":/
13
+
14
+ attr_reader :diff, :colour
15
+
16
+ def initialize diff, options = {}
17
+ @diff = diff
18
+ @colour = options.fetch(:colour, false)
19
+ end
20
+
21
+ def self.call diff, options = {colour: Pact.configuration.color_enabled}
22
+ new(diff, options).call
23
+ end
24
+
25
+ def call
26
+ to_s
27
+ end
28
+
29
+ def to_hash
30
+ diff
31
+ end
32
+
33
+ def to_s
34
+ colourise_message_if_configured fix_json_formatting(diff.to_json)
35
+ end
36
+
37
+ def colourise_message_if_configured message
38
+ if colour
39
+ colourise_message message
40
+ else
41
+ message
42
+ end
43
+ end
44
+
45
+ def colourise_message message
46
+ message.split("\n").collect{| line | colourise(line) }.join("\n")
47
+ end
48
+
49
+ def colourise line
50
+ line.gsub(EXPECTED){|match| coloured_key match, :red }.gsub(ACTUAL){ | match | coloured_key match, :green }
51
+ end
52
+
53
+ def coloured_key match, colour
54
+ '"' + Rainbow(match.downcase.gsub(/^"|":$/,'')).color(colour) + '":'
55
+ end
56
+
57
+ end
58
+
59
+ end
60
+ end
@@ -0,0 +1,35 @@
1
+ require 'pact/matchers/difference_indicator'
2
+
3
+ module Pact
4
+ class ExpectedType < Pact::DifferenceIndicator
5
+
6
+ def initialize value
7
+ @value = value
8
+ end
9
+
10
+ def type
11
+ @value.class.name
12
+ end
13
+
14
+ def to_json options = {}
15
+ type
16
+ end
17
+
18
+ def as_json options = {}
19
+ type
20
+ end
21
+
22
+ def eq? other
23
+ self.class == other.class && other.type == type
24
+ end
25
+
26
+ def == other
27
+ eq? other
28
+ end
29
+
30
+ def to_s
31
+ type
32
+ end
33
+
34
+ end
35
+ end
@@ -0,0 +1,76 @@
1
+ # @api public. Used by lib/pact/provider/rspec/pact_broker_formatter.rb
2
+ module Pact
3
+ module Matchers
4
+ class ExtractDiffMessages
5
+
6
+ attr_reader :diff
7
+
8
+ def initialize diff, options = {}
9
+ @diff = diff
10
+ end
11
+
12
+ def self.call diff, options = {}
13
+ new(diff, options).call
14
+ end
15
+
16
+ def to_hash
17
+ to_a
18
+ end
19
+
20
+ def call
21
+ to_a
22
+ end
23
+
24
+ def to_s
25
+ diff_messages(diff).join("\n")
26
+ end
27
+
28
+ def to_a
29
+ diff_messages(diff)
30
+ end
31
+
32
+ def diff_messages obj, path = [], messages = []
33
+ case obj
34
+ when Hash then handle_hash obj, path, messages
35
+ when Array then handle_array obj, path, messages
36
+ when BaseDifference then handle_difference obj, path, messages
37
+ when NoDiffAtIndex then nil
38
+ else
39
+ raise "Invalid diff, expected Hash, Array, NoDiffAtIndex or BaseDifference, found #{obj.class}"
40
+ end
41
+ messages
42
+ end
43
+
44
+ def handle_hash hash, path, messages
45
+ hash.each_pair do | key, value |
46
+ next_part = key =~ /\s/ ? key.inspect : key
47
+ diff_messages value, path + [".#{next_part}"], messages
48
+ end
49
+ end
50
+
51
+ def handle_array array, path, messages
52
+ array.each_with_index do | obj, index |
53
+ diff_messages obj, path + ["[#{index}]"], messages
54
+ end
55
+ end
56
+
57
+ def handle_difference difference, path, messages
58
+ if difference.message
59
+ message = difference.message
60
+ message = message.gsub("<path>", path_to_s(path))
61
+ message = message.gsub("<parent_path>", parent_path_to_s(path))
62
+ messages << message
63
+ end
64
+ end
65
+
66
+ def path_to_s path
67
+ "$" + path.join
68
+ end
69
+
70
+ def parent_path_to_s path
71
+ path_to_s(path[0..-2])
72
+ end
73
+
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,15 @@
1
+ require 'pact/matchers/difference_indicator'
2
+
3
+ module Pact
4
+ class IndexNotFound < Pact::DifferenceIndicator
5
+
6
+ def to_s
7
+ "<item not found>"
8
+ end
9
+
10
+ def empty?
11
+ true
12
+ end
13
+ end
14
+
15
+ end