Skip to content

Commit bf0532c

Browse files
authored
Merge pull request #102 from blues/alex-improve-test-cov
Improve test coverage
2 parents 36ffa8f + dbcb72b commit bf0532c

File tree

4 files changed

+156
-18
lines changed

4 files changed

+156
-18
lines changed

test/fluent_api/conftest.py

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
@pytest.fixture
1313
def run_fluent_api_notecard_api_mapping_test():
14-
def _run_test(fluent_api, notecard_api_name, req_params, rename_map=None):
14+
def _run_test(fluent_api, notecard_api_name, req_params, rename_key_map=None, rename_value_map=None):
1515
card = notecard.Notecard()
1616
card.Transaction = MagicMock()
1717

@@ -21,21 +21,32 @@ def _run_test(fluent_api, notecard_api_name, req_params, rename_map=None):
2121
# There are certain fluent APIs that have keyword arguments that don't
2222
# map exactly onto the Notecard API. For example, note.changes takes a
2323
# 'maximum' parameter, but in the JSON request that gets sent to the
24-
# Notecard, it's sent as 'max'. The rename_map allows a test to specify
24+
# Notecard, it's sent as 'max'. The rename_key_map allows a test to specify
2525
# how a fluent API's keyword args map to Notecard API args, in cases
2626
# where they differ.
27-
if rename_map is not None:
28-
for old_key, new_key in rename_map.items():
27+
if rename_key_map is not None:
28+
for old_key, new_key in rename_key_map.items():
2929
expected_notecard_api_req[new_key] = expected_notecard_api_req.pop(old_key)
3030

31+
# Additionally, some Notecard API args have values that are not directly
32+
# mapped, but are instead derived from the value of another arg. For
33+
# example, note.template takes a 'compact' parameter, but the value of
34+
# that parameter is actually derived from the value of the 'format'
35+
# parameter. The rename_value_map allows a test to specify how a fluent
36+
# API's keyword args map to Notecard API args, in cases where the value
37+
# of one arg is derived from the value of another arg.
38+
if rename_value_map is not None:
39+
for key, new_value in rename_value_map.items():
40+
expected_notecard_api_req[key] = new_value
41+
3142
card.Transaction.assert_called_once_with(expected_notecard_api_req)
3243

3344
return _run_test
3445

3546

3647
@pytest.fixture
3748
def run_fluent_api_invalid_notecard_test():
38-
def _run_test(fluent_api, req_params):
49+
def _run_test(fluent_api, req_params, rename_key_map=None, rename_value_map=None):
3950
with pytest.raises(Exception, match='Notecard object required'):
4051
# Call with None instead of a valid Notecard object.
4152
fluent_api(None, **req_params)

test/fluent_api/test_note.py

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44

55
@pytest.mark.parametrize(
6-
'fluent_api,notecard_api,req_params,rename_map',
6+
'fluent_api,notecard_api,req_params,rename_key_map,rename_value_map',
77
[
88
(
99
note.add,
@@ -12,8 +12,10 @@
1212
'file': 'data.qo',
1313
'body': {'key_a:', 'val_a', 'key_b', 42},
1414
'payload': 'ewogICJpbnRlcnZhbHMiOiI2MCwxMiwxNCIKfQ==',
15+
'port': 50,
1516
'sync': True
1617
},
18+
None,
1719
None
1820
),
1921
(
@@ -30,7 +32,8 @@
3032
},
3133
{
3234
'maximum': 'max'
33-
}
35+
},
36+
None
3437
),
3538
(
3639
note.delete,
@@ -41,7 +44,8 @@
4144
},
4245
{
4346
'note_id': 'note'
44-
}
47+
},
48+
None
4549
),
4650
(
4751
note.get,
@@ -54,17 +58,25 @@
5458
},
5559
{
5660
'note_id': 'note'
57-
}
61+
},
62+
None
5863
),
5964
(
6065
note.template,
6166
'note.template',
6267
{
6368
'file': 'my-settings.db',
6469
'body': {'key_a:', 'val_a', 'key_b', 42},
65-
'length': 42
70+
'length': 42,
71+
'port': 50,
72+
'compact': True
6673
},
67-
None
74+
{
75+
'compact': 'format'
76+
},
77+
{
78+
'format': 'compact'
79+
}
6880
),
6981
(
7082
note.update,
@@ -77,18 +89,21 @@
7789
},
7890
{
7991
'note_id': 'note'
80-
}
92+
},
93+
None
8194
)
8295
]
8396
)
8497
class TestNote:
8598
def test_fluent_api_maps_notecard_api_correctly(
86-
self, fluent_api, notecard_api, req_params, rename_map,
87-
run_fluent_api_notecard_api_mapping_test):
99+
self, fluent_api, notecard_api, req_params, rename_key_map,
100+
rename_value_map, run_fluent_api_notecard_api_mapping_test):
88101
run_fluent_api_notecard_api_mapping_test(fluent_api, notecard_api,
89-
req_params, rename_map)
102+
req_params, rename_key_map,
103+
rename_value_map)
90104

91105
def test_fluent_api_fails_with_invalid_notecard(
92-
self, fluent_api, notecard_api, req_params, rename_map,
93-
run_fluent_api_invalid_notecard_test):
94-
run_fluent_api_invalid_notecard_test(fluent_api, req_params)
106+
self, fluent_api, notecard_api, req_params, rename_key_map,
107+
rename_value_map, run_fluent_api_invalid_notecard_test):
108+
run_fluent_api_invalid_notecard_test(fluent_api, req_params,
109+
rename_key_map, rename_value_map)

test/test_md5.py

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import unittest
2+
import sys
3+
4+
5+
class TestMD5(unittest.TestCase):
6+
def setUp(self):
7+
# Store original implementation name
8+
self.original_implementation = sys.implementation.name
9+
# Clear the module from sys.modules to force reload
10+
if 'notecard.md5' in sys.modules:
11+
del sys.modules['notecard.md5']
12+
13+
def tearDown(self):
14+
# Restore original implementation
15+
sys.implementation.name = self.original_implementation
16+
# Clear the module again
17+
if 'notecard.md5' in sys.modules:
18+
del sys.modules['notecard.md5']
19+
20+
def test_non_cpython_implementation(self):
21+
"""Test our custom MD5 implementation used in non-CPython environments"""
22+
# Set implementation to non-cpython before importing
23+
sys.implementation.name = 'non-cpython'
24+
import notecard.md5
25+
26+
test_cases = [
27+
(b'', 'd41d8cd98f00b204e9800998ecf8427e'),
28+
(b'hello', '5d41402abc4b2a76b9719d911017c592'),
29+
(b'hello world', '5eb63bbbe01eeed093cb22bb8f5acdc3'),
30+
(b'The quick brown fox jumps over the lazy dog',
31+
'9e107d9d372bb6826bd81d3542a419d6'),
32+
(b'123456789', '25f9e794323b453885f5181f1b624d0b'),
33+
(b'!@#$%^&*()', '05b28d17a7b6e7024b6e5d8cc43a8bf7')
34+
]
35+
36+
for input_bytes, expected in test_cases:
37+
with self.subTest(input_bytes=input_bytes):
38+
result = notecard.md5.digest(input_bytes)
39+
self.assertEqual(result, expected)

test/test_validators.py

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import unittest
2+
import sys
3+
from unittest.mock import MagicMock, patch
4+
from notecard import Notecard
5+
from notecard.validators import validate_card_object
6+
7+
8+
class TestValidators(unittest.TestCase):
9+
10+
def setUp(self):
11+
self.mock_notecard = MagicMock(spec=Notecard)
12+
# Store original implementation name
13+
self.original_implementation = sys.implementation.name
14+
# Clear the module from sys.modules to force reload
15+
if 'notecard.validators' in sys.modules:
16+
del sys.modules['notecard.validators']
17+
18+
def tearDown(self):
19+
# Restore original implementation
20+
sys.implementation.name = self.original_implementation
21+
# Clear the module again
22+
if 'notecard.validators' in sys.modules:
23+
del sys.modules['notecard.validators']
24+
25+
def test_validate_card_object_with_valid_notecard(self):
26+
@validate_card_object
27+
def test_func(card):
28+
return True
29+
30+
result = test_func(self.mock_notecard)
31+
self.assertTrue(result)
32+
33+
def test_validate_card_object_with_invalid_notecard(self):
34+
@validate_card_object
35+
def test_func(card):
36+
return True
37+
38+
with self.assertRaises(Exception) as context:
39+
test_func("not a notecard")
40+
self.assertEqual(str(context.exception), "Notecard object required")
41+
42+
@unittest.skipIf(sys.implementation.name != "cpython", "Function metadata only preserved in CPython")
43+
def test_validate_card_object_preserves_metadata(self):
44+
@validate_card_object
45+
def test_func(card):
46+
"""Test function docstring."""
47+
return True
48+
49+
self.assertEqual(test_func.__name__, "test_func")
50+
self.assertEqual(test_func.__doc__, "Test function docstring.")
51+
52+
def test_validate_card_object_non_cpython(self):
53+
sys.implementation.name = 'non-cpython'
54+
from notecard.validators import validate_card_object
55+
56+
@validate_card_object
57+
def test_func(card):
58+
return True
59+
60+
result = test_func(self.mock_notecard)
61+
self.assertTrue(result)
62+
63+
def test_validate_card_object_non_cpython_with_invalid_notecard(self):
64+
sys.implementation.name = 'non-cpython'
65+
from notecard.validators import validate_card_object
66+
67+
@validate_card_object
68+
def test_func(card):
69+
return True
70+
71+
with self.assertRaises(Exception) as context:
72+
test_func("not a notecard")
73+
self.assertEqual(str(context.exception), "Notecard object required")

0 commit comments

Comments
 (0)