Skip to content

Commit eb63864

Browse files
Output a rerun command when a verification fails
Uses the options from the Ruby pact-verifier to print a command that can be used to rerun specific interactions.
1 parent 7c7bc7d commit eb63864

File tree

3 files changed

+96
-8
lines changed

3 files changed

+96
-8
lines changed

pact/test/test_verify.py

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,11 @@ def setUpClass(cls):
2121
# terminal wants to handle unicode. Because we mock Popen to avoid
2222
# calling the real verifier, we need to get the actual result of
2323
# locale to provide it to Click during the test run.
24-
cls.locale = Popen(
25-
['locale', '-a'], stdout=PIPE, stderr=PIPE).communicate()[0]
24+
if os.name == 'nt':
25+
cls.locale = '' # pragma: no cover
26+
else:
27+
cls.locale = Popen(
28+
['locale', '-a'], stdout=PIPE, stderr=PIPE).communicate()[0]
2629

2730
def setUp(self):
2831
super(mainTestCase, self).setUp()
@@ -34,6 +37,9 @@ def setUp(self):
3437
self.mock_isfile = patch.object(
3538
verify, 'isfile', autospec=True).start()
3639

40+
self.mock_rerun_command = patch.object(
41+
verify, 'rerun_command', autospec=True).start()
42+
3743
self.runner = CliRunner()
3844
self.default_call = [
3945
'--provider-base-url=http://localhost',
@@ -48,10 +54,14 @@ def setUp(self):
4854

4955
def assertProcess(self, *expected):
5056
self.assertEqual(self.mock_Popen.call_count, 1)
51-
actual = self.mock_Popen.mock_calls[0][1][0]
57+
call = self.mock_Popen.mock_calls[0]
58+
actual = call[1][0]
5259
self.assertEqual(actual[0], VERIFIER_PATH)
5360
self.assertEqual(len(set(actual)), len(expected) + 1)
5461
self.assertEqual(set(actual[1:]), set(expected))
62+
self.assertEqual(
63+
call[2]['env']['PACT_INTERACTION_RERUN_COMMAND'],
64+
self.mock_rerun_command.return_value)
5565

5666
def test_provider_base_url_is_required(self):
5767
result = self.runner.invoke(verify.main, [])
@@ -62,7 +72,7 @@ def test_provider_base_url_is_required(self):
6272
def test_pact_urls_are_required(self):
6373
result = self.runner.invoke(
6474
verify.main, ['--provider-base-url=http://localhost'])
65-
print(result)
75+
6676
self.assertEqual(result.exit_code, 1)
6777
self.assertIn(b'--pact-url or --pact-urls', result.output_bytes)
6878
self.assertFalse(self.mock_Popen.called)
@@ -121,6 +131,8 @@ def test_all_options(self):
121131
'--provider-states-setup-url=http://localhost/provider-states/set',
122132
'--pact-broker-username=user',
123133
'--pact-broker-password=pass',
134+
'--publish-verification-results',
135+
'--provider-app-version=1.2.3',
124136
'--timeout=60'
125137
])
126138
self.assertEqual(result.exit_code, 0)
@@ -132,7 +144,9 @@ def test_all_options(self):
132144
'./pacts/consumer-provider.json,./pacts/consumer-provider2.json',
133145
'--provider-states-setup-url=http://localhost/provider-states/set',
134146
'--broker-username=user',
135-
'--broker-password=pass')
147+
'--broker-password=pass',
148+
'--publish-verification-results',
149+
'--provider-app-version', '1.2.3')
136150
self.mock_Popen.return_value.communicate.assert_called_once_with(
137151
timeout=60)
138152

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

172+
def test_publishing_missing_version(self):
173+
result = self.runner.invoke(verify.main, [
174+
'--pact-urls=./pacts/consumer-provider.json',
175+
'--provider-base-url=http://localhost',
176+
'--publish-verification-results'
177+
])
178+
self.assertEqual(result.exit_code, 1)
179+
self.assertIn(
180+
b'Provider application version is required', result.output_bytes)
181+
self.assertFalse(self.mock_Popen.return_value.communicate.called)
182+
158183

159184
class expand_directoriesTestCase(TestCase):
160185
def setUp(self):
@@ -236,3 +261,34 @@ def test_file_does_not_exist(self):
236261
self.assertIs(result, False)
237262
self.mock_isfile.assert_called_once_with(
238263
'./pacts/consumer-provider.json')
264+
265+
266+
class rerun_commandTestCase(TestCase):
267+
def setUp(self):
268+
self.addCleanup(patch.stopall)
269+
self.mock_platform = patch.object(
270+
verify.platform, 'platform', autospec=True).start()
271+
272+
@patch.object(verify.sys, 'argv', new=[
273+
'pact-verifier', '--pact-url=./consumer-provider.json'])
274+
def test_posix(self):
275+
self.mock_platform.return_value = 'linux'
276+
result = verify.rerun_command()
277+
self.assertEqual(
278+
result, "PACT_DESCRIPTION='<PACT_DESCRIPTION>'"
279+
" PACT_PROVIDER_STATE='<PACT_PROVIDER_STATE>'"
280+
" pact-verifier --pact-url=./consumer-provider.json")
281+
282+
@patch.object(verify.sys, 'argv', new=[
283+
'pact-verifier.exe', '--pact-url=./consumer-provider.json'])
284+
def test_windows(self):
285+
self.mock_platform.return_value = 'Windows'
286+
result = verify.rerun_command()
287+
self.assertEqual(
288+
result, "cmd.exe /v /c \""
289+
"set PACT_DESCRIPTION=<PACT_DESCRIPTION>"
290+
"& set PACT_PROVIDER_STATE=<PACT_PROVIDER_STATE>"
291+
"& pact-verifier.exe"
292+
" --pact-url=./consumer-provider.json"
293+
" & set PACT_DESCRIPTION="
294+
" & set PACT_PROVIDER_STATE=\"")

pact/verify.py

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
"""Methods to verify previously created pacts."""
2+
import os
3+
import platform
24
import sys
35
from os import listdir
46
from os.path import isfile, isdir, join
@@ -109,6 +111,7 @@ def main(base_url, pact_url, pact_urls, states_url,
109111
}
110112
command = [VERIFIER_PATH] + [
111113
'{}={}'.format(k, v) for k, v in options.items() if v]
114+
112115
if publish_verification_results:
113116
if not provider_app_version:
114117
click.echo(
@@ -120,7 +123,10 @@ def main(base_url, pact_url, pact_urls, states_url,
120123
command.extend(["--provider-app-version",
121124
provider_app_version,
122125
"--publish-verification-results"])
123-
p = subprocess.Popen(command)
126+
127+
env = os.environ.copy()
128+
env['PACT_INTERACTION_RERUN_COMMAND'] = rerun_command()
129+
p = subprocess.Popen(command, env=env)
124130
p.communicate(timeout=timeout)
125131
sys.exit(p.returncode)
126132

@@ -167,5 +173,26 @@ def path_exists(path):
167173
return isfile(path)
168174

169175

176+
def rerun_command():
177+
"""
178+
Create a rerun command template for failed interactions.
179+
180+
:rtype: str
181+
"""
182+
is_windows = 'windows' in platform.platform().lower()
183+
if is_windows:
184+
return (
185+
'cmd.exe /v /c "'
186+
'set PACT_DESCRIPTION=<PACT_DESCRIPTION>'
187+
'& set PACT_PROVIDER_STATE=<PACT_PROVIDER_STATE>'
188+
'& {command}'
189+
' & set PACT_DESCRIPTION='
190+
' & set PACT_PROVIDER_STATE="'.format(command=' '.join(sys.argv)))
191+
else:
192+
return ("PACT_DESCRIPTION='<PACT_DESCRIPTION>'"
193+
" PACT_PROVIDER_STATE='<PACT_PROVIDER_STATE>'"
194+
" {command}".format(command=' '.join(sys.argv)))
195+
196+
170197
if __name__ == '__main__':
171198
sys.exit(main())

setup.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414

1515
IS_64 = sys.maxsize > 2 ** 32
16-
PACT_STANDALONE_VERSION = '1.1.1'
16+
PACT_STANDALONE_VERSION = '1.8.0'
1717

1818

1919
here = os.path.abspath(os.path.dirname(__file__))
@@ -86,7 +86,12 @@ def install_ruby_app(bin_path):
8686
path = os.path.join(bin_path, suffix)
8787
resp = urlopen(uri.format(version=PACT_STANDALONE_VERSION, suffix=suffix))
8888
with open(path, 'wb') as f:
89-
f.write(resp.read())
89+
if resp.code == 200:
90+
f.write(resp.read())
91+
else:
92+
raise RuntimeError(
93+
'Received HTTP {} when downloading {}'.format(
94+
resp.code, resp.url))
9095

9196
if 'windows' in platform.platform().lower():
9297
with ZipFile(path) as f:

0 commit comments

Comments
 (0)