Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
10 changes: 10 additions & 0 deletions notecard/notecard.py
Original file line number Diff line number Diff line change
Expand Up @@ -361,6 +361,16 @@ def Transaction(self, req, lock=True):

error = True
break
elif '{heartbeat}' in rsp_json['err']:
if self._debug:
try:
print('Response has heartbeat field indicating ' + \
f'heartbeat: {rsp_json["status"]}')
except Exception as e:
print(e)

error = False
continue

error = False
break
Expand Down
64 changes: 64 additions & 0 deletions test/test_notecard.py
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,70 @@ def test_transaction_does_not_retry_on_bad_bin_error_in_response(

assert card._transact.call_count == 1

@pytest.mark.parametrize('num_heartbeats,debug_enabled', [
(1, False),
(4, False),
(1, True),
(4, True),
(notecard.CARD_TRANSACTION_RETRIES + 1, False),
(notecard.CARD_TRANSACTION_RETRIES + 1, True),
])
def test_transaction_continues_after_heartbeat_to_get_valid_response(
self, arrange_transaction_test, num_heartbeats, debug_enabled):
card = arrange_transaction_test()
card._debug = debug_enabled
req = {"req": "note.add"}

# num_heartbeats of heartbeat responses followed by valid response
heartbeat_response = b'{"err":"{heartbeat}"}\r\n'
valid_response = b'{"total":42}\r\n'
card._transact.side_effect = [heartbeat_response] * num_heartbeats + [valid_response]

json_responses = [{'err': '{heartbeat}'}] * num_heartbeats + [{'total': 42}]

with patch('notecard.notecard.json.loads') as mock_loads:
mock_loads.side_effect = json_responses
if debug_enabled:
with patch('builtins.print') as mock_print:
result = card.Transaction(req)
# Verify debug messages were printed for each heartbeat
assert mock_print.call_count >= num_heartbeats
else:
result = card.Transaction(req)

assert card._transact.call_count == num_heartbeats + 1
assert result == {'total': 42}

def test_transaction_debug_heartbeat_with_and_without_status(
self, arrange_transaction_test):
card = arrange_transaction_test()
card._debug = True
req = {"req": "note.add"}

# First heartbeat has a status field, second doesn't, then valid response
heartbeat_with_status = b'{"err":"{heartbeat}","status":"testing stsafe"}\r\n'
heartbeat_without_status = b'{"err":"{heartbeat}"}\r\n'
valid_response = b'{"total":42}\r\n'
card._transact.side_effect = [heartbeat_with_status, heartbeat_without_status, valid_response]

json_responses = [
{'err': '{heartbeat}', 'status': 'testing stsafe'},
{'err': '{heartbeat}'},
{'total': 42}
]

with patch('notecard.notecard.json.loads') as mock_loads:
mock_loads.side_effect = json_responses
with patch('builtins.print') as mock_print:
result = card.Transaction(req)

# Verify the status message was printed for first heartbeat
mock_print.assert_any_call('Response has heartbeat field indicating heartbeat: testing stsafe')
# Verify the exception was printed for second heartbeat (KeyError: 'status')
printed_calls = [str(call) for call in mock_print.call_args_list]
assert any("KeyError" in call and "'status'" in call for call in printed_calls)
assert result == {'total': 42}

@pytest.mark.parametrize(
'rsp_expected,return_type',
[
Expand Down