diff --git a/Makefile b/Makefile index f8a587db9..900028387 100644 --- a/Makefile +++ b/Makefile @@ -44,9 +44,9 @@ define E2E done pact-verifier \ --provider-base-url=http://localhost:5000 \ - --pact-urls=./pacts/consumer-provider.json \ --provider-states-url=http://localhost:5000/_pact/provider-states \ - --provider-states-setup-url=http://localhost:5000/_pact/provider-states/active + --provider-states-setup-url=http://localhost:5000/_pact/provider-states/active \ + ./pacts/consumer-provider.json endef diff --git a/e2e/contracts/test_e2e.py b/e2e/contracts/test_e2e.py index 0f0d57cab..01dd0a243 100644 --- a/e2e/contracts/test_e2e.py +++ b/e2e/contracts/test_e2e.py @@ -93,7 +93,7 @@ def test_sparse(self): (pact .given('the user `bob` exists') .upon_receiving('a request for the user object of `bob`') - .with_request('get', '/users/bob') + .with_request('get', Term('/users/[a-z]+', '/users/bob')) .will_respond_with(200, body={ 'username': SomethingLike('bob'), 'id': Term('\d+', '123')})) diff --git a/pact/pact.py b/pact/pact.py index 3ea16c336..081e35e8c 100644 --- a/pact/pact.py +++ b/pact/pact.py @@ -40,7 +40,8 @@ class Pact(object): def __init__(self, consumer, provider, host_name='localhost', port=1234, log_dir=None, ssl=False, sslcert=None, sslkey=None, - cors=False, pact_dir=None, version='2.0.0'): + cors=False, pact_dir=None, version='2.0.0', + file_write_mode='overwrite'): """ Constructor for Pact. @@ -74,6 +75,13 @@ def __init__(self, consumer, provider, host_name='localhost', port=1234, :param version: The Pact Specification version to use, defaults to '2.0.0'. :type version: str + :param file_write_mode: `overwrite` or `merge`. Use `merge` when + running multiple mock service instances in parallel for the same + consumer/provider pair. Ensure the pact file is deleted before + running tests when using this option so that interactions deleted + from the code are not maintained in the file. Defaults to + `overwrite`. + :type file_write_mode: str """ scheme = 'https' if ssl else 'http' self.uri = '{scheme}://{host_name}:{port}'.format( @@ -81,6 +89,7 @@ def __init__(self, consumer, provider, host_name='localhost', port=1234, self.consumer = consumer self.cors = cors + self.file_write_mode = file_write_mode self.host_name = host_name self.log_dir = log_dir or os.getcwd() self.pact_dir = pact_dir or os.getcwd() @@ -137,6 +146,7 @@ def start_service(self): '--port={}'.format(self.port), '--log', '{}/pact-mock-service.log'.format(self.log_dir), '--pact-dir', self.pact_dir, + '--pact-file-write-mode', self.file_write_mode, '--pact-specification-version={}'.format(self.version), '--consumer', self.consumer.name, '--provider', self.provider.name] @@ -192,13 +202,8 @@ def verify(self): self.uri + '/interactions/verification', headers=self.HEADERS) assert resp.status_code == 200, resp.text - payload = { - 'consumer': {'name': self.consumer.name}, - 'provider': {'name': self.provider.name}, - 'pact_dir': self.pact_dir - } resp = requests.post( - self.uri + '/pact', headers=self.HEADERS, json=payload) + self.uri + '/pact', headers=self.HEADERS) assert resp.status_code == 200, resp.text def with_request(self, method, path, body=None, headers=None, query=None): diff --git a/pact/test/test_pact.py b/pact/test/test_pact.py index 5d27dc795..1f9d21700 100644 --- a/pact/test/test_pact.py +++ b/pact/test/test_pact.py @@ -35,7 +35,8 @@ def test_init_custom_mock_service(self): target = Pact( self.consumer, self.provider, host_name='192.168.1.1', port=8000, log_dir='/logs', ssl=True, sslcert='/ssl.cert', sslkey='/ssl.pem', - cors=True, pact_dir='/pacts', version='3.0.0') + cors=True, pact_dir='/pacts', version='3.0.0', + file_write_mode='merge') self.assertIs(target.consumer, self.consumer) self.assertIs(target.cors, True) @@ -49,6 +50,7 @@ def test_init_custom_mock_service(self): self.assertEqual(target.sslkey, '/ssl.pem') self.assertEqual(target.uri, 'https://192.168.1.1:8000') self.assertEqual(target.version, '3.0.0') + self.assertEqual(target.file_write_mode, 'merge') self.assertEqual(len(target._interactions), 0) def test_definition_sparse(self): @@ -232,6 +234,7 @@ def test_start_fails(self): '--port=1234', '--log', '/logs/pact-mock-service.log', '--pact-dir', '/pacts', + '--pact-file-write-mode', 'overwrite', '--pact-specification-version=2.0.0', '--consumer', 'consumer', '--provider', 'provider']) @@ -247,6 +250,7 @@ def test_start_no_ssl(self): '--port=1234', '--log', '/logs/pact-mock-service.log', '--pact-dir', '/pacts', + '--pact-file-write-mode', 'overwrite', '--pact-specification-version=2.0.0', '--consumer', 'consumer', '--provider', 'provider']) @@ -263,6 +267,7 @@ def test_start_with_ssl(self): '--port=1234', '--log', '/logs/pact-mock-service.log', '--pact-dir', '/pacts', + '--pact-file-write-mode', 'overwrite', '--pact-specification-version=2.0.0', '--consumer', 'consumer', '--provider', 'provider', @@ -373,9 +378,7 @@ def setUp(self): 'post', 'http://localhost:1234/pact', data=None, headers={'X-Pact-Mock-Service': 'true'}, - json={'pact_dir': os.getcwd(), - 'consumer': {'name': 'TestConsumer'}, - 'provider': {'name': 'TestProvider'}}) + json=None) def test_success(self): self.mock_requests.side_effect = iter([Mock(status_code=200)] * 2) diff --git a/pact/test/test_verify.py b/pact/test/test_verify.py index 12f007d6b..fbd52dd56 100644 --- a/pact/test/test_verify.py +++ b/pact/test/test_verify.py @@ -42,9 +42,10 @@ def setUp(self): self.runner = CliRunner() self.default_call = [ - '--provider-base-url=http://localhost', - '--pact-urls=./pacts/consumer-provider.json,' - './pacts/consumer-provider2.json,./pacts/consumer-provider3.json'] + './pacts/consumer-provider.json', + './pacts/consumer-provider2.json', + './pacts/consumer-provider3.json', + '--provider-base-url=http://localhost'] self.default_opts = [ '--provider-base-url=http://localhost', @@ -74,7 +75,7 @@ def test_pact_urls_are_required(self): verify.main, ['--provider-base-url=http://localhost']) self.assertEqual(result.exit_code, 1) - self.assertIn(b'--pact-url or --pact-urls', result.output_bytes) + self.assertIn(b'at least one', result.output_bytes) self.assertFalse(self.mock_Popen.called) def test_local_pact_urls_must_exist(self): @@ -123,6 +124,7 @@ def test_password_from_env_var(self): def test_all_options(self): self.mock_Popen.return_value.returncode = 0 result = self.runner.invoke(verify.main, [ + './pacts/consumer-provider5.json', '--provider-base-url=http://localhost', '--pact-urls=./pacts/consumer-provider.json,' './pacts/consumer-provider2.json', @@ -135,13 +137,15 @@ def test_all_options(self): '--provider-app-version=1.2.3', '--timeout=60' ]) - self.assertEqual(result.exit_code, 0) + self.assertEqual(result.exit_code, 0, result.output) self.assertEqual(self.mock_Popen.call_count, 1) self.assertProcess( + './pacts/consumer-provider5.json', + './pacts/consumer-provider3.json', + './pacts/consumer-provider4.json', + './pacts/consumer-provider.json', + './pacts/consumer-provider2.json', '--provider-base-url=http://localhost', - '--pact-urls=./pacts/consumer-provider3.json,' - './pacts/consumer-provider4.json,' - './pacts/consumer-provider.json,./pacts/consumer-provider2.json', '--provider-states-setup-url=http://localhost/provider-states/set', '--broker-username=user', '--broker-password=pass', @@ -163,9 +167,9 @@ def test_deprecated_pact_urls(self): result.output_bytes) self.assertEqual(self.mock_Popen.call_count, 1) self.assertProcess( - '--provider-base-url=http://localhost', - '--pact-urls=./pacts/consumer-provider.json,' - './pacts/consumer-provider2.json') + './pacts/consumer-provider.json', + './pacts/consumer-provider2.json', + '--provider-base-url=http://localhost') self.mock_Popen.return_value.communicate.assert_called_once_with( timeout=30) diff --git a/pact/verify.py b/pact/verify.py index faefefef1..1e2f832a0 100644 --- a/pact/verify.py +++ b/pact/verify.py @@ -16,20 +16,23 @@ @click.command() +@click.argument('pacts', nargs=-1) @click.option( 'base_url', '--provider-base-url', help='Base URL of the provider to verify against.', required=True) @click.option( 'pact_url', '--pact-url', - help='The URI of the pact to verify.' + help='DEPRECATED: specify pacts as arguments instead.\n' + 'The URI of the pact to verify.' ' Can be an HTTP URI, a local file or directory path. ' ' It can be specified multiple times to verify several pacts.', - multiple=True) + multiple=True) # Remove in major version 1.0.0 @click.option( 'pact_urls', '--pact-urls', default='', - help='The URI(s) of the pact to verify.' + help='DEPRECATED: specify pacts as arguments instead.\n' + 'The URI(s) of the pact to verify.' ' Can be an HTTP URI(s) or local file path(s).' ' Provide multiple URI separated by a comma.', multiple=True) # Remove in major version 1.0.0 @@ -64,7 +67,7 @@ default=False, help='Publish verification results to the broker', is_flag=True) -def main(base_url, pact_url, pact_urls, states_url, +def main(pacts, base_url, pact_url, pact_urls, states_url, states_setup_url, username, password, timeout, provider_app_version, publish_verification_results): """ @@ -72,11 +75,11 @@ def main(base_url, pact_url, pact_urls, states_url, Minimal example: - pact-verifier --provider-base-url=http://localhost:8080 --pact-url=./pact + pact-verifier --provider-base-url=http://localhost:8080 ./pacts """ # NOQA error = click.style('Error:', fg='red') warning = click.style('Warning:', fg='yellow') - all_pact_urls = list(pact_url) + all_pact_urls = list(pacts) + list(pact_url) for urls in pact_urls: # Remove in major version 1.0.0 all_pact_urls.extend(p for p in urls.split(',') if p) @@ -90,7 +93,7 @@ def main(base_url, pact_url, pact_urls, states_url, if not all_pact_urls: click.echo( error - + ' At least one of --pact-url or --pact-urls is required.') + + ' You must supply at least one pact file or directory to verify') raise click.Abort() all_pact_urls = expand_directories(all_pact_urls) @@ -104,13 +107,13 @@ def main(base_url, pact_url, pact_urls, states_url, options = { '--provider-base-url': base_url, - '--pact-urls': ','.join(all_pact_urls), '--provider-states-setup-url': states_setup_url, '--broker-username': username, '--broker-password': password } - command = [VERIFIER_PATH] + [ - '{}={}'.format(k, v) for k, v in options.items() if v] + command = [VERIFIER_PATH] + command.extend(all_pact_urls) + command.extend(['{}={}'.format(k, v) for k, v in options.items() if v]) if publish_verification_results: if not provider_app_version: diff --git a/setup.py b/setup.py index a69bb5895..751ab6049 100644 --- a/setup.py +++ b/setup.py @@ -13,7 +13,7 @@ IS_64 = sys.maxsize > 2 ** 32 -PACT_STANDALONE_VERSION = '1.8.0' +PACT_STANDALONE_VERSION = '1.9.0' here = os.path.abspath(os.path.dirname(__file__))