@@ -474,6 +474,62 @@ def test_update_field(self):
474474 self .assertTrue (doc ['_rev' ].startswith ('2-' ))
475475 self .assertEqual (doc ['pets' ], ['cat' , 'dog' , 'fish' ])
476476
477+ @mock .patch ('cloudant.document.Document.save' )
478+ def test_update_field_maxretries (self , m_save ):
479+ """
480+ Test that conflict retries work for updating a single field.
481+ """
482+ # Create a doc
483+ doc = Document (self .db , 'julia006' )
484+ doc ['name' ] = 'julia'
485+ doc ['age' ] = 6
486+ doc .create ()
487+ self .assertTrue (doc ['_rev' ].startswith ('1-' ))
488+ self .assertEqual (doc ['age' ], 6 )
489+ # Mock conflicts when saving updates
490+ m_save .side_effect = requests .HTTPError (response = mock .Mock (status_code = 409 , reason = 'conflict' ))
491+ # Tests that failing on retry eventually throws
492+ with self .assertRaises (requests .HTTPError ) as cm :
493+ doc .update_field (doc .field_set , 'age' , 7 , max_tries = 2 )
494+
495+ # There is an off-by-one error for "max_tries"
496+ # It really means max_retries i.e. 1 attempt
497+ # followed by a max of 2 retries
498+ self .assertEqual (m_save .call_count , 3 )
499+ self .assertEqual (cm .exception .response .status_code , 409 )
500+ self .assertEqual (cm .exception .response .reason , 'conflict' )
501+ # Fetch again before asserting, otherwise we assert against
502+ # the locally updated age field
503+ doc .fetch ()
504+ self .assertFalse (doc ['_rev' ].startswith ('2-' ))
505+ self .assertNotEqual (doc ['age' ], 7 )
506+
507+ def test_update_field_success_on_retry (self ):
508+ """
509+ Test that conflict retries work for updating a single field.
510+ """
511+ # Create a doc
512+ doc = Document (self .db , 'julia006' )
513+ doc ['name' ] = 'julia'
514+ doc ['age' ] = 6
515+ doc .create ()
516+ self .assertTrue (doc ['_rev' ].startswith ('1-' ))
517+ self .assertEqual (doc ['age' ], 6 )
518+
519+ # Mock when saving the document
520+ # 1st call throw a 409
521+ # 2nd call delegate to the real doc.save()
522+ with mock .patch ('cloudant.document.Document.save' ,
523+ side_effect = [requests .HTTPError (response = mock .Mock (status_code = 409 , reason = 'conflict' )),
524+ doc .save ()]) as m_save :
525+ # A list of side effects containing only 1 element
526+ doc .update_field (doc .field_set , 'age' , 7 , max_tries = 1 )
527+ # Two calls to save, one with a 409 and one that succeeds
528+ self .assertEqual (m_save .call_count , 2 )
529+ # Check that the _rev and age field were updated
530+ self .assertTrue (doc ['_rev' ].startswith ('2-' ))
531+ self .assertEqual (doc ['age' ], 7 )
532+
477533 def test_delete_document_failure (self ):
478534 """
479535 Test failure condition when attempting to remove a document
0 commit comments