Skip to content
This repository has been archived by the owner on Dec 5, 2019. It is now read-only.

New documentation for v0.4.0 release #133

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
49eed2c
Working on new documentation for the next release
maxlinc Jul 2, 2014
9294416
Update README.md
maxlinc Jul 3, 2014
00adb0f
Update README.md
maxlinc Jul 3, 2014
0822258
Working on new documentation for the next release
maxlinc Jul 2, 2014
9dae3a7
Update README.md
maxlinc Jul 3, 2014
39acf1a
Update README.md
maxlinc Jul 3, 2014
af52899
README links and README as default doc for viewdocs
maxlinc Jul 5, 2014
f540bda
Clerks sample
maxlinc Jul 5, 2014
5fc1ba1
Merge branch 'v0.4.0_docs' of github.com:thoughtworks/pacto into v0.4…
maxlinc Jul 5, 2014
f7b3689
Added example file for custom clerk.
maxlinc Jul 5, 2014
677846d
Working on new documentation for the next release
maxlinc Jul 2, 2014
d38e668
Update README.md
maxlinc Jul 3, 2014
ab6a131
Update README.md
maxlinc Jul 3, 2014
f64be00
Working on new documentation for the next release
maxlinc Jul 2, 2014
afc061b
Update README.md
maxlinc Jul 3, 2014
dc3a1c0
Merged master
maxlinc Jul 5, 2014
0d1e20b
README links and README as default doc for viewdocs
maxlinc Jul 5, 2014
799fed2
Clerks sample
maxlinc Jul 5, 2014
c70e734
Added example file for custom clerk.
maxlinc Jul 5, 2014
558b8cd
Merge branch 'v0.4.0_docs' of github.com:thoughtworks/pacto into v0.4…
maxlinc Jul 5, 2014
6ae60b6
Merge remote-tracking branch 'origin/stenographer' into v0.4.0_docs
maxlinc Jul 5, 2014
972bd94
Updated configuration sample/doc
maxlinc Jul 6, 2014
18f08a8
Updated samples/docs for cops
maxlinc Jul 6, 2014
f53d730
Actors docs
maxlinc Jul 7, 2014
1b1b71c
Add forensics doc (even though the feature's not implemented yet)
maxlinc Jul 7, 2014
b0939c9
Merge branch 'forensics' into v0.4.0_docs
maxlinc Jul 8, 2014
f9841ab
Add docs generated from samples
maxlinc Jul 8, 2014
98e4266
Don't document have_investigated matcher... yet
maxlinc Jul 8, 2014
c7d6a32
Separate README.md (project overview) from docs/index.md (detailed usage
maxlinc Jul 8, 2014
ef436c6
Generate contract doc
maxlinc Jul 8, 2014
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,3 +27,6 @@ RSpec/DescribeClass:
Exclude:
- samples/**/*
- spec/integration/**/*
RSpec/MultipleDescribes:
Exclude:
- samples/**/*
1 change: 1 addition & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ desc 'Run the samples'
task :samples do
FileUtils.rm_rf('samples/tmp')
sh 'bundle exec polytrix exec --code2doc samples/*.rb samples/*.sh'
sh 'bundle exec polytrix exec --code2doc samples/*.json --lang js'
end

desc 'Build the documentation from the samples'
Expand Down
58 changes: 58 additions & 0 deletions docs/actors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
Pacto uses actors stub **providers** or simulate **consumers** based on the contract. There
are two built-in actors. The FromExamples actor will produce requests or responses based on
the examples in the contract. The JSONGenerator actor will attempt to generate requests or
responses that match the JSON schema, though it only works for simple schemas. The FromExamples
actor is the default, but falls back to the JSONGenerator actor if there are no examples available.
Consider the following contract:

```json
{
"name": "Ping",
"request": {
"headers": {
},
"http_method": "get",
"path": "/api/ping"
},
"response": {
"headers": {
"Content-Type": "application/json"
},
"status": 200,
"schema": {
"$schema": "http://json-schema.org/draft-03/schema#",
"type": "object",
"required": true,
"properties": {
"ping": {
"type": "string",
"required": true
}
}
}
},
"examples": {
"default": {
"request": {
},
"response": {
"body": {
"ping": "pong - from the example!"
}
}
}
}
}
```
Then Pacto will generate the following response by default (via FromExamples):

```json
{"ping":"pong - from the example!"}
```

If you didn't have an example, then Pacto generate very basic mock data based on the schema types,
producing something like:

```json
{"ping":"bar"}
```
40 changes: 40 additions & 0 deletions docs/clerks.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
Pacto clerks are responsible for loading contracts. Pacto has built-in support for a native
contract format, but we've setup a Clerks plugin API so you can more easily load information
from other formats, like [Swagger](https://github.com/wordnik/swagger-spec),
[apiblueprint](http://apiblueprint.org/), or [RAML](http://raml.org/).
Note: This is a preliminary API and may change in the future, including adding support
for conversion between formats, or generating each format from real HTTP interactions.
In order to add a loading clerk, you just implement a class that responds to build_from_file
and returns Contract object or a collection of Contract objects.

```rb
require 'yaml'
require 'pacto'

class SimpleYAMLClerk
def build_from_file(path, _host)
data = YAML.load(File.read(path))
data['services'].map do | service_name, service_definition |
request_clause = Pacto::RequestClause.new service_definition['request']
response_clause = Pacto::ResponseClause.new service_definition['response']
Pacto::Contract.new(name: service_name, request: request_clause, response: response_clause)
end
end
end
```

You can then register the clerk with Pacto:

```rb
Pacto.contract_factory.add_factory :simple_yaml, SimpleYAMLClerk.new
```

And then you can use it with the normal clerks API, by passing the identifier you used to register
the clerk:

```rb
contracts = Pacto.load_contracts 'simple_service_map.yaml', 'http://example.com', :simple_yaml
contract_names = contracts.map(&:name)
puts "Defined contracts: #{contract_names}"
```

39 changes: 39 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
Pacto will disable live connections, so you will get an error if
your code unexpectedly calls an service that was not stubbed. If you
want to re-enable connections, run `WebMock.allow_net_connect!` after
requiring pacto.

```rb
require 'pacto'
WebMock.allow_net_connect!
```

Pacto can be configured via a block:

```rb
Pacto.configure do |c|
c.contracts_path = 'contracts' # Path for loading/storing contracts.
c.strict_matchers = true # If the request matching should be strict (especially regarding HTTP Headers).
c.stenographer_log_file = nil # Set to nil to disable the stenographer log.
end
```

You can also do inline configuration. This example tells the
[json-schema-generator](https://github.com/maxlinc/json-schema-generator) to
store default values in the schema.

```rb
Pacto.configuration.generator_options = { defaults: true }
```

All Pacto configuration and metrics can be reset ia `Pacto.clear!`. If you're using
RSpec you may want to clear between each scenario:
If you're using Pacto's rspec matchers you might want to configure a reset between each scenario

```rb
require 'pacto/rspec'
RSpec.configure do |c|
c.after(:each) { Pacto.clear! }
end
```

109 changes: 109 additions & 0 deletions docs/contracts.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
Pacto Contracts describe the constraints we want to put on interactions between a consumer and a provider. It sets some expectations about the headers expected for both the request and response, the expected response status code. It also uses [json-schema](http://json-schema.org/) to define the allowable request body (if one should exist) and response body.

```js
{
```

The Request section comes first. In this case, we're just describing a simple get request that does not require any parameters or a request body.

```js
"request": {
"headers": {
```

A request must exactly match these headers for Pacto to believe the request matches the contract, unless `Pacto.configuration.strict_matchers` is false.

```js
"Accept": "application/vnd.github.beta+json",
"Accept-Encoding": "gzip;q=1.0,deflate;q=0.6,identity;q=0.3"
},
```

The `method` and `path` are required. The `path` may be an [rfc6570 URI template](http://tools.ietf.org/html/rfc6570) for more flexible matching.

```js
"http_method": "get",
"path": "/repos/thoughtworks/pacto/readme"
},
"response": {
"headers": {
"Content-Type": "application/json; charset=utf-8",
"Status": "200 OK",
"Cache-Control": "public, max-age=60, s-maxage=60",
"Etag": "\"fc8e78b0a9694de66d47317768b20820\"",
"Vary": "Accept, Accept-Encoding",
"Access-Control-Allow-Credentials": "true",
"Access-Control-Expose-Headers": "ETag, Link, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval",
"Access-Control-Allow-Origin": "*"
},
"status": 200,
"schema": {
"$schema": "http://json-schema.org/draft-03/schema#",
"description": "Generated from https://api.github.com/repos/thoughtworks/pacto/readme with shasum 3ae59164c6d9f84c0a81f21fb63e17b3b8ce6894",
"type": "object",
"required": true,
"properties": {
"name": {
"type": "string",
"required": true
},
"path": {
"type": "string",
"required": true
},
"sha": {
"type": "string",
"required": true
},
"size": {
"type": "integer",
"required": true
},
"url": {
"type": "string",
"required": true
},
"html_url": {
"type": "string",
"required": true
},
"git_url": {
"type": "string",
"required": true
},
"type": {
"type": "string",
"required": true
},
"content": {
"type": "string",
"required": true
},
"encoding": {
"type": "string",
"required": true
},
"_links": {
"type": "object",
"required": true,
"properties": {
"self": {
"type": "string",
"required": true
},
"git": {
"type": "string",
"required": true
},
"html": {
"type": "string",
"required": true
}
}
}
}
}
}
}
```

50 changes: 50 additions & 0 deletions docs/cops.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
You can create a custom cop that investigates the request/response and sees if it complies with a
contract. The cop should return a list of citations if it finds any problems.

```rb
require 'pacto'
class MyCustomCop
def investigate(_request, _response, contract)
citations = []
citations << 'Contract must have a request schema' if contract.request.schema.empty?
citations << 'Contract must have a response schema' if contract.response.schema.empty?
citations
end
end
```

You can activate the cop by adding it to the active_cops. The active_cops are reset
by `Pacto.clear!`

```rb
Pacto::Cops.active_cops << MyCustomCop.new
```

Or you could add it as a registered cop. These cops are not cleared - they form the
default set of Cops used by Pacto:

```rb
Pacto::Cops.register_cop MyCustomCop.new
```

The cops will be used to validate any service requests/responses detected by Pacto,
including when we simulate consumers:

```rb
Pacto.validate!
contracts = Pacto.load_contracts('contracts', 'http://localhost:5000')
contracts.stub_providers
puts contracts.simulate_consumers
```

You could also completely reset the registered cops if you don't want to use
all of Pacto's built-in cops:

```rb
Pacto::Cops.registered_cops.clear
Pacto::Cops.register_cop Pacto::Cops::ResponseBodyCop

contracts = Pacto.load_contracts('contracts', 'http://localhost:5000')
puts contracts.simulate_consumers
```

64 changes: 64 additions & 0 deletions docs/forensics.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
Pacto has a few RSpec matchers to help you ensure a **consumer** and **producer** are
interacting properly. First, let's setup the rspec suite.

```rb
require 'rspec/autorun' # Not generally needed
require 'pacto/rspec'
WebMock.allow_net_connect!
Pacto.validate!
Pacto.load_contracts('contracts', 'http://localhost:5000').stub_providers
```

It's usually a good idea to reset Pacto between each scenario. `Pacto.reset` just clears the
data and metrics about which services were called. `Pacto.clear!` also resets all configuration
and plugins.

```rb
RSpec.configure do |c|
c.after(:each) { Pacto.reset }
end
```

Pacto provides some RSpec matchers related to contract testing, like making sure
Pacto didn't received any unrecognized requests (`have_unmatched_requests`) and that
the HTTP requests matched up with the terms of the contract (`have_failed_investigations`).

```rb
describe Faraday do
let(:connection) { described_class.new(url: 'http://localhost:5000') }

it 'passes contract tests' do
connection.get '/api/ping'
expect(Pacto).to_not have_failed_investigations
expect(Pacto).to_not have_unmatched_requests
end
end
```

There are also some matchers for collaboration testing, so you can make sure each scenario is
calling the expected services and sending the right type of data.

```rb
describe Faraday do
let(:connection) { described_class.new(url: 'http://localhost:5000') }
before(:each) do
connection.get '/api/ping'

connection.post do |req|
req.url '/api/echo'
req.headers['Content-Type'] = 'application/json'
req.body = '{"foo": "bar"}'
end
end

it 'calls the ping service' do
expect(Pacto).to have_validated(:get, 'http://localhost:5000/api/ping').against_contract('Ping')
end

it 'sends data to the echo service' do
expect(Pacto).to have_investigated('Echo').with_request(body: hash_including('foo' => 'bar'))
expect(Pacto).to have_investigated('Echo').with_response(body: hash_including('foo' => 'bar'))
end
end
```

Loading