Skip to content

Commit 8eca1e5

Browse files
committed
feat: add support for generate_from_provider_state
1 parent dd051e4 commit 8eca1e5

File tree

9 files changed

+159
-80
lines changed

9 files changed

+159
-80
lines changed

documentation/README_V2.md

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -172,14 +172,23 @@ For details of the implementation, see [matchers.rb](../lib/pact/v2/generators.r
172172
- `generate_time(format: nil)` - Generates a time string in the specified `format`.
173173
- `generate_datetime(format: nil)` - Generates a datetime string in the specified `format`.
174174
- `generate_random_boolean` - Generates a random boolean value (`true` or `false`).
175-
- `generate_from_provider_state(expression:)` - Generates a value from the provider state using the given `expression`.
175+
- `generate_from_provider_state(expression:, example:)` - Generates a value from the provider state using the given `expression` and `example` value. Allows templating of url and query paths with values only know at provider verification time.
176176
- `generate_mock_server_url(regex: nil, example: nil)` - Generates a mock server URL. Optionally, specify a `regex` matches and/or an `example` value.
177177

178178
These generators can be used in your DSL definitions to provide dynamic values for requests, responses, or messages in your contract tests.
179179

180180
#### Generator Examples
181181

182182
```rb
183+
.with_request(
184+
method: :get,
185+
path: generate_from_provider_state(
186+
expression: '/alligators/${alligator_name}',
187+
example: '/alligators/Mary'),
188+
headers: headers)
189+
190+
...
191+
183192
body: {
184193
_links: {
185194
:'pf:publish-provider-contract' => {
@@ -433,30 +442,30 @@ The following projects were designed for pact-ruby-v1 and have been migrated to
433442

434443
- pact broker client
435444
- v1
436-
- v2 https://github.com/YOU54F/pact_broker-client/pull/1
445+
- v2 <https://github.com/YOU54F/pact_broker-client/pull/1>
437446
- pact broker
438447
- v1
439-
- v2 https://github.com/YOU54F/pact_broker/pull/14
448+
- v2 <https://github.com/YOU54F/pact_broker/pull/14>
440449
- animal service
441450
- v1
442451
- In repo: [example/animal-service](../example/animal-service/)
443-
- Standalone: https://github.com/safdotdev/animal-service
452+
- Standalone: <https://github.com/safdotdev/animal-service>
444453
- v2
445454
- In repo: [example/animal-service-v2](../example/animal-service-v2/)
446-
- Standalone: https://github.com/safdotdev/animal-service/pull/1
455+
- Standalone: <https://github.com/safdotdev/animal-service/pull/1>
447456
- zoo app
448457
- v1
449458
- In repo: [example/zoo-app](../example/zoo-app/)
450-
- Standalone: https://github.com/safdotdev/zoo-app
459+
- Standalone: <https://github.com/safdotdev/zoo-app>
451460
- v2
452461
- In repo: [example/zoo-app-v2](../example/zoo-app-v2/)
453-
- Standalone: https://github.com/safdotdev/zoo-app/pull/1
462+
- Standalone: <https://github.com/safdotdev/zoo-app/pull/1>
454463
- message consumer/provider
455464
- v1
456-
- v2 https://github.com/safdotdev/pact-ruby-demo/compare/main...safdotdev:pact-ruby-demo:feat/pact-ruby-v2
465+
- v2 <https://github.com/safdotdev/pact-ruby-demo/compare/main...safdotdev:pact-ruby-demo:feat/pact-ruby-v2>
457466
- e2e http consumer/provider
458467
- v1
459-
- v2 https://github.com/safdotdev/pact-ruby-e2e-example/pull/1
468+
- v2 <https://github.com/safdotdev/pact-ruby-e2e-example/pull/1>
460469

461470
### Demos
462471

example/animal-service-v2/Rakefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,5 +8,5 @@ require 'rspec/core/rake_task'
88

99
RSpec::Core::RakeTask.new('pact:v2:verify') do |task|
1010
task.pattern = 'spec/pact/consumers/*_spec.rb'
11-
task.rspec_opts = ['-t pact', '--require rails_helper']
11+
task.rspec_opts = ['-t pact_v2', '--require rails_helper']
1212
end

example/animal-service-v2/spec/pact/consumers/http_spec.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
include RSpec::Mocks::ExampleMethods
1010

1111
RSpec.describe 'Verify consumers for Bar Provider', :pact_v2 do
12-
http_pact_provider 'Animal Service', opts: {
12+
http_pact_provider 'Animal Service', opts: {
1313
pact_dir: File.expand_path('../../../../zoo-app-v2/spec/pacts', __dir__),
1414
http_port: 9292,
1515
app: AnimalService::Api
@@ -25,6 +25,12 @@
2525
end
2626
end
2727

28+
provider_state 'there is an alligator named {alligator_name}' do
29+
set_up do |params|
30+
AnimalService::DATABASE[:animals].insert(name: params['alligator_name'])
31+
end
32+
end
33+
2834
provider_state 'there is not an alligator named Mary' do
2935
set_up do
3036
AnimalService::DATABASE[:animals].truncate

example/zoo-app-v2/Rakefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ end
1313

1414
RSpec::Core::RakeTask.new('spec:v2') do |task|
1515
task.pattern = 'spec/pact/providers/**/*_spec.rb'
16-
task.rspec_opts = ['-t pact', '--require rails_helper']
16+
task.rspec_opts = ['-t pact_v2', '--require rails_helper']
1717
end
1818

1919
task :default => :spec

example/zoo-app-v2/spec/pact/providers/animal_service/animal_service_client_spec.rb

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
require 'zoo_app/animal_service_client'
33

44
RSpec.describe 'ZooApp::AnimalServiceClient', :pact_v2 do
5-
has_http_pact_between 'Zoo App', 'Animal Service', opts: { pact_specification: 'V2' }
5+
has_http_pact_between 'Zoo App', 'Animal Service', opts: { pact_specification: 'V4' }
66

77
subject { ZooApp::AnimalServiceClient }
88

@@ -18,9 +18,10 @@
1818
context 'when an alligator by the given name exists' do
1919
let(:interaction) do
2020
super()
21-
.given("there is an alligator named #{alligator_name}")
21+
.given('there is an alligator named {alligator_name}', { alligator_name: alligator_name })
2222
.upon_receiving('a request for an alligator')
23-
.with_request(method: :get, path: "/alligators/#{alligator_name}", headers: headers)
23+
.with_request(method: :get, path: generate_from_provider_state(expression: '/alligators/${alligator_name}',
24+
example: '/alligators/Mary'), headers: headers)
2425
.will_respond_with(status: 200, body: alligator_body, headers: content_headers)
2526
end
2627

example/zoo-app-v2/spec/pacts/Zoo App-Animal Service.json

Lines changed: 75 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -5,70 +5,128 @@
55
"interactions": [
66
{
77
"description": "a request for an alligator",
8-
"providerState": "an error occurs retrieving an alligator",
8+
"pending": false,
9+
"providerStates": [
10+
{
11+
"name": "an error occurs retrieving an alligator"
12+
}
13+
],
914
"request": {
1015
"headers": {
11-
"Accept": "application/json"
16+
"Accept": [
17+
"application/json"
18+
]
1219
},
1320
"method": "GET",
1421
"path": "/alligators/Mary"
1522
},
1623
"response": {
1724
"body": {
18-
"error": "Argh!!!"
25+
"content": {
26+
"error": "Argh!!!"
27+
},
28+
"contentType": "application/json",
29+
"encoded": false
1930
},
2031
"headers": {
21-
"Content-Type": "application/json;charset=utf-8"
32+
"Content-Type": [
33+
"application/json;charset=utf-8"
34+
]
2235
},
2336
"status": 500
24-
}
37+
},
38+
"transport": "http",
39+
"type": "Synchronous/HTTP"
2540
},
2641
{
2742
"description": "a request for an alligator",
28-
"providerState": "there is an alligator named Mary",
43+
"pending": false,
44+
"providerStates": [
45+
{
46+
"name": "there is an alligator named {alligator_name}",
47+
"params": {
48+
"alligator_name": "Mary"
49+
}
50+
}
51+
],
2952
"request": {
53+
"generators": {
54+
"path": {
55+
"expression": "/alligators/${alligator_name}",
56+
"type": "ProviderState"
57+
}
58+
},
3059
"headers": {
31-
"Accept": "application/json"
60+
"Accept": [
61+
"application/json"
62+
]
63+
},
64+
"matchingRules": {
65+
"path": {
66+
"combine": "AND",
67+
"matchers": [
68+
{
69+
"match": "type"
70+
}
71+
]
72+
}
3273
},
3374
"method": "GET",
3475
"path": "/alligators/Mary"
3576
},
3677
"response": {
3778
"body": {
38-
"name": "Mary"
79+
"content": {
80+
"name": "Mary"
81+
},
82+
"contentType": "application/json",
83+
"encoded": false
3984
},
4085
"headers": {
41-
"Content-Type": "application/json;charset=utf-8"
86+
"Content-Type": [
87+
"application/json;charset=utf-8"
88+
]
4289
},
4390
"status": 200
44-
}
91+
},
92+
"transport": "http",
93+
"type": "Synchronous/HTTP"
4594
},
4695
{
4796
"description": "a request for an alligator",
48-
"providerState": "there is not an alligator named Mary",
97+
"pending": false,
98+
"providerStates": [
99+
{
100+
"name": "there is not an alligator named Mary"
101+
}
102+
],
49103
"request": {
50104
"headers": {
51-
"Accept": "application/json"
105+
"Accept": [
106+
"application/json"
107+
]
52108
},
53109
"method": "GET",
54110
"path": "/alligators/Mary"
55111
},
56112
"response": {
57113
"status": 404
58-
}
114+
},
115+
"transport": "http",
116+
"type": "Synchronous/HTTP"
59117
}
60118
],
61119
"metadata": {
120+
"pact-ruby-v2": {
121+
"pact-ffi": "0.4.28"
122+
},
62123
"pactRust": {
63124
"ffi": "0.4.28",
64125
"mockserver": "1.2.16",
65126
"models": "1.3.5"
66127
},
67128
"pactSpecification": {
68-
"version": "2.0.0"
69-
},
70-
"pact-ruby-v2": {
71-
"pact-ffi": "0.4.28"
129+
"version": "4.0"
72130
}
73131
},
74132
"provider": {

lib/pact/v2/generators.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ def generate_random_boolean
3737
Pact::V2::Generators::RandomBooleanGenerator.new
3838
end
3939

40-
def generate_from_provider_state(expression:)
41-
Pact::V2::Generators::ProviderStateGenerator.new(expression: expression)
40+
def generate_from_provider_state(expression:, example:)
41+
Pact::V2::Generators::ProviderStateGenerator.new(expression: expression, example: example).as_basic
4242
end
4343

4444
def generate_mock_server_url(regex: nil, example: nil)

lib/pact/v2/generators/base.rb

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -249,14 +249,17 @@ def as_basic
249249
class ProviderStateGenerator
250250
include Base
251251

252-
def initialize(expression:)
252+
def initialize(expression:, example:)
253253
@expression = expression
254+
@value = example
254255
end
255256

256257
def as_basic
257258
{
259+
'pact:matcher:type': 'type',
258260
"pact:generator:type" => "ProviderState",
259-
"expression" => @expression
261+
"expression" => @expression,
262+
"value" => @value
260263
}
261264
end
262265
end

0 commit comments

Comments
 (0)