• This repository has been archived on 05/Dec/2019
  • Stars
    star
    400
  • Rank 107,435 (Top 3 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 11 years ago
  • Updated over 6 years ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Pacto settles disputes between JSON providers and consumers

Gem Version Build Status Code Climate Dependency Status Coverage Status

Pacto is currently INACTIVE. Unfortunately none of the maintainers have had enough time to maintain it. While we feel that Pacto had good ideas, we also feel that a lot has changed since Pacto was first conceived (including the OpenAPIs initiative) and that too much work would be required to maintain & update Pacto. Instead, we encourage others to use other projects that have focused on Consumer-Driven Contracts, like Pact, or to write their own Pacto-inspired project.

[INACTIVE] Pacto

Pacto is a judge that arbitrates contract disputes between a service provider and one or more consumers. In other words, it is a framework for Integration Contract Testing, and helps guide service evolution patterns like Consumer-Driven Contracts or Documentation-Driven Contracts.

Pacto considers two major terms in order decide if there has been a breach of contract: the request clause and the response clause.

The request clause defines what information must be sent by the consumer to the provider in order to compel them to render a service. The request clause often describes the required HTTP request headers like Content-Type, the required parameters, and the required request body (defined in json-schema) when applicable. Providers are not held liable for failing to deliver services for invalid requests.

The response clause defines what information must be returned by the provider to the consumer in order to successfully complete the transaction. This commonly includes HTTP response headers like Location as well as the required response body (also defined in json-schema).

Test Doubles

The consumer may also enter into an agreement with test doubles like WebMock, vcr or mountebank. The services delivered by the test doubles for the purposes of development and testing will be held to the same conditions as the service the final services rendered by the parent provider. This prevents misrepresentation of sample services as realistic, leading to damages during late integration.

Pacto can provide a test double if you cannot afford your own.

Due Diligence

Pacto usually makes it clear if the consumer or provider is at fault, but if a contract is too vague Pacto cannot assign blame, and if it is too specific the matter may become litigious.

Pacto can provide a contract writer that tries to strike a balance, but you may still need to adjust the terms.

Implied Terms

  • Pacto only arbitrates contracts for JSON services.
  • Pacto requires Ruby 1.9.3 or newer, though you can use Polyglot Testing techniques to support older Rubies and non-Ruby projects.

Roadmap

  • The test double provided by Pacto is only semi-competent. It handles simple cases, but we expect to find a more capable replacement in the near future.
  • Pacto will provide additional Contract Writers for converting from apiblueprint, WADL, or other documentation formats in the future. It's part of our goal to support Documentation-Driven Contracts
  • Pacto reserves the right to consider other clauses in the future, like security and compliance to industry specifications.

Usage

See also: http://thoughtworks.github.io/pacto/usage/

Pacto can perform three activities: generating, validating, or stubbing services. You can do each of these activities against either live or stubbed services.

You can also use Pacto Server if you are working with non-Ruby projects.

Configuration

In order to start with Pacto, you just need to require it and optionally customize the default Configuration. For example:

require 'pacto'

Pacto.configure do |config|
  config.contracts_path = 'contracts'
end

Generating

The easiest way to get started with Pacto is to run a suite of live tests and tell Pacto to generate the contracts:

Pacto.generate!
# run your tests

If you're using the same configuration as above, this will produce Contracts in the contracts/ folder.

We know we cannot generate perfect Contracts, especially if we only have one sample request. So we do our best to give you a good starting point, but you will likely want to customize the contract so the validation is more strict in some places and less strict in others.

Contract Lists

In order to stub or validate a group of contracts you need to create a ContractList. A ContractList represent a collection of endpoints attached to the same host.

require 'pacto'

default_contracts = Pacto.load_contracts('contracts/services', 'http://example.com')
authentication_contracts = Pacto.load_contracts('contracts/auth', 'http://example.com')
legacy_contracts = Pacto.load_contracts('contracts/legacy', 'http://example.com')

Validating

Once you have a ContractList, you can validate all the contracts against the live host.

contracts = Pacto.load_contracts('contracts/services', 'http://example.com')
contracts.simulate_consumers

This method will hit the real endpoint, with a request created based on the request part of the contract.
The response will be compared against the response part of the contract and any structural difference will generate a validation error.

Running this in your build pipeline will ensure that your contracts actually match the real world, and that you can run your acceptance tests against the contract stubs without worries.

Stubbing

To generate stubs based on a ContractList you can run:

contracts = Pacto.load_contracts('contracts/services', 'http://example.com')
contracts.stub_providers

This method uses webmock to ensure that whenever you make a request against one of contracts you actually get a stubbed response, based on the default values specified on the contract, instead of hitting the real provider.

You can override any default value on the contracts by providing a hash of optional values to the stub_providers method. This will override the keys for every contract in the list

contracts = Pacto.load_contracts('contracts/services', 'http://example.com')
contracts.stub_providers(request_id: 14, name: "Marcos")

Pacto Server (non-Ruby usage)

See also: http://thoughtworks.github.io/pacto/patterns/polyglot/

We've bundled a small server that embeds pacto so you can use it for non-Ruby projects. If you want to take advantage of the full features, you'll still need to use Ruby (usually rspec) to drive your API testing. You can run just the server in order to stub or to write validation issues to a log, but you won't have access to the full API fail your tests if there were validation problems.

Command-line

The command-line version of the server will show you all the options. These same options are used when you launch the server from within rspec. In order to see the options: bundle exec pacto-server --help

Some examples:

$ bundle exec pacto-server -sv --generate
# Runs a server that will generate Contracts for each request received
$ bundle exec pacto-server -sv --stub --validate
# Runs the server that provides stubs and checks them against Contracts
$ bundle exec pacto-server -sv --live --validate --host
# Runs the server that acts as a proxy to http://example.com, validating each request/response against a Contract

RSpec test helper

You can also launch a server from within an rspec test. The server does start up an external port and runs asynchronously so it doens't block your main test thread from kicking off your consumer code. This gives you an externally accessable server that non-Ruby clients can hit, but still gives you the full Pacto API in order to validate and spy on HTTP interactions.

Example usage of the rspec test helper:

require 'rspec'
require 'pacto/rspec'
require 'pacto/test_helper'

describe 'my consumer' do
  include Pacto::TestHelper

  it 'calls a service' do
    with_pacto(
      :port => 5000,
      :directory => '../spec/integration/data',
      :stub => true) do |pacto_endpoint|
      # call your code
      system "curl #{pacto_endpoint}/echo"
      # check results
      expect(Pacto).to have_validated(:get, 'https://localhost/echo')
    end
  end
end

Rake Tasks

Pacto includes a few Rake tasks to help with common tasks. If you want to use these tasks, just add this top your Rakefile:

require 'pacto/rake_task'

This should add several new tasks to you project:

rake pacto:generate[input_dir,output_dir,host]  # Generates contracts from partial contracts
rake pacto:meta_validate[dir]                   # Validates a directory of contract definitions
rake pacto:validate[host,dir]                   # Validates all contracts in a given directory against a given host

The pacto:generate task will take partially defined Contracts and generate the missing pieces. See Generate for more details.

The pacto:meta_validate task makes sure that your Contracts are valid. It only checks the Contracts, not the services that implement them.

The pacto:validate task sends a request to an actual provider and ensures their response complies with the Contract.

Contracts

Pacto works by associating a service with a Contract. The Contract is a JSON description of the service that uses json-schema to describe the response body. You don't need to write your contracts by hand. In fact, we recommend generating a Contract from your documentation or a service.

A contract is composed of a request that has:

  • Method: the method of the HTTP request (e.g. GET, POST, PUT, DELETE);
  • Path: the relative path (without host) of the provider's endpoint;
  • Headers: headers sent in the HTTP request;
  • Params: any data or parameters of the HTTP request (e.g. query string for GET, body for POST).

And a response has that has:

  • Status: the HTTP response status code (e.g. 200, 404, 500);
  • Headers: the HTTP response headers;
  • Body: a JSON Schema defining the expected structure of the HTTP response body.

Below is an example contract for a GET request to the /hello_world endpoint of a provider:

{
  "request": {
    "method": "GET",
    "path": "/hello_world",
    "headers": {
      "Accept": "application/json"
    },
    "params": {}
  },

  "response": {
    "status": 200,
    "headers": {
      "Content-Type": "application/json"
    },
    "body": {
      "description": "A simple response",
      "type": "object",
      "properties": {
        "message": {
          "type": "string"
        }
      }
    }
  }
}

Constraints

  • Pacto only works with JSON services
  • Pacto requires Ruby 1.9.3 or newer (though you can older Rubies or non-Ruby projects with a Pacto Server)
  • Pacto cannot currently specify multiple acceptable status codes (e.g. 200 or 201)

Contributing

Read the CONTRIBUTING.md file.

More Repositories

1

build-your-own-radar

A library that generates an interactive radar, inspired by https://thoughtworks.com/radar/.
CSS
2,155
star
2

talisman

Using a pre-commit hook, Talisman validates the outgoing changeset for things that look suspicious β€” such as tokens, passwords, and private keys.
Go
1,892
star
3

cruisecontrol.rb

CruiseControl for Ruby. Keep it simple.
Ruby
613
star
4

mlops-platforms

Compare MLOps Platforms. Breakdowns of SageMaker, VertexAI, AzureML, Dataiku, Databricks, h2o, kubeflow, mlflow...
377
star
5

metrik

An easy-to-use, cross-platform measurement tool that pulls data out of CD pipelines and analysis the four key metrics for you.
Kotlin
359
star
6

dadoware

Brazilian-Portuguese word list and instructions booklet for Diceware
Jupyter Notebook
150
star
7

epirust

An agent-based epidemiology simulation framework built in Rust
Rust
99
star
8

HeartBeat

HeartBeat is a tool for tracking project delivery metrics that can help you get a better understanding of delivery performance. This product allows you easily get all aspects of source data faster and more accurate to analyze team delivery performance which enables delivery teams and team leaders focusing on driving continuous improvement and enhancing team productivity and efficiency.
Java
92
star
9

maeve-csms

MaEVe is an experimental EV Charge Station Management System (CSMS)
Go
76
star
10

dancing-glyphs

Two screen savers for macOS that show ThoughtWorks glyphs dancing around.
Swift
60
star
11

twde-datalab

Onboarding to data science by ThoughtWorks
Jupyter Notebook
55
star
12

Arium

Automation Testing Framework for XR(Unity) Applications.
C#
46
star
13

PipelinePatterns

Community library of software delivery pipeline patterns
39
star
14

tw2021-screensaver

A screen saver based on an illustration that was part of the 2021 Thoughtworks brand refresh
Swift
39
star
15

p2-old

The P2 Magazine
JavaScript
35
star
16

voto-como-vamos

Ruby
31
star
17

hardposit-chisel3

Chisel library for Unum Type-III Posit Arithmetic
Scala
28
star
18

what_they_say

Accessibility product to help people with hear disability
JavaScript
28
star
19

GilgaMesh

An open-source Bluetooth Low Energy self-healing mesh implementation for the nRF51/52 series.
C
26
star
20

letshelp.it

Ruby
18
star
21

tramchester

tramchester - a simple travel planning app for Manchester's tram system
Java
18
star
22

runes

Building the `runes` command with TDD in Go.
Go
18
star
23

byor-voting-web-app

TypeScript
15
star
24

simplebandit

lightweight contextual bandit library for ts/js
TypeScript
15
star
25

dashy

A Dashboard for managing app's services requests
CSS
13
star
26

vila-pinto-website

CEA – Centro de Educação Ambiental da Vila Pinto official website repository
Ruby
12
star
27

cruisecontrol.rb-contrib

Plugins of various kinds for CruiseControl.rb.
Ruby
12
star
28

metricating-api

A system that collects issue tracker data to visualize agile team metrics
JavaScript
10
star
29

inSpac

An SDK and a Keycloak plugin for fast integrating with Singpass Single Sign-on auth scheme (modified OpenID-Connect 1.0)
Java
10
star
30

billbo

Help an institution that helps other people. They got a lot of bills to pay and we got a lot of ideas to share.
JavaScript
9
star
31

byor-voting-infrastructure

TypeScript
8
star
32

candouble

Test doubles for the CAN bus
Rust
8
star
33

byor-voting-server

TypeScript
7
star
34

clojure-bootstrap

Collection of templates for Clojure apps
Clojure
6
star
35

sbteo

Compile server for sbt
Scala
6
star
36

voto-como-vamos-2

New Voto Como Vamos platform
Ruby
6
star
37

winston_wolfe

A best-of-breed, harm-minimisation testing tool (in Java)
Java
5
star
38

pii-anonymizer

data anonymization project
Python
4
star
39

PandoraRL

PandoraRL defines an RL algorithm for predicting optimal binding pose of a ligand for a given protein. The protein and ligand molecules are represented using generalized graph convolution where nodes define the residues and ligand-atoms, and the edges define the atomic interactions.
Jupyter Notebook
4
star
40

pacto-demo

A demo of Pacto, with consumers for iOS, Android, and Web
Ruby
4
star
41

murmurs.air

A desktop client for Mingle murmurs
JavaScript
4
star
42

rosetta-jvm

Demo project for working with multiple JVM languages in one code base
Groovy
3
star
43

smip

A framework for SOME/IP development in Rust
Rust
3
star
44

corpos-percussivos

CSS
3
star
45

ghc_voting_app

JavaScript
3
star
46

common-ci-tasks

Common CI tasks for Ruby (Rails, Sinatra, Gem) projects
Ruby
3
star
47

arthritiscampaign

Ruby
2
star
48

connections-hubot

Hubot repository for connections team
CoffeeScript
2
star
49

dashy4r

dashy4r is a gem that helps you use ruby to integrate with Dashy
Ruby
2
star
50

anonymizer

Simple proxy to anonymize e-mails to remove a barrier to providing feedback.
Python
2
star
51

antiviral-peptide-predictions-using-gan

PandoraGAN: Generating antiviral peptides using generative models
Python
2
star
52

mingle

Mingle: Agile Project Management [Archived]
2
star
53

YouthZoneApp

A simple java open source android application orginally built to the support activities of Onside Youth Zone
Java
1
star
54

mic-predictor

Python
1
star
55

lcm-sayu-infra

1
star
56

lcm-sayu

1
star