pact-support 0.0.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 (107) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +29 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +8 -0
  5. data/CHANGELOG.md +4 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +80 -0
  8. data/LICENSE.txt +22 -0
  9. data/README.md +4 -0
  10. data/Rakefile +4 -0
  11. data/lib/pact/configuration.rb +164 -0
  12. data/lib/pact/consumer/request.rb +27 -0
  13. data/lib/pact/consumer_contract.rb +1 -0
  14. data/lib/pact/consumer_contract/consumer_contract.rb +114 -0
  15. data/lib/pact/consumer_contract/file_name.rb +19 -0
  16. data/lib/pact/consumer_contract/headers.rb +51 -0
  17. data/lib/pact/consumer_contract/interaction.rb +73 -0
  18. data/lib/pact/consumer_contract/pact_file.rb +24 -0
  19. data/lib/pact/consumer_contract/request.rb +73 -0
  20. data/lib/pact/consumer_contract/response.rb +51 -0
  21. data/lib/pact/consumer_contract/service_consumer.rb +28 -0
  22. data/lib/pact/consumer_contract/service_provider.rb +28 -0
  23. data/lib/pact/logging.rb +14 -0
  24. data/lib/pact/matchers.rb +1 -0
  25. data/lib/pact/matchers/actual_type.rb +16 -0
  26. data/lib/pact/matchers/base_difference.rb +37 -0
  27. data/lib/pact/matchers/differ.rb +153 -0
  28. data/lib/pact/matchers/difference.rb +13 -0
  29. data/lib/pact/matchers/difference_indicator.rb +26 -0
  30. data/lib/pact/matchers/embedded_diff_formatter.rb +62 -0
  31. data/lib/pact/matchers/expected_type.rb +35 -0
  32. data/lib/pact/matchers/index_not_found.rb +15 -0
  33. data/lib/pact/matchers/list_diff_formatter.rb +101 -0
  34. data/lib/pact/matchers/matchers.rb +139 -0
  35. data/lib/pact/matchers/no_diff_indicator.rb +18 -0
  36. data/lib/pact/matchers/regexp_difference.rb +13 -0
  37. data/lib/pact/matchers/type_difference.rb +16 -0
  38. data/lib/pact/matchers/unexpected_index.rb +11 -0
  39. data/lib/pact/matchers/unexpected_key.rb +11 -0
  40. data/lib/pact/matchers/unix_diff_formatter.rb +114 -0
  41. data/lib/pact/reification.rb +28 -0
  42. data/lib/pact/rspec.rb +53 -0
  43. data/lib/pact/shared/active_support_support.rb +51 -0
  44. data/lib/pact/shared/dsl.rb +76 -0
  45. data/lib/pact/shared/jruby_support.rb +18 -0
  46. data/lib/pact/shared/json_differ.rb +15 -0
  47. data/lib/pact/shared/key_not_found.rb +15 -0
  48. data/lib/pact/shared/null_expectation.rb +31 -0
  49. data/lib/pact/shared/request.rb +97 -0
  50. data/lib/pact/shared/text_differ.rb +14 -0
  51. data/lib/pact/something_like.rb +49 -0
  52. data/lib/pact/support.rb +9 -0
  53. data/lib/pact/support/version.rb +5 -0
  54. data/lib/pact/symbolize_keys.rb +12 -0
  55. data/lib/pact/term.rb +85 -0
  56. data/lib/tasks/pact.rake +29 -0
  57. data/pact-support.gemspec +35 -0
  58. data/spec/lib/pact/consumer/request_spec.rb +25 -0
  59. data/spec/lib/pact/consumer_contract/active_support_support_spec.rb +58 -0
  60. data/spec/lib/pact/consumer_contract/consumer_contract_spec.rb +141 -0
  61. data/spec/lib/pact/consumer_contract/headers_spec.rb +107 -0
  62. data/spec/lib/pact/consumer_contract/interaction_spec.rb +151 -0
  63. data/spec/lib/pact/consumer_contract/request_spec.rb +329 -0
  64. data/spec/lib/pact/consumer_contract/response_spec.rb +73 -0
  65. data/spec/lib/pact/matchers/differ_spec.rb +215 -0
  66. data/spec/lib/pact/matchers/difference_spec.rb +22 -0
  67. data/spec/lib/pact/matchers/embedded_diff_formatter_spec.rb +90 -0
  68. data/spec/lib/pact/matchers/index_not_found_spec.rb +21 -0
  69. data/spec/lib/pact/matchers/list_diff_formatter_spec.rb +120 -0
  70. data/spec/lib/pact/matchers/matchers_spec.rb +500 -0
  71. data/spec/lib/pact/matchers/regexp_difference_spec.rb +20 -0
  72. data/spec/lib/pact/matchers/type_difference_spec.rb +34 -0
  73. data/spec/lib/pact/matchers/unexpected_index_spec.rb +20 -0
  74. data/spec/lib/pact/matchers/unexpected_key_spec.rb +20 -0
  75. data/spec/lib/pact/matchers/unix_diff_formatter_spec.rb +216 -0
  76. data/spec/lib/pact/reification_spec.rb +67 -0
  77. data/spec/lib/pact/shared/dsl_spec.rb +86 -0
  78. data/spec/lib/pact/shared/json_differ_spec.rb +36 -0
  79. data/spec/lib/pact/shared/key_not_found_spec.rb +20 -0
  80. data/spec/lib/pact/shared/request_spec.rb +196 -0
  81. data/spec/lib/pact/shared/text_differ_spec.rb +54 -0
  82. data/spec/lib/pact/something_like_spec.rb +21 -0
  83. data/spec/lib/pact/term_spec.rb +89 -0
  84. data/spec/spec_helper.rb +19 -0
  85. data/spec/support/a_consumer-a_producer.json +32 -0
  86. data/spec/support/a_consumer-a_provider.json +32 -0
  87. data/spec/support/active_support_if_configured.rb +6 -0
  88. data/spec/support/case-insensitive-response-header-matching.json +21 -0
  89. data/spec/support/consumer_contract_template.json +24 -0
  90. data/spec/support/dsl_spec_support.rb +7 -0
  91. data/spec/support/factories.rb +82 -0
  92. data/spec/support/generated_index.md +4 -0
  93. data/spec/support/generated_markdown.md +55 -0
  94. data/spec/support/interaction_view_model.json +63 -0
  95. data/spec/support/interaction_view_model_with_terms.json +50 -0
  96. data/spec/support/markdown_pact.json +48 -0
  97. data/spec/support/missing_provider_states_output.txt +25 -0
  98. data/spec/support/options.json +21 -0
  99. data/spec/support/shared_examples_for_request.rb +94 -0
  100. data/spec/support/spec_support.rb +20 -0
  101. data/spec/support/stubbing.json +22 -0
  102. data/spec/support/term.json +48 -0
  103. data/spec/support/test_app_fail.json +61 -0
  104. data/spec/support/test_app_pass.json +38 -0
  105. data/spec/support/test_app_with_right_content_type_differ.json +23 -0
  106. data/tasks/spec.rake +6 -0
  107. metadata +401 -0
@@ -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,62 @@
1
+ require 'pact/shared/active_support_support'
2
+ require 'term/ansicolor'
3
+
4
+ module Pact
5
+ module Matchers
6
+ class EmbeddedDiffFormatter
7
+
8
+ include Pact::ActiveSupportSupport
9
+ C = ::Term::ANSIColor
10
+
11
+
12
+ EXPECTED = /"EXPECTED([A-Z_]*)":/
13
+
14
+ ACTUAL = /"ACTUAL([A-Z_]*)":/
15
+
16
+ attr_reader :diff, :colour
17
+
18
+ def initialize diff, options = {}
19
+ @diff = diff
20
+ @colour = options.fetch(:colour, false)
21
+ end
22
+
23
+ def self.call diff, options = {colour: Pact.configuration.color_enabled}
24
+ new(diff, options).call
25
+ end
26
+
27
+ def call
28
+ to_s
29
+ end
30
+
31
+ def to_hash
32
+ diff
33
+ end
34
+
35
+ def to_s
36
+ colourise_message_if_configured fix_json_formatting(diff.to_json)
37
+ end
38
+
39
+ def colourise_message_if_configured message
40
+ if colour
41
+ colourise_message message
42
+ else
43
+ message
44
+ end
45
+ end
46
+
47
+ def colourise_message message
48
+ message.split("\n").collect{| line | colourise(line) }.join("\n")
49
+ end
50
+
51
+ def colourise line
52
+ line.gsub(EXPECTED){|match| coloured_key match, :red }.gsub(ACTUAL){ | match | coloured_key match, :green }
53
+ end
54
+
55
+ def coloured_key match, colour
56
+ '"' + C.color(colour, match.downcase.gsub(/^"|":$/,'')) + '":'
57
+ end
58
+
59
+ end
60
+
61
+ end
62
+ 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,15 @@
1
+ require 'pact/matchers/difference_indicator'
2
+
3
+ module Pact
4
+ class IndexNotFound < Pact::DifferenceIndicator
5
+
6
+ def to_s
7
+ "<index not found>"
8
+ end
9
+
10
+ def empty?
11
+ true
12
+ end
13
+ end
14
+
15
+ end
@@ -0,0 +1,101 @@
1
+ module Pact
2
+ module Matchers
3
+ class ListDiffFormatter
4
+
5
+ attr_reader :diff
6
+
7
+ def initialize diff, options = {}
8
+ @diff = diff
9
+ end
10
+
11
+ def self.call diff, options = {}
12
+ new(diff, options).call
13
+ end
14
+
15
+ def to_hash
16
+ diff
17
+ end
18
+
19
+ def call
20
+ to_s
21
+ end
22
+
23
+ def to_s
24
+ diff_descriptions(diff).join("\n")
25
+ end
26
+
27
+ def diff_descriptions obj, path = [], descriptions = []
28
+ case obj
29
+ when Hash then handle_hash obj, path, descriptions
30
+ when Array then handle_array obj, path, descriptions
31
+ when Difference then handle_difference obj, path, descriptions
32
+ when TypeDifference then handle_mismatched_type obj, path, descriptions
33
+ when RegexpDifference then handle_mismatched_regexp obj, path, descriptions
34
+ when NoDiffIndicator then nil
35
+ else
36
+ raise "Invalid diff, expected Hash, Array, NoDiffIndicator or Difference, found #{obj.class}"
37
+ end
38
+ descriptions
39
+ end
40
+
41
+ def handle_hash hash, path, descriptions
42
+ hash.each_pair do | key, value |
43
+ diff_descriptions value, path + [key.inspect], descriptions
44
+ end
45
+ end
46
+
47
+ def handle_array array, path, descriptions
48
+ array.each_with_index do | obj, index |
49
+ diff_descriptions obj, path + [index], descriptions
50
+ end
51
+ end
52
+
53
+ def handle_difference difference, path, descriptions
54
+ case difference.expected
55
+ when Pact::UnexpectedKey then handle_unexpected_key(difference, path, descriptions)
56
+ when Pact::UnexpectedIndex then handle_unexpected_index(difference, path, descriptions)
57
+ else
58
+ case difference.actual
59
+ when Pact::KeyNotFound then handle_key_not_found(difference, path, descriptions)
60
+ when Pact::IndexNotFound then handle_index_not_found(difference, path, descriptions)
61
+ else
62
+ handle_mismatched_value(difference, path, descriptions)
63
+ end
64
+ end
65
+ end
66
+
67
+ def handle_unexpected_index difference, path, descriptions
68
+ descriptions << "\tAt:\n\t\t#{path_to_s(path)}\n\tArray contained unexpected item:\n\t\t#{difference.actual.ai}"
69
+ end
70
+
71
+ def handle_mismatched_value difference, path, descriptions
72
+ descriptions << "\tAt:\n\t\t#{path_to_s(path)}\n\tExpected:\n\t\t#{difference.expected.ai}\n\tActual:\n\t\t#{difference.actual.ai}"
73
+ end
74
+
75
+ def handle_mismatched_regexp difference, path, descriptions
76
+ descriptions << "\tAt:\n\t\t#{path_to_s(path)}\n\tExpected to match:\n\t\t#{difference.expected.inspect}\n\tActual:\n\t\t#{difference.actual.ai}"
77
+ end
78
+
79
+ def handle_mismatched_type difference, path, descriptions
80
+ descriptions << "\tAt:\n\t\t#{path_to_s(path)}\n\tExpected type:\n\t\t#{difference.expected}\n\tActual type:\n\t\t#{difference.actual}"
81
+ end
82
+
83
+ def handle_index_not_found difference, path, descriptions
84
+ descriptions << "\tAt:\n\t\t#{path_to_s(path)}\n\tMissing index with value:\n\t\t#{difference.expected.ai}"
85
+ end
86
+
87
+ def handle_key_not_found difference, path, descriptions
88
+ descriptions << "\tAt:\n\t\t#{path_to_s(path)}\n\tMissing key with value:\n\t\t#{difference.expected.ai}"
89
+ end
90
+
91
+ def handle_unexpected_key difference, path, descriptions
92
+ descriptions << "\tAt:\n\t\t#{path_to_s(path)}\n\tHash contained unexpected key with value:\n\t\t#{difference.actual.ai}"
93
+ end
94
+
95
+ def path_to_s path
96
+ "[" + path.join("][") + "]"
97
+ end
98
+
99
+ end
100
+ end
101
+ end
@@ -0,0 +1,139 @@
1
+ require 'awesome_print'
2
+ require 'pact/term'
3
+ require 'pact/something_like'
4
+ require 'pact/shared/null_expectation'
5
+ require 'pact/shared/key_not_found'
6
+ require 'pact/matchers/unexpected_key'
7
+ require 'pact/matchers/unexpected_index'
8
+ require 'pact/matchers/index_not_found'
9
+ require 'pact/matchers/difference'
10
+ require 'pact/matchers/regexp_difference'
11
+ require 'pact/matchers/type_difference'
12
+ require 'pact/matchers/expected_type'
13
+ require 'pact/matchers/actual_type'
14
+ require 'pact/matchers/no_diff_indicator'
15
+
16
+ module Pact
17
+ module Matchers
18
+
19
+ NO_DIFF_INDICATOR = NoDiffIndicator.new
20
+ DEFAULT_OPTIONS = {allow_unexpected_keys: true, type: false}.freeze
21
+
22
+ def diff expected, actual, opts = {}
23
+ calculate_diff(Pact::Term.unpack_regexps(expected), actual, DEFAULT_OPTIONS.merge(opts))
24
+ end
25
+
26
+ def type_diff expected, actual, opts = {}
27
+ calculate_diff Pact::Term.unpack_regexps(expected), actual, DEFAULT_OPTIONS.merge(opts).merge(type: true)
28
+ end
29
+
30
+ private
31
+
32
+ def calculate_diff expected, actual, opts = {}
33
+ options = DEFAULT_OPTIONS.merge(opts)
34
+ case expected
35
+ when Hash then hash_diff(expected, actual, options)
36
+ when Array then array_diff(expected, actual, options)
37
+ when Regexp then regexp_diff(expected, actual, options)
38
+ when Pact::SomethingLike then calculate_diff(expected.contents, actual, options.merge(:type => true))
39
+ else object_diff(expected, actual, options)
40
+ end
41
+ end
42
+
43
+ alias_method :structure_diff, :type_diff # Backwards compatibility
44
+
45
+ def regexp_diff regexp, actual, options
46
+ if actual.is_a?(String) && regexp.match(actual)
47
+ {}
48
+ else
49
+ RegexpDifference.new regexp, actual
50
+ end
51
+ end
52
+
53
+ def array_diff expected, actual, options
54
+ if actual.is_a? Array
55
+ actual_array_diff expected, actual, options
56
+ else
57
+ Difference.new expected, actual
58
+ end
59
+ end
60
+
61
+ def actual_array_diff expected, actual, options
62
+ difference = []
63
+ diff_found = false
64
+ length = [expected.length, actual.length].max
65
+ length.times do | index|
66
+ expected_item = expected.fetch(index, Pact::UnexpectedIndex.new)
67
+ actual_item = actual.fetch(index, Pact::IndexNotFound.new)
68
+ if (item_diff = calculate_diff(expected_item, actual_item, options)).any?
69
+ diff_found = true
70
+ difference << item_diff
71
+ else
72
+ difference << NO_DIFF_INDICATOR
73
+ end
74
+ end
75
+ diff_found ? difference : {}
76
+ end
77
+
78
+ def actual_hash_diff expected, actual, options
79
+ difference = expected.keys.inject({}) do |calculate_diff, key|
80
+ if (diff_at_key = calculate_diff(expected[key], actual.fetch(key, Pact::KeyNotFound.new), options)).any?
81
+ calculate_diff[key] = diff_at_key
82
+ end
83
+ calculate_diff
84
+ end
85
+ difference.merge(check_for_unexpected_keys(expected, actual, options))
86
+ end
87
+
88
+ def check_for_unexpected_keys expected, actual, options
89
+ if options[:allow_unexpected_keys]
90
+ {}
91
+ else
92
+ (actual.keys - expected.keys).inject({}) do | calculate_diff, key |
93
+ calculate_diff[key] = Difference.new(UnexpectedKey.new, actual[key])
94
+ calculate_diff
95
+ end
96
+ end
97
+ end
98
+
99
+ def hash_diff expected, actual, options
100
+ if actual.is_a? Hash
101
+ actual_hash_diff expected, actual, options
102
+ else
103
+ Difference.new expected, actual
104
+ end
105
+ end
106
+
107
+ def type_difference expected, actual
108
+ if types_match? expected, actual
109
+ {}
110
+ else
111
+ TypeDifference.new type_diff_expected_display(expected), type_diff_actual_display(actual)
112
+ end
113
+ end
114
+
115
+ def type_diff_actual_display actual
116
+ actual.is_a?(KeyNotFound) ? actual : ActualType.new(actual)
117
+ end
118
+
119
+ def type_diff_expected_display expected
120
+ ExpectedType.new(expected)
121
+ end
122
+
123
+ def types_match? expected, actual
124
+ #There must be a more elegant way to do this
125
+ expected.class == actual.class ||
126
+ (expected.is_a?(TrueClass) && actual.is_a?(FalseClass)) ||
127
+ (expected.is_a?(FalseClass) && actual.is_a?(TrueClass))
128
+ end
129
+
130
+ def object_diff expected, actual, options
131
+ return type_difference(expected, actual) if options[:type]
132
+ if expected != actual
133
+ Difference.new expected, actual
134
+ else
135
+ {}
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,18 @@
1
+ module Pact
2
+ module Matchers
3
+ class NoDiffIndicator
4
+
5
+ def to_json options = {}
6
+ to_s
7
+ end
8
+
9
+ def to_s
10
+ 'no difference here!'
11
+ end
12
+
13
+ def == other
14
+ other.is_a? NoDiffIndicator
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,13 @@
1
+ require 'pact/matchers/base_difference'
2
+
3
+ module Pact
4
+ module Matchers
5
+ class RegexpDifference < BaseDifference
6
+
7
+ def as_json options = {}
8
+ {:EXPECTED_TO_MATCH => expected.inspect, :ACTUAL => actual}
9
+ end
10
+
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,16 @@
1
+ require 'pact/matchers/base_difference'
2
+
3
+ module Pact
4
+ module Matchers
5
+ class TypeDifference < BaseDifference
6
+
7
+ def as_json options = {}
8
+ if KeyNotFound === actual
9
+ {:EXPECTED_TYPE => expected.as_json, :ACTUAL => actual.as_json }
10
+ else
11
+ {:EXPECTED_TYPE => expected.as_json, :ACTUAL_TYPE => actual.as_json }
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end