text: var async = require('../lib');
  var {expect} = require('chai');
  var assert = require('assert');
  var _ = require('lodash');
  
  describe("retry", () => {
  
      // Issue 306 on github: https://github.com/caolan/async/issues/306
      it('retry when attempt succeeds',(done) => {
          var failed = 3;
          var callCount = 0;
          var expectedResult = 'success';
          function fn(callback) {
              callCount++;
              failed--;
              if (!failed) callback(null, expectedResult);
              else callback(true); // respond with error
          }
          async.retry(fn, (err, result) => {
              assert(err === null, err + " passed instead of 'null'");
              assert.equal(callCount, 3, 'did not retry the correct number of times');
              assert.equal(result, expectedResult, 'did not return the expected result');
              done();
          });
      });
  
      it('retry when all attempts fail',(done) => {
          var times = 3;
          var callCount = 0;
          var error = 'ERROR';
          var erroredResult = 'RESULT';
          function fn(callback) {
              callCount++;
              callback(error + callCount, erroredResult + callCount); // respond with indexed values
          }
          async.retry(times, fn, (err, result) => {
              assert.equal(callCount, 3, "did not retry the correct number of times");
              assert.equal(err, error + times, "Incorrect error was returned");
              assert.equal(result, erroredResult + times, "Incorrect result was returned");
              done();
          });
      });
  
      it('retry fails with invalid arguments',(done) => {
          expect(() => {
              async.retry("");
          }).to.throw();
          expect(() => {
              async.retry();
          }).to.throw();
          expect(() => {
              async.retry(() => {}, 2, () => {});
          }).to.throw();
          done();
      });
  
      it('retry with interval when all attempts fail',function(done) {
          this.retries(3); // this test is flakey due to timing issues
  
          var times = 3;
          var interval = 50;
          var callCount = 0;
          var error = 'ERROR';
          var erroredResult = 'RESULT';
          function fn(callback) {
              callCount++;
              callback(error + callCount, erroredResult + callCount); // respond with indexed values
          }
          var start = Date.now();
          async.retry({ times, interval}, fn, (err, result) => {
              var duration = Date.now() - start;
              expect(duration).to.be.above(interval * (times - 1) - times);
              assert.equal(callCount, 3, "did not retry the correct number of times");
              assert.equal(err, error + times, "Incorrect error was returned");
              assert.equal(result, erroredResult + times, "Incorrect result was returned");
              done();
          });
      });
  
      it('retry with custom interval when all attempts fail',(done) => {
          var times = 3;
          var intervalFunc = function(retryCount) { return retryCount * 100; };
          var callCount = 0;
          var error = 'ERROR';
          var erroredResult = 'RESULT';
          function fn(callback) {
              callCount++;
              callback(error + callCount, erroredResult + callCount); // respond with indexed values
          }
          var start = Date.now();
          async.retry({ times, interval: intervalFunc}, fn, (err, result) => {
              var duration = Date.now() - start;
              expect(duration).to.be.above(300 - times);
              assert.equal(callCount, 3, "did not retry the correct number of times");
              assert.equal(err, error + times, "Incorrect error was returned");
              assert.equal(result, erroredResult + times, "Incorrect result was returned");
              done();
          });
      });
  
      it("should not require a callback", (done) => {
          var called = false;
          async.retry(3, (cb) => {
              called = true;
              cb();
          });
          setTimeout(() => {
              assert(called);
              done();
          }, 10);
      });
  
      it("should not require a callback and use the default times", (done) => {
          var calls = 0;
          async.retry((cb) => {
              calls++;
              cb("fail");
          }).catch(() => {});
          setTimeout(() => {
              expect(calls).to.equal(5);
              done();
          }, 50);
      });
  
      it("should be cancelable", (done) => {
          var calls = 0;
          async.retry(2, (cb) => {
              calls++;
              cb(calls > 1 ? false : 'fail');
          }, () => { throw new Error('should not get here') });
          setTimeout(() => {
              expect(calls).to.equal(2);
              done();
          }, 10);
      });
  
      it('retry does not precompute the intervals (#1226)', (done) => {
          var callTimes = [];
          function intervalFunc() {
              callTimes.push(Date.now());
              return 100;
          }
          function fn(callback) {
              callback({}); // respond with indexed values
          }
          async.retry({ times: 4, interval: intervalFunc}, fn, () => {
              expect(callTimes[1] - callTimes[0]).to.be.above(90);
              expect(callTimes[2] - callTimes[1]).to.be.above(90);
              done();
          });
      });
  
      it('retry passes all resolve arguments to callback', (done) => {
          function fn(callback) {
              callback(null, 1, 2, 3); // respond with indexed values
          }
          async.retry(5, fn, _.rest((args) => {
              expect(args).to.be.eql([null, 1, 2, 3]);
              done();
          }));
      });
  
      // note this is a synchronous test ensuring retry is synchrnous in the fastest (most straightforward) case
      it('retry calls fn immediately and will call callback if successful', () => {
          function fn(callback) {
              callback(null, {a: 1});
          }
          async.retry(5, fn, (err, result) => {
              expect(result).to.be.eql({a: 1});
          });
      });
  
      it('retry when all attempts fail and error continue test returns true',(done) => {
          var times = 3;
          var callCount = 0;
          var error = 'ERROR';
          var special = 'SPECIAL_ERROR';
          var erroredResult = 'RESULT';
          function fn(callback) {
              callCount++;
              callback(error + callCount, erroredResult + callCount);
          }
          function errorTest(err) {
              return err && err !== special;
          }
          var options = {
              times,
              errorFilter: errorTest
          };
          async.retry(options, fn, (err, result) => {
              assert.equal(callCount, 3, "did not retry the correct number of times");
              assert.equal(err, error + times, "Incorrect error was returned");
              assert.equal(result, erroredResult + times, "Incorrect result was returned");
              done();
          });
      });
  
      it('retry when some attempts fail and error test returns false at some invokation',(done) => {
          var callCount = 0;
          var error = 'ERROR';
          var special = 'SPECIAL_ERROR';
          var erroredResult = 'RESULT';
          function fn(callback) {
              callCount++;
              var err = callCount === 2 ? special : error + callCount;
              callback(err, erroredResult + callCount);
          }
          function errorTest(err) {
              return err && err === error + callCount; // just a different pattern
          }
          var options = {
              errorFilter: errorTest
          };
          async.retry(options, fn, (err, result) => {
              assert.equal(callCount, 2, "did not retry the correct number of times");
              assert.equal(err, special, "Incorrect error was returned");
              assert.equal(result, erroredResult + 2, "Incorrect result was returned");
              done();
          });
      });
  
      it('retry with interval when some attempts fail and error test returns false at some invokation',function(done) {
          this.retries(3); // flakey test
  
          var interval = 50;
          var callCount = 0;
          var error = 'ERROR';
          var erroredResult = 'RESULT';
          var special = 'SPECIAL_ERROR';
          var specialCount = 3;
          function fn(callback) {
              callCount++;
              var err = callCount === specialCount ? special : error + callCount;
              callback(err, erroredResult + callCount);
          }
          function errorTest(err) {
              return err && err !== special;
          }
          var start = Date.now();
          async.retry({ interval, errorFilter: errorTest }, fn, (err, result) => {
              var duration = Date.now() - start;
              expect(duration).to.be.above(interval * (specialCount - 1) - specialCount);
              assert.equal(callCount, specialCount, "did not retry the correct number of times");
              assert.equal(err, special, "Incorrect error was returned");
              assert.equal(result, erroredResult + specialCount, "Incorrect result was returned");
              done();
          });
      });
  
      it('retry when first attempt succeeds and error test should not be called',(done) => {
          var callCount = 0;
          var error = 'ERROR';
          var erroredResult = 'RESULT';
          var continueTestCalled = false;
          function fn(callback) {
              callCount++;
              callback(null, erroredResult + callCount);
          }
          function errorTest(err) {
              continueTestCalled = true;
              return err && err === error;
          }
          var options = {
              errorFilter: errorTest
          };
          async.retry(options, fn, _.rest((args) => {
              assert.equal(callCount, 1, "did not retry the correct number of times");
              expect(args).to.be.eql([null, erroredResult + callCount]);
              assert.equal(continueTestCalled, false, "error test function was called");
              done();
          }));
      });
  });
  