Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 16 additions & 5 deletions test/fluent_api/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

@pytest.fixture
def run_fluent_api_notecard_api_mapping_test():
def _run_test(fluent_api, notecard_api_name, req_params, rename_map=None):
def _run_test(fluent_api, notecard_api_name, req_params, rename_key_map=None, rename_value_map=None):
card = notecard.Notecard()
card.Transaction = MagicMock()

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

# Additionally, some Notecard API args have values that are not directly
# mapped, but are instead derived from the value of another arg. For
# example, note.template takes a 'compact' parameter, but the value of
# that parameter is actually derived from the value of the 'format'
# parameter. The rename_value_map allows a test to specify how a fluent
# API's keyword args map to Notecard API args, in cases where the value
# of one arg is derived from the value of another arg.
if rename_value_map is not None:
for key, new_value in rename_value_map.items():
expected_notecard_api_req[key] = new_value

card.Transaction.assert_called_once_with(expected_notecard_api_req)

return _run_test


@pytest.fixture
def run_fluent_api_invalid_notecard_test():
def _run_test(fluent_api, req_params):
def _run_test(fluent_api, req_params, rename_key_map=None, rename_value_map=None):
with pytest.raises(Exception, match='Notecard object required'):
# Call with None instead of a valid Notecard object.
fluent_api(None, **req_params)
Expand Down
41 changes: 28 additions & 13 deletions test/fluent_api/test_note.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@


@pytest.mark.parametrize(
'fluent_api,notecard_api,req_params,rename_map',
'fluent_api,notecard_api,req_params,rename_key_map,rename_value_map',
[
(
note.add,
Expand All @@ -12,8 +12,10 @@
'file': 'data.qo',
'body': {'key_a:', 'val_a', 'key_b', 42},
'payload': 'ewogICJpbnRlcnZhbHMiOiI2MCwxMiwxNCIKfQ==',
'port': 50,
'sync': True
},
None,
None
),
(
Expand All @@ -30,7 +32,8 @@
},
{
'maximum': 'max'
}
},
None
),
(
note.delete,
Expand All @@ -41,7 +44,8 @@
},
{
'note_id': 'note'
}
},
None
),
(
note.get,
Expand All @@ -54,17 +58,25 @@
},
{
'note_id': 'note'
}
},
None
),
(
note.template,
'note.template',
{
'file': 'my-settings.db',
'body': {'key_a:', 'val_a', 'key_b', 42},
'length': 42
'length': 42,
'port': 50,
'compact': True
},
None
{
'compact': 'format'
},
{
'format': 'compact'
}
),
(
note.update,
Expand All @@ -77,18 +89,21 @@
},
{
'note_id': 'note'
}
},
None
)
]
)
class TestNote:
def test_fluent_api_maps_notecard_api_correctly(
self, fluent_api, notecard_api, req_params, rename_map,
run_fluent_api_notecard_api_mapping_test):
self, fluent_api, notecard_api, req_params, rename_key_map,
rename_value_map, run_fluent_api_notecard_api_mapping_test):
run_fluent_api_notecard_api_mapping_test(fluent_api, notecard_api,
req_params, rename_map)
req_params, rename_key_map,
rename_value_map)

def test_fluent_api_fails_with_invalid_notecard(
self, fluent_api, notecard_api, req_params, rename_map,
run_fluent_api_invalid_notecard_test):
run_fluent_api_invalid_notecard_test(fluent_api, req_params)
self, fluent_api, notecard_api, req_params, rename_key_map,
rename_value_map, run_fluent_api_invalid_notecard_test):
run_fluent_api_invalid_notecard_test(fluent_api, req_params,
rename_key_map, rename_value_map)
39 changes: 39 additions & 0 deletions test/test_md5.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import unittest
import sys


class TestMD5(unittest.TestCase):
def setUp(self):
# Store original implementation name
self.original_implementation = sys.implementation.name
# Clear the module from sys.modules to force reload
if 'notecard.md5' in sys.modules:
del sys.modules['notecard.md5']

def tearDown(self):
# Restore original implementation
sys.implementation.name = self.original_implementation
# Clear the module again
if 'notecard.md5' in sys.modules:
del sys.modules['notecard.md5']

def test_non_cpython_implementation(self):
"""Test our custom MD5 implementation used in non-CPython environments"""
# Set implementation to non-cpython before importing
sys.implementation.name = 'non-cpython'
import notecard.md5

test_cases = [
(b'', 'd41d8cd98f00b204e9800998ecf8427e'),
(b'hello', '5d41402abc4b2a76b9719d911017c592'),
(b'hello world', '5eb63bbbe01eeed093cb22bb8f5acdc3'),
(b'The quick brown fox jumps over the lazy dog',
'9e107d9d372bb6826bd81d3542a419d6'),
(b'123456789', '25f9e794323b453885f5181f1b624d0b'),
(b'!@#$%^&*()', '05b28d17a7b6e7024b6e5d8cc43a8bf7')
]

for input_bytes, expected in test_cases:
with self.subTest(input_bytes=input_bytes):
result = notecard.md5.digest(input_bytes)
self.assertEqual(result, expected)
73 changes: 73 additions & 0 deletions test/test_validators.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import unittest
import sys
from unittest.mock import MagicMock, patch
from notecard import Notecard
from notecard.validators import validate_card_object


class TestValidators(unittest.TestCase):

def setUp(self):
self.mock_notecard = MagicMock(spec=Notecard)
# Store original implementation name
self.original_implementation = sys.implementation.name
# Clear the module from sys.modules to force reload
if 'notecard.validators' in sys.modules:
del sys.modules['notecard.validators']

def tearDown(self):
# Restore original implementation
sys.implementation.name = self.original_implementation
# Clear the module again
if 'notecard.validators' in sys.modules:
del sys.modules['notecard.validators']

def test_validate_card_object_with_valid_notecard(self):
@validate_card_object
def test_func(card):
return True

result = test_func(self.mock_notecard)
self.assertTrue(result)

def test_validate_card_object_with_invalid_notecard(self):
@validate_card_object
def test_func(card):
return True

with self.assertRaises(Exception) as context:
test_func("not a notecard")
self.assertEqual(str(context.exception), "Notecard object required")

@unittest.skipIf(sys.implementation.name != "cpython", "Function metadata only preserved in CPython")
def test_validate_card_object_preserves_metadata(self):
@validate_card_object
def test_func(card):
"""Test function docstring."""
return True

self.assertEqual(test_func.__name__, "test_func")
self.assertEqual(test_func.__doc__, "Test function docstring.")

def test_validate_card_object_non_cpython(self):
sys.implementation.name = 'non-cpython'
from notecard.validators import validate_card_object

@validate_card_object
def test_func(card):
return True

result = test_func(self.mock_notecard)
self.assertTrue(result)

def test_validate_card_object_non_cpython_with_invalid_notecard(self):
sys.implementation.name = 'non-cpython'
from notecard.validators import validate_card_object

@validate_card_object
def test_func(card):
return True

with self.assertRaises(Exception) as context:
test_func("not a notecard")
self.assertEqual(str(context.exception), "Notecard object required")