@@ -649,6 +649,46 @@ - (void)testDoesNotRetryOnPermanentError {
649649 [self awaitExpectations ];
650650}
651651
652+ - (void )testMakesDefaultMaxAttempts {
653+ FIRFirestore *firestore = [self firestore ];
654+ FIRDocumentReference *doc1 = [[firestore collectionWithPath: @" counters" ] documentWithAutoID ];
655+ auto counter = std::make_shared<std::atomic_int>(0 );
656+
657+ [self writeDocumentRef: doc1 data: @{@" count" : @(15.0 )}];
658+
659+ // Skip backoff delays.
660+ [firestore workerQueue ]->SkipDelaysForTimerId (TimerId::RetryTransaction);
661+
662+ XCTestExpectation *expectation = [self expectationWithDescription: @" transaction" ];
663+ [firestore
664+ runTransactionWithBlock: ^id _Nullable (FIRTransaction *transaction, NSError **error) {
665+ ++(*counter);
666+ // Get the first doc.
667+ [transaction getDocument: doc1 error: error];
668+ XCTAssertNil (*error);
669+ // Do a write outside of the transaction to cause the transaction to fail.
670+ dispatch_semaphore_t writeSemaphore = dispatch_semaphore_create (0 );
671+ int newValue = 1234 + counter->load ();
672+ [doc1 setData: @{
673+ @" count" : @(newValue)
674+ }
675+ completion: ^(NSError *) {
676+ dispatch_semaphore_signal (writeSemaphore);
677+ }];
678+ // We can block on it, because transactions run on a background queue.
679+ dispatch_semaphore_wait (writeSemaphore, DISPATCH_TIME_FOREVER);
680+ return nil ;
681+ }
682+ completion: ^(id , NSError *_Nullable error) {
683+ [expectation fulfill ];
684+ XCTAssertNotNil (error);
685+ XCTAssertEqual (error.code , FIRFirestoreErrorCodeFailedPrecondition);
686+ XCTAssertEqual (counter->load (), 5 );
687+ }];
688+ [self awaitExpectations ];
689+ }
690+
691+
652692- (void )testSuccessWithNoTransactionOperations {
653693 FIRFirestore *firestore = [self firestore ];
654694 XCTestExpectation *expectation = [self expectationWithDescription: @" transaction" ];
0 commit comments