hs-pact-support 1.17.1

Sign up to get free protection for your applications and to get access to all the features.
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