Skip to content

Commit

Permalink
Merge pull request #63 from pact-foundation/run-specific-interactions
Browse files Browse the repository at this point in the history
Resolves #44: Output a rerun command when a verification fails
  • Loading branch information
matthewbalvanz-wf authored Dec 17, 2017
2 parents 7c7bc7d + eb63864 commit ad69039
Show file tree
Hide file tree
Showing 3 changed files with 96 additions and 8 deletions.
66 changes: 61 additions & 5 deletions pact/test/test_verify.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,11 @@ def setUpClass(cls):
# terminal wants to handle unicode. Because we mock Popen to avoid
# calling the real verifier, we need to get the actual result of
# locale to provide it to Click during the test run.
cls.locale = Popen(
['locale', '-a'], stdout=PIPE, stderr=PIPE).communicate()[0]
if os.name == 'nt':
cls.locale = '' # pragma: no cover
else:
cls.locale = Popen(
['locale', '-a'], stdout=PIPE, stderr=PIPE).communicate()[0]

def setUp(self):
super(mainTestCase, self).setUp()
Expand All @@ -34,6 +37,9 @@ def setUp(self):
self.mock_isfile = patch.object(
verify, 'isfile', autospec=True).start()

self.mock_rerun_command = patch.object(
verify, 'rerun_command', autospec=True).start()

self.runner = CliRunner()
self.default_call = [
'--provider-base-url=http://localhost',
Expand All @@ -48,10 +54,14 @@ def setUp(self):

def assertProcess(self, *expected):
self.assertEqual(self.mock_Popen.call_count, 1)
actual = self.mock_Popen.mock_calls[0][1][0]
call = self.mock_Popen.mock_calls[0]
actual = call[1][0]
self.assertEqual(actual[0], VERIFIER_PATH)
self.assertEqual(len(set(actual)), len(expected) + 1)
self.assertEqual(set(actual[1:]), set(expected))
self.assertEqual(
call[2]['env']['PACT_INTERACTION_RERUN_COMMAND'],
self.mock_rerun_command.return_value)

def test_provider_base_url_is_required(self):
result = self.runner.invoke(verify.main, [])
Expand All @@ -62,7 +72,7 @@ def test_provider_base_url_is_required(self):
def test_pact_urls_are_required(self):
result = self.runner.invoke(
verify.main, ['--provider-base-url=http://localhost'])
print(result)

self.assertEqual(result.exit_code, 1)
self.assertIn(b'--pact-url or --pact-urls', result.output_bytes)
self.assertFalse(self.mock_Popen.called)
Expand Down Expand Up @@ -121,6 +131,8 @@ def test_all_options(self):
'--provider-states-setup-url=http://localhost/provider-states/set',
'--pact-broker-username=user',
'--pact-broker-password=pass',
'--publish-verification-results',
'--provider-app-version=1.2.3',
'--timeout=60'
])
self.assertEqual(result.exit_code, 0)
Expand All @@ -132,7 +144,9 @@ def test_all_options(self):
'./pacts/consumer-provider.json,./pacts/consumer-provider2.json',
'--provider-states-setup-url=http://localhost/provider-states/set',
'--broker-username=user',
'--broker-password=pass')
'--broker-password=pass',
'--publish-verification-results',
'--provider-app-version', '1.2.3')
self.mock_Popen.return_value.communicate.assert_called_once_with(
timeout=60)

Expand All @@ -155,6 +169,17 @@ def test_deprecated_pact_urls(self):
self.mock_Popen.return_value.communicate.assert_called_once_with(
timeout=30)

def test_publishing_missing_version(self):
result = self.runner.invoke(verify.main, [
'--pact-urls=./pacts/consumer-provider.json',
'--provider-base-url=http://localhost',
'--publish-verification-results'
])
self.assertEqual(result.exit_code, 1)
self.assertIn(
b'Provider application version is required', result.output_bytes)
self.assertFalse(self.mock_Popen.return_value.communicate.called)


class expand_directoriesTestCase(TestCase):
def setUp(self):
Expand Down Expand Up @@ -236,3 +261,34 @@ def test_file_does_not_exist(self):
self.assertIs(result, False)
self.mock_isfile.assert_called_once_with(
'./pacts/consumer-provider.json')


class rerun_commandTestCase(TestCase):
def setUp(self):
self.addCleanup(patch.stopall)
self.mock_platform = patch.object(
verify.platform, 'platform', autospec=True).start()

@patch.object(verify.sys, 'argv', new=[
'pact-verifier', '--pact-url=./consumer-provider.json'])
def test_posix(self):
self.mock_platform.return_value = 'linux'
result = verify.rerun_command()
self.assertEqual(
result, "PACT_DESCRIPTION='<PACT_DESCRIPTION>'"
" PACT_PROVIDER_STATE='<PACT_PROVIDER_STATE>'"
" pact-verifier --pact-url=./consumer-provider.json")

@patch.object(verify.sys, 'argv', new=[
'pact-verifier.exe', '--pact-url=./consumer-provider.json'])
def test_windows(self):
self.mock_platform.return_value = 'Windows'
result = verify.rerun_command()
self.assertEqual(
result, "cmd.exe /v /c \""
"set PACT_DESCRIPTION=<PACT_DESCRIPTION>"
"& set PACT_PROVIDER_STATE=<PACT_PROVIDER_STATE>"
"& pact-verifier.exe"
" --pact-url=./consumer-provider.json"
" & set PACT_DESCRIPTION="
" & set PACT_PROVIDER_STATE=\"")
29 changes: 28 additions & 1 deletion pact/verify.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
"""Methods to verify previously created pacts."""
import os
import platform
import sys
from os import listdir
from os.path import isfile, isdir, join
Expand Down Expand Up @@ -109,6 +111,7 @@ def main(base_url, pact_url, pact_urls, states_url,
}
command = [VERIFIER_PATH] + [
'{}={}'.format(k, v) for k, v in options.items() if v]

if publish_verification_results:
if not provider_app_version:
click.echo(
Expand All @@ -120,7 +123,10 @@ def main(base_url, pact_url, pact_urls, states_url,
command.extend(["--provider-app-version",
provider_app_version,
"--publish-verification-results"])
p = subprocess.Popen(command)

env = os.environ.copy()
env['PACT_INTERACTION_RERUN_COMMAND'] = rerun_command()
p = subprocess.Popen(command, env=env)
p.communicate(timeout=timeout)
sys.exit(p.returncode)

Expand Down Expand Up @@ -167,5 +173,26 @@ def path_exists(path):
return isfile(path)


def rerun_command():
"""
Create a rerun command template for failed interactions.
:rtype: str
"""
is_windows = 'windows' in platform.platform().lower()
if is_windows:
return (
'cmd.exe /v /c "'
'set PACT_DESCRIPTION=<PACT_DESCRIPTION>'
'& set PACT_PROVIDER_STATE=<PACT_PROVIDER_STATE>'
'& {command}'
' & set PACT_DESCRIPTION='
' & set PACT_PROVIDER_STATE="'.format(command=' '.join(sys.argv)))
else:
return ("PACT_DESCRIPTION='<PACT_DESCRIPTION>'"
" PACT_PROVIDER_STATE='<PACT_PROVIDER_STATE>'"
" {command}".format(command=' '.join(sys.argv)))


if __name__ == '__main__':
sys.exit(main())
9 changes: 7 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@


IS_64 = sys.maxsize > 2 ** 32
PACT_STANDALONE_VERSION = '1.1.1'
PACT_STANDALONE_VERSION = '1.8.0'


here = os.path.abspath(os.path.dirname(__file__))
Expand Down Expand Up @@ -86,7 +86,12 @@ def install_ruby_app(bin_path):
path = os.path.join(bin_path, suffix)
resp = urlopen(uri.format(version=PACT_STANDALONE_VERSION, suffix=suffix))
with open(path, 'wb') as f:
f.write(resp.read())
if resp.code == 200:
f.write(resp.read())
else:
raise RuntimeError(
'Received HTTP {} when downloading {}'.format(
resp.code, resp.url))

if 'windows' in platform.platform().lower():
with ZipFile(path) as f:
Expand Down

0 comments on commit ad69039

Please sign in to comment.