1 # -*- coding: utf-8; -*-
4 # Part of ‘dput’, a Debian package upload toolkit.
6 # This is free software, and you are welcome to redistribute it under
7 # certain conditions; see the end of this file for copyright
8 # information, grant of license, and disclaimer of warranty.
10 """ Unit tests for ‘crypto’ module. """
12 from __future__
import (absolute_import
, unicode_literals
)
26 __package__
= str("test")
27 __import__(__package__
)
28 sys
.path
.insert(1, os
.path
.dirname(os
.path
.dirname(__file__
)))
33 patch_system_interfaces
,
34 set_fake_file_scenario
,
35 setup_fake_file_fixtures
,
39 def make_gpgme_signature_scenarios():
40 """ Make a collection of scenarios for `gpgme.Signature` instances. """
43 ('signature-good validity-unknown', {
44 'signature': mock
.MagicMock(
46 fpr
="BADBEEF2FACEDCADF00DBEEFDECAFBAD",
47 status
=gpgme
.ERR_NO_ERROR
,
48 summary
=functools
.reduce(
49 operator
.ior
, [gpgme
.SIGSUM_GREEN
]),
50 validity
=gpgme
.VALIDITY_UNKNOWN
),
51 'expected_character': "good",
52 'expected_description': (
53 "Good signature from F00DBEEFDECAFBAD"),
55 ('signature-good validity-never', {
56 'signature': mock
.MagicMock(
58 fpr
="BADBEEF2FACEDCADF00DBEEFDECAFBAD",
59 status
=gpgme
.ERR_NO_ERROR
,
60 summary
=functools
.reduce(
61 operator
.ior
, [gpgme
.SIGSUM_GREEN
]),
62 validity
=gpgme
.VALIDITY_NEVER
),
63 'expected_character': "good",
64 'expected_description': (
65 "Good signature from F00DBEEFDECAFBAD"),
67 ('signature-good validity-full key-expired', {
68 'signature': mock
.MagicMock(
70 fpr
="BADBEEF2FACEDCADF00DBEEFDECAFBAD",
71 status
=gpgme
.ERR_NO_ERROR
,
72 summary
=functools
.reduce(operator
.ior
, [
73 gpgme
.SIGSUM_GREEN
, gpgme
.SIGSUM_KEY_EXPIRED
]),
74 validity
=gpgme
.VALIDITY_FULL
),
75 'expected_character': "good",
76 'expected_description': (
77 "Good signature from F00DBEEFDECAFBAD"),
79 ('signature-good validity-full', {
80 'signature': mock
.MagicMock(
82 fpr
="BADBEEF2FACEDCADF00DBEEFDECAFBAD",
83 status
=gpgme
.ERR_NO_ERROR
,
84 summary
=functools
.reduce(operator
.ior
, [
85 gpgme
.SIGSUM_VALID
, gpgme
.SIGSUM_GREEN
]),
86 validity
=gpgme
.VALIDITY_FULL
),
87 'expected_character': "valid",
88 'expected_description': (
89 "Valid signature from F00DBEEFDECAFBAD"),
92 'signature': mock
.MagicMock(
94 fpr
="BADBEEF2FACEDCADF00DBEEFDECAFBAD",
95 status
=gpgme
.ERR_BAD_SIGNATURE
,
96 summary
=functools
.reduce(
97 operator
.ior
, [gpgme
.SIGSUM_RED
]),
98 validity
=gpgme
.VALIDITY_FULL
),
99 'expected_character': "bad",
100 'expected_description': (
101 "Bad signature from F00DBEEFDECAFBAD"),
108 class characterise_signature_TestCase(
109 testscenarios
.WithScenarios
,
111 """ Test cases for function `characterise_signature`. """
113 scenarios
= make_gpgme_signature_scenarios()
115 def test_returns_expected_character(self
):
116 """ Should return expected character for signature. """
117 result
= dput
.crypto
.characterise_signature(self
.signature
)
118 self
.assertEqual(result
, self
.expected_character
)
121 class describe_signature_TestCase(
122 testscenarios
.WithScenarios
,
124 """ Test cases for function `describe_signature`. """
126 scenarios
= make_gpgme_signature_scenarios()
128 def test_returns_expected_character(self
):
129 """ Should return expected character for signature. """
130 result
= dput
.crypto
.describe_signature(self
.signature
)
131 self
.assertEqual(result
, self
.expected_description
)
134 def make_gpgme_verify_scenarios():
135 """ Make a collection of scenarios for ‘Context.verify’ method.
137 :return: A collection of scenarios for tests.
139 The collection is a mapping from scenario name to a dictionary of
144 signatures_by_name
= {
145 name
: scenario
['signature']
146 for (name
, scenario
) in make_gpgme_signature_scenarios()}
148 scenarios_by_name
= {
151 signatures_by_name
['signature-good validity-unknown'],
156 signatures_by_name
['signature-good validity-full'],
160 'exception': gpgme
.GpgmeError(
161 gpgme
.ERR_SOURCE_GPGME
, gpgme
.ERR_BAD_SIGNATURE
,
165 'exception': gpgme
.GpgmeError(
166 gpgme
.ERR_SOURCE_GPGME
, gpgme
.ERR_SIG_EXPIRED
,
167 "Signature expired"),
170 'exception': gpgme
.GpgmeError(
171 gpgme
.ERR_SOURCE_GPGME
, gpgme
.ERR_NO_DATA
,
175 'exception': ValueError,
180 'default': scenarios_by_name
['goodsig'],
184 for (name
, scenario
) in scenarios_by_name
.items())
189 def setup_gpgme_verify_fixtures(testcase
):
190 """ Set up fixtures for GPGME interaction behaviour. """
191 scenarios
= make_gpgme_verify_scenarios()
192 testcase
.gpgme_verify_scenarios
= scenarios
195 class check_file_signature_TestCase(testtools
.TestCase
):
196 """ Test cases for `check_file_signature` function. """
199 """ Set up test fixtures. """
200 super(check_file_signature_TestCase
, self
).setUp()
201 patch_system_interfaces(self
)
203 setup_fake_file_fixtures(self
)
204 set_fake_file_scenario(self
, 'exist-minimal')
208 self
.patch_gpgme_context()
210 setup_gpgme_verify_fixtures(self
)
211 self
.set_gpgme_verify_scenario('default')
213 def set_test_args(self
):
214 """ Set the arguments for the test call to the function. """
215 self
.test_args
= dict(
216 infile
=self
.file_double
.fake_file
,
219 def patch_gpgme_context(self
):
220 """ Patch the ‘gpgme.Context’ class for this test case. """
221 class_patcher
= mock
.patch
.object(gpgme
, 'Context')
222 class_patcher
.start()
223 self
.addCleanup(class_patcher
.stop
)
225 def set_gpgme_verify_scenario(self
, name
):
226 """ Set the status scenario for the ‘Context.verify’ call. """
227 scenario
= self
.gpgme_verify_scenarios
[name
]
228 mock_class
= gpgme
.Context
229 self
.mock_gpgme_context
= mock_class
.return_value
230 mock_func
= self
.mock_gpgme_context
.verify
231 if 'exception' in scenario
:
232 mock_func
.side_effect
= scenario
['exception']
234 mock_func
.return_value
= scenario
['result']
236 def assert_stderr_contains_gpgme_error(self
, code
):
237 """ Assert the `stderr` content contains the GPGME message. """
238 expected_output
= textwrap
.dedent("""\
239 gpgme: {path}: error {code}: ...
241 path
=self
.file_double
.path
, code
=code
)
243 sys
.stderr
.getvalue(),
244 testtools
.matchers
.DocTestMatches(
245 expected_output
, doctest
.ELLIPSIS
))
247 def test_calls_gpgme_verify_with_expected_args(self
):
248 """ Should call `gpgme.Context.verify` with expected args. """
249 dput
.crypto
.check_file_signature(**self
.test_args
)
250 gpgme
.Context
.return_value
.verify
.assert_called_with(
251 self
.file_double
.fake_file
, None, None)
253 def test_calls_sys_exit_if_gnupg_reports_bad_signature(self
):
254 """ Should call `sys.exit` if GnuPG reports bad signature. """
255 self
.set_gpgme_verify_scenario('badsig')
256 with testtools
.ExpectedException(gpgme
.GpgmeError
):
257 dput
.crypto
.check_file_signature(**self
.test_args
)
258 self
.assert_stderr_contains_gpgme_error(gpgme
.ERR_BAD_SIGNATURE
)
260 def test_calls_sys_exit_if_gnupg_reports_sig_expired(self
):
261 """ Should call `sys.exit` if GnuPG reports signature expired. """
262 self
.set_gpgme_verify_scenario('errsig')
263 with testtools
.ExpectedException(gpgme
.GpgmeError
):
264 dput
.crypto
.check_file_signature(**self
.test_args
)
265 self
.assert_stderr_contains_gpgme_error(gpgme
.ERR_SIG_EXPIRED
)
267 def test_calls_sys_exit_if_gnupg_reports_nodata(self
):
268 """ Should call `sys.exit` if GnuPG reports no data. """
269 self
.set_gpgme_verify_scenario('nodata')
270 with testtools
.ExpectedException(gpgme
.GpgmeError
):
271 dput
.crypto
.check_file_signature(**self
.test_args
)
272 self
.assert_stderr_contains_gpgme_error(gpgme
.ERR_NO_DATA
)
274 def test_outputs_message_if_gnupg_reports_goodsig(self
):
275 """ Should output a message if GnuPG reports a good signature. """
276 self
.set_gpgme_verify_scenario('goodsig')
277 dput
.crypto
.check_file_signature(**self
.test_args
)
278 expected_output
= textwrap
.dedent("""\
279 gpgme: {path}: Good signature from ...
280 """).format(path
=self
.file_double
.path
)
282 sys
.stderr
.getvalue(),
283 testtools
.matchers
.DocTestMatches(
284 expected_output
, doctest
.ELLIPSIS
))
286 def test_outputs_message_if_gnupg_reports_validsig(self
):
287 """ Should output a message if GnuPG reports a valid signature. """
288 self
.set_gpgme_verify_scenario('validsig')
289 dput
.crypto
.check_file_signature(**self
.test_args
)
290 expected_output
= textwrap
.dedent("""\
291 gpgme: {path}: Valid signature from ...
292 """).format(path
=self
.file_double
.path
)
294 sys
.stderr
.getvalue(),
295 testtools
.matchers
.DocTestMatches(
296 expected_output
, doctest
.ELLIPSIS
))
299 # Copyright © 2015–2016 Ben Finney <bignose@debian.org>
301 # This is free software: you may copy, modify, and/or distribute this work
302 # under the terms of the GNU General Public License as published by the
303 # Free Software Foundation; version 3 of that license or any later version.
304 # No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details.
311 # vim: fileencoding=utf-8 filetype=python :