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.
- checksums.yaml +15 -0
- data/.gitignore +29 -0
- data/.rspec +2 -0
- data/.travis.yml +8 -0
- data/CHANGELOG.md +4 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +80 -0
- data/LICENSE.txt +22 -0
- data/README.md +4 -0
- data/Rakefile +4 -0
- data/lib/pact/configuration.rb +164 -0
- data/lib/pact/consumer/request.rb +27 -0
- data/lib/pact/consumer_contract.rb +1 -0
- data/lib/pact/consumer_contract/consumer_contract.rb +114 -0
- data/lib/pact/consumer_contract/file_name.rb +19 -0
- data/lib/pact/consumer_contract/headers.rb +51 -0
- data/lib/pact/consumer_contract/interaction.rb +73 -0
- data/lib/pact/consumer_contract/pact_file.rb +24 -0
- data/lib/pact/consumer_contract/request.rb +73 -0
- data/lib/pact/consumer_contract/response.rb +51 -0
- data/lib/pact/consumer_contract/service_consumer.rb +28 -0
- data/lib/pact/consumer_contract/service_provider.rb +28 -0
- data/lib/pact/logging.rb +14 -0
- data/lib/pact/matchers.rb +1 -0
- data/lib/pact/matchers/actual_type.rb +16 -0
- data/lib/pact/matchers/base_difference.rb +37 -0
- data/lib/pact/matchers/differ.rb +153 -0
- data/lib/pact/matchers/difference.rb +13 -0
- data/lib/pact/matchers/difference_indicator.rb +26 -0
- data/lib/pact/matchers/embedded_diff_formatter.rb +62 -0
- data/lib/pact/matchers/expected_type.rb +35 -0
- data/lib/pact/matchers/index_not_found.rb +15 -0
- data/lib/pact/matchers/list_diff_formatter.rb +101 -0
- data/lib/pact/matchers/matchers.rb +139 -0
- data/lib/pact/matchers/no_diff_indicator.rb +18 -0
- data/lib/pact/matchers/regexp_difference.rb +13 -0
- data/lib/pact/matchers/type_difference.rb +16 -0
- data/lib/pact/matchers/unexpected_index.rb +11 -0
- data/lib/pact/matchers/unexpected_key.rb +11 -0
- data/lib/pact/matchers/unix_diff_formatter.rb +114 -0
- data/lib/pact/reification.rb +28 -0
- data/lib/pact/rspec.rb +53 -0
- data/lib/pact/shared/active_support_support.rb +51 -0
- data/lib/pact/shared/dsl.rb +76 -0
- data/lib/pact/shared/jruby_support.rb +18 -0
- data/lib/pact/shared/json_differ.rb +15 -0
- data/lib/pact/shared/key_not_found.rb +15 -0
- data/lib/pact/shared/null_expectation.rb +31 -0
- data/lib/pact/shared/request.rb +97 -0
- data/lib/pact/shared/text_differ.rb +14 -0
- data/lib/pact/something_like.rb +49 -0
- data/lib/pact/support.rb +9 -0
- data/lib/pact/support/version.rb +5 -0
- data/lib/pact/symbolize_keys.rb +12 -0
- data/lib/pact/term.rb +85 -0
- data/lib/tasks/pact.rake +29 -0
- data/pact-support.gemspec +35 -0
- data/spec/lib/pact/consumer/request_spec.rb +25 -0
- data/spec/lib/pact/consumer_contract/active_support_support_spec.rb +58 -0
- data/spec/lib/pact/consumer_contract/consumer_contract_spec.rb +141 -0
- data/spec/lib/pact/consumer_contract/headers_spec.rb +107 -0
- data/spec/lib/pact/consumer_contract/interaction_spec.rb +151 -0
- data/spec/lib/pact/consumer_contract/request_spec.rb +329 -0
- data/spec/lib/pact/consumer_contract/response_spec.rb +73 -0
- data/spec/lib/pact/matchers/differ_spec.rb +215 -0
- data/spec/lib/pact/matchers/difference_spec.rb +22 -0
- data/spec/lib/pact/matchers/embedded_diff_formatter_spec.rb +90 -0
- data/spec/lib/pact/matchers/index_not_found_spec.rb +21 -0
- data/spec/lib/pact/matchers/list_diff_formatter_spec.rb +120 -0
- data/spec/lib/pact/matchers/matchers_spec.rb +500 -0
- data/spec/lib/pact/matchers/regexp_difference_spec.rb +20 -0
- data/spec/lib/pact/matchers/type_difference_spec.rb +34 -0
- data/spec/lib/pact/matchers/unexpected_index_spec.rb +20 -0
- data/spec/lib/pact/matchers/unexpected_key_spec.rb +20 -0
- data/spec/lib/pact/matchers/unix_diff_formatter_spec.rb +216 -0
- data/spec/lib/pact/reification_spec.rb +67 -0
- data/spec/lib/pact/shared/dsl_spec.rb +86 -0
- data/spec/lib/pact/shared/json_differ_spec.rb +36 -0
- data/spec/lib/pact/shared/key_not_found_spec.rb +20 -0
- data/spec/lib/pact/shared/request_spec.rb +196 -0
- data/spec/lib/pact/shared/text_differ_spec.rb +54 -0
- data/spec/lib/pact/something_like_spec.rb +21 -0
- data/spec/lib/pact/term_spec.rb +89 -0
- data/spec/spec_helper.rb +19 -0
- data/spec/support/a_consumer-a_producer.json +32 -0
- data/spec/support/a_consumer-a_provider.json +32 -0
- data/spec/support/active_support_if_configured.rb +6 -0
- data/spec/support/case-insensitive-response-header-matching.json +21 -0
- data/spec/support/consumer_contract_template.json +24 -0
- data/spec/support/dsl_spec_support.rb +7 -0
- data/spec/support/factories.rb +82 -0
- data/spec/support/generated_index.md +4 -0
- data/spec/support/generated_markdown.md +55 -0
- data/spec/support/interaction_view_model.json +63 -0
- data/spec/support/interaction_view_model_with_terms.json +50 -0
- data/spec/support/markdown_pact.json +48 -0
- data/spec/support/missing_provider_states_output.txt +25 -0
- data/spec/support/options.json +21 -0
- data/spec/support/shared_examples_for_request.rb +94 -0
- data/spec/support/spec_support.rb +20 -0
- data/spec/support/stubbing.json +22 -0
- data/spec/support/term.json +48 -0
- data/spec/support/test_app_fail.json +61 -0
- data/spec/support/test_app_pass.json +38 -0
- data/spec/support/test_app_with_right_content_type_differ.json +23 -0
- data/tasks/spec.rake +6 -0
- metadata +401 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
{
|
|
2
|
+
"provider": {
|
|
3
|
+
"name": "a provider"
|
|
4
|
+
},
|
|
5
|
+
"consumer": {
|
|
6
|
+
"name": "a consumer"
|
|
7
|
+
},
|
|
8
|
+
"interactions": [
|
|
9
|
+
{
|
|
10
|
+
"description": "request one",
|
|
11
|
+
"request": {
|
|
12
|
+
"method": "get",
|
|
13
|
+
"path": "/path_one"
|
|
14
|
+
},
|
|
15
|
+
"response": {
|
|
16
|
+
},
|
|
17
|
+
"provider_state": "state one"
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
"description": "request two",
|
|
21
|
+
"request": {
|
|
22
|
+
"method": "get",
|
|
23
|
+
"path": "/path_two"
|
|
24
|
+
},
|
|
25
|
+
"response": {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
],
|
|
29
|
+
"metadata": {
|
|
30
|
+
"pactSpecificationVersion": "1.0"
|
|
31
|
+
}
|
|
32
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"consumer": {
|
|
3
|
+
"name": "an easy consumer"
|
|
4
|
+
},
|
|
5
|
+
"provider": {
|
|
6
|
+
"name": "a provider which returns headers that don't match the expected case"
|
|
7
|
+
},
|
|
8
|
+
"interactions": [
|
|
9
|
+
{
|
|
10
|
+
"description": "a test request",
|
|
11
|
+
"request": {
|
|
12
|
+
"method": "get",
|
|
13
|
+
"path": "/"
|
|
14
|
+
},
|
|
15
|
+
"response": {
|
|
16
|
+
"status": 200,
|
|
17
|
+
"headers": {"Content-Type": "application/hippo"}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"provider": {
|
|
3
|
+
"name": "a provider"
|
|
4
|
+
},
|
|
5
|
+
"consumer": {
|
|
6
|
+
"name": "a consumer"
|
|
7
|
+
},
|
|
8
|
+
"interactions": [
|
|
9
|
+
{
|
|
10
|
+
"description": "request one",
|
|
11
|
+
"request": {
|
|
12
|
+
"method": "get",
|
|
13
|
+
"path": "/path_one"
|
|
14
|
+
},
|
|
15
|
+
"response": {
|
|
16
|
+
"status" : 200
|
|
17
|
+
},
|
|
18
|
+
"provider_state": "state one"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"metadata": {
|
|
22
|
+
"pactSpecificationVersion": "1.0"
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
require 'hashie'
|
|
2
|
+
require 'hashie/extensions/key_conversion'
|
|
3
|
+
|
|
4
|
+
module Pact
|
|
5
|
+
module HashUtils
|
|
6
|
+
|
|
7
|
+
class Converter < Hash
|
|
8
|
+
include Hashie::Extensions::KeyConversion
|
|
9
|
+
include Hashie::Extensions::DeepMerge
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def symbolize_keys hash
|
|
13
|
+
Hash[Converter[hash].symbolize_keys]
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def stringify_keys hash
|
|
17
|
+
Hash[Converter[hash].stringify_keys]
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def deep_merge hash1, hash2
|
|
21
|
+
Converter[hash1].deep_merge(Converter[hash2])
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class InteractionFactory
|
|
27
|
+
|
|
28
|
+
extend Pact::HashUtils
|
|
29
|
+
|
|
30
|
+
def self.create hash = {}
|
|
31
|
+
defaults = {
|
|
32
|
+
'description' => 'a description',
|
|
33
|
+
'provider_state' => 'a thing exists',
|
|
34
|
+
'request' => {
|
|
35
|
+
'path' => '/path',
|
|
36
|
+
'method' => 'get',
|
|
37
|
+
},
|
|
38
|
+
'response' => {
|
|
39
|
+
'status' => 200,
|
|
40
|
+
'body' => {a: 'response body'}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
Pact::Interaction.from_hash(stringify_keys(deep_merge(defaults, stringify_keys(hash))))
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class ConsumerContractFactory
|
|
49
|
+
extend Pact::HashUtils
|
|
50
|
+
DEFAULTS = {:consumer_name => 'consumer',
|
|
51
|
+
:provider_name => 'provider',
|
|
52
|
+
:interactions => [InteractionFactory.create]}
|
|
53
|
+
|
|
54
|
+
def self.create overrides = {}
|
|
55
|
+
options = deep_merge(symbolize_keys(DEFAULTS), symbolize_keys(overrides))
|
|
56
|
+
Pact::ConsumerContract.new({:consumer => Pact::ServiceConsumer.new(name: options[:consumer_name]),
|
|
57
|
+
:provider => Pact::ServiceProvider.new(name: options[:provider_name]),
|
|
58
|
+
:interactions => options[:interactions]})
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class ResponseFactory
|
|
65
|
+
extend Pact::HashUtils
|
|
66
|
+
DEFAULTS = {:status => 200, :body => {a: 'body'}}.freeze
|
|
67
|
+
def self.create_hash overrides = {}
|
|
68
|
+
deep_merge(DEFAULTS, overrides)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
class RequestFactory
|
|
73
|
+
extend Pact::HashUtils
|
|
74
|
+
DEFAULTS = {:path => '/path', :method => 'get', :query => 'query', :headers => {}}.freeze
|
|
75
|
+
def self.create_hash overrides = {}
|
|
76
|
+
deep_merge(DEFAULTS, overrides)
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def self.create_actual overrides = {}
|
|
80
|
+
Pact::Consumer::Request::Actual.from_hash(create_hash(overrides))
|
|
81
|
+
end
|
|
82
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
### A pact between Some Consumer and Some Provider
|
|
2
|
+
|
|
3
|
+
#### Requests from Some Consumer to Some Provider
|
|
4
|
+
|
|
5
|
+
* [A request for alligators](#a_request_for_alligators_given_alligators_exist) given alligators exist
|
|
6
|
+
|
|
7
|
+
* [A request for polar bears](#a_request_for_polar_bears)
|
|
8
|
+
|
|
9
|
+
#### Interactions
|
|
10
|
+
|
|
11
|
+
<a name="a_request_for_alligators_given_alligators_exist"></a>
|
|
12
|
+
Given **alligators exist**, upon receiving **a request for alligators** from Some Consumer, with
|
|
13
|
+
```json
|
|
14
|
+
{
|
|
15
|
+
"method": "get",
|
|
16
|
+
"path": "/alligators"
|
|
17
|
+
}
|
|
18
|
+
```
|
|
19
|
+
Some Provider will respond with:
|
|
20
|
+
```json
|
|
21
|
+
{
|
|
22
|
+
"status": 200,
|
|
23
|
+
"headers": {
|
|
24
|
+
"Content-Type": "application/json"
|
|
25
|
+
},
|
|
26
|
+
"body": {
|
|
27
|
+
"alligators": [
|
|
28
|
+
{
|
|
29
|
+
"name": "Bob",
|
|
30
|
+
"phoneNumber": "12345678"
|
|
31
|
+
}
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
```
|
|
36
|
+
<a name="a_request_for_polar_bears"></a>
|
|
37
|
+
Upon receiving **a request for polar bears** from Some Consumer, with
|
|
38
|
+
```json
|
|
39
|
+
{
|
|
40
|
+
"method": "get",
|
|
41
|
+
"path": "/polar-bears"
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
Some Provider will respond with:
|
|
45
|
+
```json
|
|
46
|
+
{
|
|
47
|
+
"status": 404,
|
|
48
|
+
"headers": {
|
|
49
|
+
"Content-Type": "application/json"
|
|
50
|
+
},
|
|
51
|
+
"body": {
|
|
52
|
+
"message": "Sorry, due to climate change, the polar bears are currently unavailable."
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"provider": {
|
|
3
|
+
"name": "a provider"
|
|
4
|
+
},
|
|
5
|
+
"consumer": {
|
|
6
|
+
"name": "a consumer"
|
|
7
|
+
},
|
|
8
|
+
"interactions": [
|
|
9
|
+
{
|
|
10
|
+
"description": "a request with a body and headers",
|
|
11
|
+
"request": {
|
|
12
|
+
"method": "get",
|
|
13
|
+
"path": "/path",
|
|
14
|
+
"query": "some=thing",
|
|
15
|
+
"headers": {
|
|
16
|
+
"key": "a header"
|
|
17
|
+
},
|
|
18
|
+
"body": {
|
|
19
|
+
"key": "a body"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"response": {}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
"description": "a request with an empty body and empty headers",
|
|
26
|
+
"request": {
|
|
27
|
+
"method": "get",
|
|
28
|
+
"path": "/",
|
|
29
|
+
"headers": {},
|
|
30
|
+
"body": {}
|
|
31
|
+
},
|
|
32
|
+
"response": {}
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"description": "a response with a body and headers",
|
|
36
|
+
"request": {
|
|
37
|
+
"method": "get",
|
|
38
|
+
"path": "/"
|
|
39
|
+
},
|
|
40
|
+
"response": {
|
|
41
|
+
"headers": {
|
|
42
|
+
"key": "a header"
|
|
43
|
+
},
|
|
44
|
+
"body": {
|
|
45
|
+
"key": "a body"
|
|
46
|
+
},
|
|
47
|
+
"status": 200
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"description": "a response with an empty body and empty headers",
|
|
52
|
+
"request": {
|
|
53
|
+
"method": "get",
|
|
54
|
+
"path": "/"
|
|
55
|
+
},
|
|
56
|
+
"response": {
|
|
57
|
+
"status": 200,
|
|
58
|
+
"headers": {},
|
|
59
|
+
"body": {}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
]
|
|
63
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"provider": {
|
|
3
|
+
"name": "a provider"
|
|
4
|
+
},
|
|
5
|
+
"consumer": {
|
|
6
|
+
"name": "a consumer"
|
|
7
|
+
},
|
|
8
|
+
"interactions": [
|
|
9
|
+
{
|
|
10
|
+
"description": "an interaction with terms",
|
|
11
|
+
"request": {
|
|
12
|
+
"method": "post",
|
|
13
|
+
"path": "/path",
|
|
14
|
+
"query": "some=thing",
|
|
15
|
+
"headers": {
|
|
16
|
+
"key": "a header"
|
|
17
|
+
},
|
|
18
|
+
"body": {
|
|
19
|
+
"term": {
|
|
20
|
+
"json_class": "Pact::Term",
|
|
21
|
+
"data": {
|
|
22
|
+
"generate": "sunny",
|
|
23
|
+
"matcher": {
|
|
24
|
+
"json_class": "Regexp",
|
|
25
|
+
"o": 0,
|
|
26
|
+
"s": "sun"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"response": {
|
|
33
|
+
"status": 200,
|
|
34
|
+
"body": {
|
|
35
|
+
"term": {
|
|
36
|
+
"json_class": "Pact::Term",
|
|
37
|
+
"data": {
|
|
38
|
+
"generate": "rainy",
|
|
39
|
+
"matcher": {
|
|
40
|
+
"json_class": "Regexp",
|
|
41
|
+
"o": 0,
|
|
42
|
+
"s": "rain"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
]
|
|
50
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
{
|
|
2
|
+
"provider": {
|
|
3
|
+
"name": "Some Provider"
|
|
4
|
+
},
|
|
5
|
+
"consumer": {
|
|
6
|
+
"name": "Some Consumer"
|
|
7
|
+
},
|
|
8
|
+
"interactions": [
|
|
9
|
+
{
|
|
10
|
+
"description": "a request for alligators",
|
|
11
|
+
"provider_state": "alligators exist",
|
|
12
|
+
"request": {
|
|
13
|
+
"method": "get",
|
|
14
|
+
"path": "/alligators"
|
|
15
|
+
},
|
|
16
|
+
"response": {
|
|
17
|
+
"headers" : {"Content-Type": "application/json"},
|
|
18
|
+
"status" : 200,
|
|
19
|
+
"body" : {
|
|
20
|
+
"alligators": [{
|
|
21
|
+
"name": "Bob",
|
|
22
|
+
"phoneNumber" : {
|
|
23
|
+
"json_class": "Pact::Term",
|
|
24
|
+
"data": {
|
|
25
|
+
"generate": "12345678",
|
|
26
|
+
"matcher": {"json_class":"Regexp","o":0,"s":"\\d+"}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
},{
|
|
33
|
+
"description": "a request for polar bears",
|
|
34
|
+
"provider_state": null,
|
|
35
|
+
"request": {
|
|
36
|
+
"method": "get",
|
|
37
|
+
"path": "/polar-bears"
|
|
38
|
+
},
|
|
39
|
+
"response": {
|
|
40
|
+
"headers" : {"Content-Type": "application/json"},
|
|
41
|
+
"status" : 404,
|
|
42
|
+
"body" : {
|
|
43
|
+
"message": "Sorry, due to climate change, the polar bears are currently unavailable."
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
]
|
|
48
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
Pact.provider_states_for "Consumer 1" do
|
|
2
|
+
|
|
3
|
+
provider_state "state1" do
|
|
4
|
+
set_up do
|
|
5
|
+
# Your set up code goes here
|
|
6
|
+
end
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
provider_state "state2" do
|
|
10
|
+
set_up do
|
|
11
|
+
# Your set up code goes here
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Pact.provider_states_for "Consumer 2" do
|
|
18
|
+
|
|
19
|
+
provider_state "state3" do
|
|
20
|
+
set_up do
|
|
21
|
+
# Your set up code goes here
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"consumer": {
|
|
3
|
+
"name": "Consumer"
|
|
4
|
+
},
|
|
5
|
+
"provider": {
|
|
6
|
+
"name": "Provider"
|
|
7
|
+
},
|
|
8
|
+
"interactions": [
|
|
9
|
+
{
|
|
10
|
+
"description": "an OPTIONS request",
|
|
11
|
+
"request": {
|
|
12
|
+
"method": "options",
|
|
13
|
+
"path": "/"
|
|
14
|
+
},
|
|
15
|
+
"response": {
|
|
16
|
+
"status": 200
|
|
17
|
+
},
|
|
18
|
+
"provider_state": null
|
|
19
|
+
}
|
|
20
|
+
]
|
|
21
|
+
}
|