GitStatusChecker

throws TypeError for non-object options
assert.throws(
  // eslint-disable-next-line no-new
  () => { new GitStatusChecker(true); },
  TypeError,
  /\boptions\b/,
);
throws TypeError for non-Readable in
assert.throws(
  // eslint-disable-next-line no-new
  () => { new GitStatusChecker({ in: new stream.Writable() }); },
  TypeError,
  /\boptions.in\b/,
);
throws TypeError for non-Writable out
assert.throws(
  // eslint-disable-next-line no-new
  () => { new GitStatusChecker({ out: new stream.Readable() }); },
  TypeError,
  /\boptions.out\b/,
);
returns Error for non-Writable err
assert.throws(
  // eslint-disable-next-line no-new
  () => { new GitStatusChecker({ err: new stream.Readable() }); },
  TypeError,
  /\boptions.err\b/,
);

.checkSlugFormat()

accepts "owner/repo"
const result = GitStatusChecker.checkSlugFormat(slug);
assert.strictEqual(result, slug);
accepts "owner-1/repo-1"
const result = GitStatusChecker.checkSlugFormat(slug);
assert.strictEqual(result, slug);
rejects "repo"
assert.throws(
  () => { GitStatusChecker.checkSlugFormat(slug); },
  InvalidSlugError,
);
rejects "/owner/repo"
assert.throws(
  () => { GitStatusChecker.checkSlugFormat(slug); },
  InvalidSlugError,
);
rejects "/repo"
assert.throws(
  () => { GitStatusChecker.checkSlugFormat(slug); },
  InvalidSlugError,
);
rejects "owner/"
assert.throws(
  () => { GitStatusChecker.checkSlugFormat(slug); },
  InvalidSlugError,
);
rejects "owner//repo"
assert.throws(
  () => { GitStatusChecker.checkSlugFormat(slug); },
  InvalidSlugError,
);
rejects "owner/repo/"
assert.throws(
  () => { GitStatusChecker.checkSlugFormat(slug); },
  InvalidSlugError,
);
rejects "owner/repo/branch"
assert.throws(
  () => { GitStatusChecker.checkSlugFormat(slug); },
  InvalidSlugError,
);
rejects " owner/repo"
assert.throws(
  () => { GitStatusChecker.checkSlugFormat(slug); },
  InvalidSlugError,
);
rejects "owner /repo"
assert.throws(
  () => { GitStatusChecker.checkSlugFormat(slug); },
  InvalidSlugError,
);
rejects "owner/ repo"
assert.throws(
  () => { GitStatusChecker.checkSlugFormat(slug); },
  InvalidSlugError,
);
rejects "owner/repo "
assert.throws(
  () => { GitStatusChecker.checkSlugFormat(slug); },
  InvalidSlugError,
);

#resolveHash()

can resolve the hash of HEAD
const checker = new GitStatusChecker();
return checker.resolveHash('HEAD').then((hash) => {
  assert.match(hash, /^[a-fA-F0-9]{40}$/);
  headHash = hash;
});
can resolve a hash to itself
const checker = new GitStatusChecker();
return checker.resolveHash(headHash).then((hash) => {
  assert.strictEqual(hash, headHash);
});
rejects with Error for unresolvable name
const checker = new GitStatusChecker();
return checker.resolveHash('notabranch').then(
  sinon.mock().never(),
  (err) => {
    assert(err);
  },
);

#storeSlug()

can store a valid slug
const checker = new GitStatusChecker();
const testSlug = 'foo/bar';
return checker.storeSlug(testSlug).then((slug) => {
  assert.strictEqual(slug, testSlug);
  return git('config', '--get', GitStatusChecker.SLUG_CONFIG_NAME)
    .then((result) => {
      const configSlug = result[0].trimEnd();
      assert.strictEqual(configSlug, testSlug);
    });
});
returns Error for an invalid slug
const checker = new GitStatusChecker();
const testSlug = 'foobar';
return checker.storeSlug(testSlug).then(
  sinon.mock().never(),
  (errStore) => {
    assert.instanceOf(errStore, InvalidSlugError);
    return git('config', '--get', GitStatusChecker.SLUG_CONFIG_NAME)
      .then(
        (configSlug) => {
          assert.fail(configSlug, null, 'slug should not be stored');
        },
        (errGit) => (errGit.code === 1 ? null : Promise.reject(errGit)),
      );
  },
);

#tryStoreSlug()

can store a valid slug
const checker = new GitStatusChecker();
const testSlug = 'foo/bar';
return checker.tryStoreSlug(testSlug).then((slug) => {
  assert.strictEqual(slug, testSlug);
  return git('config', '--get', GitStatusChecker.SLUG_CONFIG_NAME)
    .then((result) => {
      const configSlug = result[0].trimEnd();
      assert.strictEqual(configSlug, testSlug);
    });
});
prints error message for an invalid slug
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const checker = new GitStatusChecker({
  out: outStream,
  err: errStream,
});
const testSlug = 'foobar';
return checker.tryStoreSlug(testSlug).then((slug) => {
  assert.strictEqual(slug, testSlug);
  assert.strictEqual(outStream.read(), null);
  assert.match(String(errStream.read()), /error/i);
  return git('config', '--get', GitStatusChecker.SLUG_CONFIG_NAME)
    .then(
      (configSlug) => {
        assert.fail(configSlug, null, 'slug should not be stored');
      },
      (errGit) => (errGit.code === 1 ? null : Promise.reject(errGit)),
    );
});

#confirmSlug()

prompts user for confirmation
const inStream = new stream.PassThrough();
const outStream = new stream.PassThrough({ encoding: 'utf8' });
const errStream = new stream.PassThrough({ encoding: 'utf8' });
const checker = new GitStatusChecker({
  in: inStream,
  out: outStream,
  err: errStream,
});
const testSlug = 'foo/bar';
const confirmP = checker.confirmSlug(testSlug);
return read(errStream)
  .then((promptMsg) => {
    assert.strictEqual(outStream.read(), null);
    assert.match(promptMsg, /correct/i);
    assert.include(promptMsg, testSlug);
    inStream.write('y\n');
    return confirmP;
  })
  .then((slug) => {
    assert.strictEqual(slug, testSlug);
    assert.strictEqual(outStream.read(), null);
    assert.strictEqual(errStream.read(), null);
  });
prompts user for slug if not confirmed
const inStream = new stream.PassThrough();
const outStream = new stream.PassThrough({ encoding: 'utf8' });
const errStream = new stream.PassThrough({ encoding: 'utf8' });
const checker = new GitStatusChecker({
  in: inStream,
  out: outStream,
  err: errStream,
});
const testSlug1 = 'foo/bar';
const testSlug2 = 'baz/quux';
const confirmP = checker.confirmSlug(testSlug1);
return read(errStream)
  .then((promptMsg1) => {
    assert.strictEqual(outStream.read(), null);
    assert.match(promptMsg1, /correct/i);
    assert.include(promptMsg1, testSlug1);
    inStream.write('n\n');
    return read(errStream);
  })
  .then((promptMsg2) => {
    assert.strictEqual(outStream.read(), null);
    assert.match(promptMsg2, /repository/i);
    assert.include(promptMsg2, testSlug1);
    inStream.write(`${testSlug2}\n`);
    return confirmP;
  }).then((slug) => {
    assert.strictEqual(slug, testSlug2);
    assert.strictEqual(outStream.read(), null);
    assert.strictEqual(errStream.read(), null);
  });
re-prompts user if slug is invalid
const inStream = new stream.PassThrough();
const outStream = new stream.PassThrough({ encoding: 'utf8' });
const errStream = new stream.PassThrough({ encoding: 'utf8' });
const checker = new GitStatusChecker({
  in: inStream,
  out: outStream,
  err: errStream,
});
const testSlug1 = 'foo/bar';
const testSlug2 = 'fred';
const testSlug3 = 'baz/quux';
const confirmP = checker.confirmSlug(testSlug1);
return read(errStream)
  .then((promptMsg1) => {
    assert.strictEqual(outStream.read(), null);
    assert.match(promptMsg1, /correct/i);
    assert.include(promptMsg1, testSlug1);
    inStream.write('n\n');
    return read(errStream);
  })
  .then((promptMsg2) => {
    assert.strictEqual(outStream.read(), null);
    assert.match(promptMsg2, /repository/i);
    assert.include(promptMsg2, testSlug1);
    inStream.write(`${testSlug2}\n`);
    return read(errStream);
  })
  .then((errorMsg) => {
    assert.strictEqual(outStream.read(), null);
    assert.match(errorMsg, /invalid/i);
    // Prompt may be part of error message or not
    if (errorMsg.includes(testSlug1)) {
      return errorMsg;
    }
    return read(errStream);
  })
  .then((promptMsg3) => {
    assert.strictEqual(outStream.read(), null);
    assert.match(promptMsg3, /repository/i);
    assert.include(promptMsg3, testSlug1);
    inStream.write(`${testSlug3}\n`);
    return confirmP;
  })
  .then((slug) => {
    assert.strictEqual(slug, testSlug3);
    assert.strictEqual(outStream.read(), null);
    assert.strictEqual(errStream.read(), null);
  });
rejects with EOFError if input ends
const inStream = new stream.PassThrough();
const outStream = new stream.PassThrough({ encoding: 'utf8' });
const errStream = new stream.PassThrough({ encoding: 'utf8' });
const checker = new GitStatusChecker({
  in: inStream,
  out: outStream,
  err: errStream,
});
const testSlug = 'foo/bar';
const confirmP = checker.confirmSlug(testSlug)
  .then(
    sinon.mock().never(),
    (err) => {
      assert.strictEqual(err.name, 'EOFError');
      // Same message as travis.rb
      assert.strictEqual(err.message, 'The input stream is exhausted.');
      // Doesn't print error message itself, but calling code will
      assert.strictEqual(outStream.read(), null);
      assert.strictEqual(errStream.read(), null);
    },
  );
const promptP = read(errStream)
  .then((promptMsg) => {
    assert.strictEqual(outStream.read(), null);
    assert.match(promptMsg, /correct/i);
    assert.include(promptMsg, testSlug);
    // End without newline (e.g. user hit ^D before return)
    inStream.end('y');
  });
return Promise.all([confirmP, promptP]);

#detectBranch()

resolves master on master
const checker = new GitStatusChecker();
return git('checkout', 'master')
  .then(() => checker.detectBranch())
  .then((branch) => {
    assert.strictEqual(branch, 'master');
  });
resolves branch1 on branch1
const checker = new GitStatusChecker();
return git('checkout', 'branch1')
  .then(() => checker.detectBranch())
  .then((branch) => {
    assert.strictEqual(branch, 'branch1');
  });
rejects with Error not on branch
const checker = new GitStatusChecker();
return git('checkout', 'HEAD^')
  .then(() => checker.detectBranch())
  .then(
    sinon.mock().never(),
    (err) => {
      assert.match(err.message, /branch/i);
    },
  );

#detectSlug()

resolves owner1/repo1 for branch1
const checker = new GitStatusChecker({
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
});
return git('checkout', branchName)
  .then(() => checker.detectSlug())
  .then((slug) => {
    assert.strictEqual(slug, remoteSlug);
  });
resolves owner2/repo2 for branch2
const checker = new GitStatusChecker({
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
});
return git('checkout', branchName)
  .then(() => checker.detectSlug())
  .then((slug) => {
    assert.strictEqual(slug, remoteSlug);
  });
resolves owner3/repo3 for branch3
const checker = new GitStatusChecker({
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
});
return git('checkout', branchName)
  .then(() => checker.detectSlug())
  .then((slug) => {
    assert.strictEqual(slug, remoteSlug);
  });
defaults to origin if branch has no remote
const checker = new GitStatusChecker({
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
});
return git('checkout', 'master')
  .then(() => checker.detectSlug())
  .then((slug) => {
    assert.strictEqual(slug, REMOTE_SLUGS.origin);
  });
defaults to origin if not on branch
const checker = new GitStatusChecker({
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
});
return git('checkout', 'HEAD^')
  .then(() => checker.detectSlug())
  .then((slug) => {
    assert.strictEqual(slug, REMOTE_SLUGS.origin);
  });
rejects with SlugDetectionError for remote with no URL
const checker = new GitStatusChecker({
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
});
return git('checkout', 'branchnourl')
  .then(() => checker.detectSlug())
  .then(
    sinon.mock().never(),
    (err) => {
      assert.strictEqual(err.name, 'SlugDetectionError');
      assert.match(err.message, /remote/i);
    },
  );
rejects with SlugDetectionError for remote without slug
const checker = new GitStatusChecker({
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
});
return git('checkout', 'branchnotslug')
  .then(() => checker.detectSlug())
  .then(
    sinon.mock().never(),
    (err) => {
      assert.strictEqual(err.name, 'SlugDetectionError');
      assert.match(err.message, /URL/i);
    },
  );
prompts for confirmation if interactive
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const checker = new GitStatusChecker({
  interactive: true,
  out: outStream,
  err: errStream,
});
const testSlug = 'prompt/slug';
const mock = sinon.mock(checker);
mock.expects('confirmSlug')
  .once().withExactArgs(REMOTE_SLUGS.origin).returns(testSlug);
return git('checkout', 'master')
  .then(() => checker.detectSlug())
  .then((slug) => {
    assert.strictEqual(slug, testSlug);
    // Only output is from prompt (which is mocked)
    assert.strictEqual(outStream.read(), null);
    assert.strictEqual(errStream.read(), null);
  });
prints result without confirmation if not interactive
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough({ encoding: 'utf8' });
const checker = new GitStatusChecker({
  out: outStream,
  err: errStream,
});
const mock = sinon.mock(checker);
mock.expects('confirmSlug').never();
return git('checkout', 'master')
  .then(() => checker.detectSlug())
  .then((slug) => {
    assert.strictEqual(slug, REMOTE_SLUGS.origin);
    assert.strictEqual(outStream.read(), null);
    // From travis.rb
    const detectMsg = `detected repository as ${slug}\n`;
    assert.strictEqual(errStream.read(), detectMsg);
  });

#loadSlug()

loads slug set by #storeSlug()
const checker = new GitStatusChecker();
const testSlug = 'foo/bar';
return checker.storeSlug(testSlug)
  .then(() => checker.loadSlug().then((slug) => {
    assert.strictEqual(slug, testSlug);
  }));
resolves null if slug is not set
const checker = new GitStatusChecker();
return checker.loadSlug().then((slug) => {
  assert.strictEqual(slug, null);
});

#findSlug()

uses #loadSlug() result if non-null
const checker = new GitStatusChecker();
const testSlug = 'foo/bar';
const mock = sinon.mock(checker);
mock.expects('loadSlug')
  .once().withExactArgs().returns(Promise.resolve(testSlug));
mock.expects('detectSlug').never();
return checker.findSlug().then((slug) => {
  assert.strictEqual(slug, testSlug);
});
uses #detectSlug() result if #loadSlug() is null
const checker = new GitStatusChecker();
const testSlug = 'foo/bar';
const mock = sinon.mock(checker);
mock.expects('loadSlug')
  .once().withExactArgs().returns(Promise.resolve(null));
mock.expects('detectSlug')
  .once().withExactArgs().returns(Promise.resolve(testSlug));
return checker.findSlug().then((slug) => {
  assert.strictEqual(slug, testSlug);
});

travisStatus integration

fetches branch state
const testSlug = 'foo/bar';
const testBranch = 'branch1';
const testOpts = { slug: testSlug, branch: testBranch };
const testResult = apiResponses.branch(testOpts);
apiMock.expects('branch')
  .once().withExactArgs(match(testOpts))
  .returns(testResult);
apiMock.expects('build').never();
apiMock.expects('repo').never();
const options = {
  apiEndpoint: apiUrl,
  branch: testBranch,
  repo: testSlug,
};
return travisStatus(options).then((result) => {
  assert.deepEqual(result, testResult);
  apiMock.verify();
  assert.strictEqual(connCount, 1);
});
fetches repo state
const testSlug = 'foo/bar';
const testOpts = { slug: testSlug };
const testResult = apiResponses.repo(testOpts);
apiMock.expects('branch').never();
apiMock.expects('build').never();
apiMock.expects('repo')
  .once().withExactArgs(match(testOpts))
  .returns(testResult);
const options = {
  apiEndpoint: apiUrl,
  repo: testSlug,
};
return travisStatus(options).then((result) => {
  assert.deepEqual(result, testResult);
  apiMock.verify();
  assert.strictEqual(connCount, 1);
});
fetches repo and build for commit
const testSlug = 'foo/bar';
const testCommit = '4e2c26acca22601fb54da35485faff7c303084eb';
const testBuildId = 123456;
const testOpts = {
  buildId: testBuildId,
  sha: testCommit,
  slug: testSlug,
};
const testBuild = apiResponses.build(testOpts);
const testRepo = apiResponses.repo(testOpts);
apiMock.expects('branch').never();
apiMock.expects('build')
  .once().withExactArgs(match({ buildId: String(testBuildId) }))
  .returns(testBuild);
apiMock.expects('repo')
  .once().withExactArgs(match({ slug: testSlug }))
  .returns(testRepo);
const options = {
  apiEndpoint: apiUrl,
  commit: testCommit,
  repo: testSlug,
};
return travisStatus(options).then((result) => {
  assert.deepEqual(result, { ...testRepo, ...testBuild });
  apiMock.verify();
  // If Agent doesn't have .destroy(), travisStatus can't do keep-alive.
  // TODO:  Check that travisStatusCmd does.
  if (typeof new http.Agent().destroy === 'function') {
    assert.strictEqual(connCount, 1);
  }
});
fetches repo state with wait
const testSlug = 'foo/bar';
const pendingResult =
  apiResponses.repo({ slug: testSlug, state: 'started' });
const passedResult = apiResponses.repo({ slug: testSlug });
apiMock.expects('branch').never();
apiMock.expects('build').never();
const expect = apiMock.expects('repo')
  .atLeast(2)
  .withExactArgs(match({ slug: testSlug }));
// We don't want to over-specify the timeout/backoff values.
// So extra calls are added to ensure it is long enough to exceed the
// keep-alive timeout.
for (let i = 0; i < 5; i += 1) {
  expect.onCall(i).returns(pendingResult);
}
expect.onCall(5).returns(passedResult);
const options = {
  apiEndpoint: apiUrl,
  repo: testSlug,
  wait: Infinity,
};
const promise = travisStatus(options).then((result) => {
  assert.deepEqual(result, passedResult);
  apiMock.verify();
});
return promise;

TravisStatusChecker

throws TypeError for non-object options
assert.throws(
  // eslint-disable-next-line no-new
  () => { new TravisStatusChecker(true); },
  TypeError,
  /\boptions\b/,
);
passes {pro: true} to Travis when apiEndpoint is PRO_URI
TravisMock = sinon.mock()
  .once()
  .withExactArgs(
    match({ pro: true }),
  );
// eslint-disable-next-line no-new
new TravisStatusChecker({
  apiEndpoint: TravisStatusChecker.PRO_URI,
});
TravisMock.verify();
passes options.token to agent.setAccessToken
const testToken = '123456';
travisHttpMock = new TravisStatusHttp();
const mock = sinon.mock(travisHttpMock);
mock.expects('setAccessToken').once().withExactArgs(testToken);
// eslint-disable-next-line no-new
new TravisStatusChecker({
  token: testToken,
});
mock.verify();

#getBranch()

returns Travis CI API resource
travisRequestMock = sinon.mock()
  .once()
  .withArgs(match(/GET/i), match(travisUrlRe))
  .yields(null, passedResponse);
const checker = new TravisStatusChecker();
const promise = checker[methodName](...args)
  .then((response) => {
    assert.deepEqual(response, passedResponse);
  });
travisRequestMock.verify();
return promise;

with options.wait

does not wait if state is not pending
travisRequestMock = sinon.mock()
  .once()
  .withArgs(match(/GET/i), match(travisUrlRe))
  .yields(null, passedResponse);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 10000 }))
    .then((response) => {
      assert.deepEqual(response, passedResponse);
    });
travisRequestMock.verify();
return promise;
retries during wait if state is pending
travisRequestMock = sinon.mock()
  .twice()
  .withArgs(match(/GET/i), match(travisUrlRe));
travisRequestMock.onFirstCall().yields(null, pendingResponse);
travisRequestMock.onSecondCall().yields(null, passedResponse);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 10000 }))
    .then((response) => {
      assert.deepEqual(response, passedResponse);
    });
for (let i = 1; i < 11; i += 1) {
  clock.tick(1000);
}
travisRequestMock.verify();
return promise;
returns pending state if wait elapses
travisRequestMock = sinon.mock()
  .atLeast(1)
  .withArgs(match(/GET/i), match(travisUrlRe))
  .yields(null, pendingResponse);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 10000 }))
    .then((response) => {
      assert.deepEqual(response, pendingResponse);
    });
for (let i = 1; i < 11; i += 1) {
  clock.tick(1000);
}
travisRequestMock.verify();
return promise;
does not wait after API error
const errTest = new Error('Test API error');
travisRequestMock = sinon.mock()
  .once()
  .withArgs(match(/GET/i), match(travisUrlRe))
  .yields(errTest);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 30000 }))
    .then(
      sinon.mock().never(),
      (err) => {
        assert.strictEqual(err, errTest);
      },
    );
travisRequestMock.verify();
return promise;
stops waiting after API error
const errTest = new Error('Test API error');
travisRequestMock = sinon.mock()
  .twice()
  .withArgs(match(/GET/i), match(travisUrlRe));
travisRequestMock.onFirstCall().yields(null, pendingResponse);
travisRequestMock.onSecondCall().yields(errTest);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 30000 }))
    .then(
      sinon.mock().never(),
      (err) => {
        assert.strictEqual(err, errTest);
      },
    );
for (let i = 1; i < 31; i += 1) {
  clock.tick(1000);
}
travisRequestMock.verify();
return promise;
rejects with TypeError for non-number wait
travisRequestMock = sinon.mock().never();
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 'hello' }))
    .then(
      sinon.mock().never(),
      (err) => {
        assert.strictEqual(err.name, 'TypeError');
        assert.match(err.message, /\bwait\b/);
      },
    );
travisRequestMock.verify();
return promise;
rejects with RangeError for negative wait
travisRequestMock = sinon.mock().never();
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: -5 }))
    .then(
      sinon.mock().never(),
      (err) => {
        assert.strictEqual(err.name, 'RangeError');
        assert.match(err.message, /\bwait\b/);
      },
    );
travisRequestMock.verify();
return promise;

#getBuild()

returns Travis CI API resource
travisRequestMock = sinon.mock()
  .once()
  .withArgs(match(/GET/i), match(travisUrlRe))
  .yields(null, passedResponse);
const checker = new TravisStatusChecker();
const promise = checker[methodName](...args)
  .then((response) => {
    assert.deepEqual(response, passedResponse);
  });
travisRequestMock.verify();
return promise;

with options.wait

does not wait if state is not pending
travisRequestMock = sinon.mock()
  .once()
  .withArgs(match(/GET/i), match(travisUrlRe))
  .yields(null, passedResponse);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 10000 }))
    .then((response) => {
      assert.deepEqual(response, passedResponse);
    });
travisRequestMock.verify();
return promise;
retries during wait if state is pending
travisRequestMock = sinon.mock()
  .twice()
  .withArgs(match(/GET/i), match(travisUrlRe));
travisRequestMock.onFirstCall().yields(null, pendingResponse);
travisRequestMock.onSecondCall().yields(null, passedResponse);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 10000 }))
    .then((response) => {
      assert.deepEqual(response, passedResponse);
    });
for (let i = 1; i < 11; i += 1) {
  clock.tick(1000);
}
travisRequestMock.verify();
return promise;
returns pending state if wait elapses
travisRequestMock = sinon.mock()
  .atLeast(1)
  .withArgs(match(/GET/i), match(travisUrlRe))
  .yields(null, pendingResponse);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 10000 }))
    .then((response) => {
      assert.deepEqual(response, pendingResponse);
    });
for (let i = 1; i < 11; i += 1) {
  clock.tick(1000);
}
travisRequestMock.verify();
return promise;
does not wait after API error
const errTest = new Error('Test API error');
travisRequestMock = sinon.mock()
  .once()
  .withArgs(match(/GET/i), match(travisUrlRe))
  .yields(errTest);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 30000 }))
    .then(
      sinon.mock().never(),
      (err) => {
        assert.strictEqual(err, errTest);
      },
    );
travisRequestMock.verify();
return promise;
stops waiting after API error
const errTest = new Error('Test API error');
travisRequestMock = sinon.mock()
  .twice()
  .withArgs(match(/GET/i), match(travisUrlRe));
travisRequestMock.onFirstCall().yields(null, pendingResponse);
travisRequestMock.onSecondCall().yields(errTest);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 30000 }))
    .then(
      sinon.mock().never(),
      (err) => {
        assert.strictEqual(err, errTest);
      },
    );
for (let i = 1; i < 31; i += 1) {
  clock.tick(1000);
}
travisRequestMock.verify();
return promise;
rejects with TypeError for non-number wait
travisRequestMock = sinon.mock().never();
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 'hello' }))
    .then(
      sinon.mock().never(),
      (err) => {
        assert.strictEqual(err.name, 'TypeError');
        assert.match(err.message, /\bwait\b/);
      },
    );
travisRequestMock.verify();
return promise;
rejects with RangeError for negative wait
travisRequestMock = sinon.mock().never();
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: -5 }))
    .then(
      sinon.mock().never(),
      (err) => {
        assert.strictEqual(err.name, 'RangeError');
        assert.match(err.message, /\bwait\b/);
      },
    );
travisRequestMock.verify();
return promise;

#getRepo()

returns Travis CI API resource
travisRequestMock = sinon.mock()
  .once()
  .withArgs(match(/GET/i), match(travisUrlRe))
  .yields(null, passedResponse);
const checker = new TravisStatusChecker();
const promise = checker[methodName](...args)
  .then((response) => {
    assert.deepEqual(response, passedResponse);
  });
travisRequestMock.verify();
return promise;

with options.wait

does not wait if state is not pending
travisRequestMock = sinon.mock()
  .once()
  .withArgs(match(/GET/i), match(travisUrlRe))
  .yields(null, passedResponse);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 10000 }))
    .then((response) => {
      assert.deepEqual(response, passedResponse);
    });
travisRequestMock.verify();
return promise;
retries during wait if state is pending
travisRequestMock = sinon.mock()
  .twice()
  .withArgs(match(/GET/i), match(travisUrlRe));
travisRequestMock.onFirstCall().yields(null, pendingResponse);
travisRequestMock.onSecondCall().yields(null, passedResponse);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 10000 }))
    .then((response) => {
      assert.deepEqual(response, passedResponse);
    });
for (let i = 1; i < 11; i += 1) {
  clock.tick(1000);
}
travisRequestMock.verify();
return promise;
returns pending state if wait elapses
travisRequestMock = sinon.mock()
  .atLeast(1)
  .withArgs(match(/GET/i), match(travisUrlRe))
  .yields(null, pendingResponse);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 10000 }))
    .then((response) => {
      assert.deepEqual(response, pendingResponse);
    });
for (let i = 1; i < 11; i += 1) {
  clock.tick(1000);
}
travisRequestMock.verify();
return promise;
does not wait after API error
const errTest = new Error('Test API error');
travisRequestMock = sinon.mock()
  .once()
  .withArgs(match(/GET/i), match(travisUrlRe))
  .yields(errTest);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 30000 }))
    .then(
      sinon.mock().never(),
      (err) => {
        assert.strictEqual(err, errTest);
      },
    );
travisRequestMock.verify();
return promise;
stops waiting after API error
const errTest = new Error('Test API error');
travisRequestMock = sinon.mock()
  .twice()
  .withArgs(match(/GET/i), match(travisUrlRe));
travisRequestMock.onFirstCall().yields(null, pendingResponse);
travisRequestMock.onSecondCall().yields(errTest);
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 30000 }))
    .then(
      sinon.mock().never(),
      (err) => {
        assert.strictEqual(err, errTest);
      },
    );
for (let i = 1; i < 31; i += 1) {
  clock.tick(1000);
}
travisRequestMock.verify();
return promise;
rejects with TypeError for non-number wait
travisRequestMock = sinon.mock().never();
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: 'hello' }))
    .then(
      sinon.mock().never(),
      (err) => {
        assert.strictEqual(err.name, 'TypeError');
        assert.match(err.message, /\bwait\b/);
      },
    );
travisRequestMock.verify();
return promise;
rejects with RangeError for negative wait
travisRequestMock = sinon.mock().never();
const checker = new TravisStatusChecker();
const promise =
  checker[methodName](...args.concat({ wait: -5 }))
    .then(
      sinon.mock().never(),
      (err) => {
        assert.strictEqual(err.name, 'RangeError');
        assert.match(err.message, /\bwait\b/);
      },
    );
travisRequestMock.verify();
return promise;

travis-status command

accepts empty arguments
travisStatus = sinon.mock()
  .once()
  .withArgs(
    match.any,
    match.func,
  );
travisStatusCmd([], sinon.mock().never());
travisStatus.verify();
returns undefined when called with a function
travisStatus = sinon.mock()
  .once()
  .withArgs(
    match.any,
    match.func,
  );
const result = travisStatusCmd(RUNTIME_ARGS, sinon.mock().never());
travisStatus.verify();
assert.strictEqual(result, undefined);
default interactive true for TTY stdout
travisStatus = sinon.mock()
  .once()
  .withArgs(
    match({ interactive: true }),
    match.func,
  );
const outStream = new stream.PassThrough();
outStream.isTTY = true;
const options = {
  out: outStream,
  err: new stream.PassThrough(),
};
travisStatusCmd(RUNTIME_ARGS, options, sinon.mock().never());
travisStatus.verify();
interprets as match(apiEndpoint: undefined, branch: undefined, commit: undefined, repo: undefined, requestOpts: not insecure, storeRepo: undefined, token: undefined, wait: undefined)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --api-endpoint https://example.com as match(apiEndpoint: https://example.com)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --branch branchname as match(branch: branchname)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --branch as match(branch: true)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --commit v1.0.0 as match(commit: v1.0.0)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --commit as match(commit: HEAD)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --debug as typeOf("object")
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --debug-http as typeOf("object")
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --explode as typeOf("object")
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --insecure as match(requestOpts: match(strictSSL: false))
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --interactive as match(interactive: true)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --org as match(apiEndpoint: https://api.travis-ci.org/)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --pro as match(apiEndpoint: https://api.travis-ci.com/)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --repo foo/bar as match(repo: foo/bar)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --skip-completion-check as typeOf("object")
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --skip-version-check as typeOf("object")
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --staging as match(apiEndpoint: https://api-staging.travis-ci.org/)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --org --staging as match(apiEndpoint: https://api-staging.travis-ci.org/)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --pro --staging as match(apiEndpoint: https://api-staging.travis-ci.com/)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --store-repo foo/bar as match(storeRepo: foo/bar)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --token 12345 as match(token: 12345)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --wait 60 as match(wait: 60000)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --wait as match(wait: Infinity)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets -E as typeOf("object")
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets -I as match(requestOpts: match(strictSSL: false))
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets -R foo/bar as match(storeRepo: foo/bar)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets -b branchname as match(branch: branchname)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets -b as match(branch: true)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets -c v1.0.0 as match(commit: v1.0.0)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets -c as match(commit: HEAD)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets -e https://example.com as match(apiEndpoint: https://example.com)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets -i as match(interactive: true)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets -r foo/bar as match(repo: foo/bar)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets -t 12345 as match(token: 12345)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets -w 60 as match(wait: 60000)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets -w as match(wait: Infinity)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --repo foo/bar --store-repo baz/quux as match(repo: baz/quux, storeRepo: baz/quux)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --store-repo foo/bar --repo baz/quux as match(repo: baz/quux, storeRepo: foo/bar)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --api-endpoint https://example.com --org --pro as match(apiEndpoint: https://api.travis-ci.com/)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --pro --org --api-endpoint https://example.com as match(apiEndpoint: https://example.com)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --pro --staging as match(apiEndpoint: https://api-staging.travis-ci.com/)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
interprets --staging --pro as match(apiEndpoint: https://api.travis-ci.com/)
travisStatus = sinon.mock()
  .once()
  .withArgs(
    expectObj,
    match.func,
  );
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, sinon.mock().never());
travisStatus.verify();
prints error and exits for -w nope
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.isAtLeast(code, 1);
  assert.strictEqual(outStream.read(), null);
  assert.match(String(errStream.read()), expectErrMsg);
  done();
});
prints error and exits for --unknown
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.isAtLeast(code, 1);
  assert.strictEqual(outStream.read(), null);
  assert.match(String(errStream.read()), expectErrMsg);
  done();
});
prints error and exits for extraarg
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.isAtLeast(code, 1);
  assert.strictEqual(outStream.read(), null);
  assert.match(String(errStream.read()), expectErrMsg);
  done();
});
normally exits with code 0 if build failed
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
with -x exits with code 1 if build canceled
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
with -x exits with code 1 if build errored
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
with -x exits with code 1 if build failed
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
with --exit-code exits with code 1 if build canceled
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
with --exit-code exits with code 1 if build errored
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
with --exit-code exits with code 1 if build failed
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
normally exits with code 0 if build queued
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
with -p exits with code 1 if build created
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
with -p exits with code 1 if build queued
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
with -p exits with code 1 if build received
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
with -p exits with code 1 if build started
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
with --fail-pending exits with code 1 if build created
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
with --fail-pending exits with code 1 if build queued
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
with --fail-pending exits with code 1 if build received
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
with --fail-pending exits with code 1 if build started
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const allArgs = RUNTIME_ARGS.concat(args);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, expectCode);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
-q exits without printing state
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const allArgs = RUNTIME_ARGS.concat(arg);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, 0);
  assert.strictEqual(outStream.read(), null);
  assert.strictEqual(errStream.read(), null);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state: 'failed' }),
);
--quiet exits without printing state
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const allArgs = RUNTIME_ARGS.concat(arg);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, 0);
  assert.strictEqual(outStream.read(), null);
  assert.strictEqual(errStream.read(), null);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state: 'failed' }),
);
prints build number and state for repo to stdout
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const buildNum = 500;
const state = 'passed';
const allArgs = RUNTIME_ARGS.concat(isBranch ? ['--branch'] : []);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, 0);
  assert.strictEqual(
    String(outStream.read()),
    // We are strict about this format since other programs may use it
    `build #${buildNum} ${state}\n`,
  );
  assert.strictEqual(errStream.read(), null);
  done();
});
travisStatus.yield(
  null,
  isBranch
    ? apiResponses.branch({ number: buildNum, state })
    : apiResponses.repo({ number: buildNum, state }),
);
prints build number and state for branch to stdout
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const buildNum = 500;
const state = 'passed';
const allArgs = RUNTIME_ARGS.concat(isBranch ? ['--branch'] : []);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, 0);
  assert.strictEqual(
    String(outStream.read()),
    // We are strict about this format since other programs may use it
    `build #${buildNum} ${state}\n`,
  );
  assert.strictEqual(errStream.read(), null);
  done();
});
travisStatus.yield(
  null,
  isBranch
    ? apiResponses.branch({ number: buildNum, state })
    : apiResponses.repo({ number: buildNum, state }),
);
prints canceled in red if interactive
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const allArgs = RUNTIME_ARGS.concat(['--interactive']);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, 0);
  const outString = String(outStream.read());
  assert.include(
    outString,
    ansiStyles[color].open + state + ansiStyles[color].close,
  );
  assert.strictEqual(errStream.read(), null);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
prints created in yellow if interactive
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const allArgs = RUNTIME_ARGS.concat(['--interactive']);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, 0);
  const outString = String(outStream.read());
  assert.include(
    outString,
    ansiStyles[color].open + state + ansiStyles[color].close,
  );
  assert.strictEqual(errStream.read(), null);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
prints errored in red if interactive
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const allArgs = RUNTIME_ARGS.concat(['--interactive']);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, 0);
  const outString = String(outStream.read());
  assert.include(
    outString,
    ansiStyles[color].open + state + ansiStyles[color].close,
  );
  assert.strictEqual(errStream.read(), null);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
prints failed in red if interactive
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const allArgs = RUNTIME_ARGS.concat(['--interactive']);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, 0);
  const outString = String(outStream.read());
  assert.include(
    outString,
    ansiStyles[color].open + state + ansiStyles[color].close,
  );
  assert.strictEqual(errStream.read(), null);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
prints passed in green if interactive
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const allArgs = RUNTIME_ARGS.concat(['--interactive']);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, 0);
  const outString = String(outStream.read());
  assert.include(
    outString,
    ansiStyles[color].open + state + ansiStyles[color].close,
  );
  assert.strictEqual(errStream.read(), null);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
prints queued in yellow if interactive
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const allArgs = RUNTIME_ARGS.concat(['--interactive']);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, 0);
  const outString = String(outStream.read());
  assert.include(
    outString,
    ansiStyles[color].open + state + ansiStyles[color].close,
  );
  assert.strictEqual(errStream.read(), null);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
prints ready in green if interactive
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const allArgs = RUNTIME_ARGS.concat(['--interactive']);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, 0);
  const outString = String(outStream.read());
  assert.include(
    outString,
    ansiStyles[color].open + state + ansiStyles[color].close,
  );
  assert.strictEqual(errStream.read(), null);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
prints received in yellow if interactive
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const allArgs = RUNTIME_ARGS.concat(['--interactive']);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, 0);
  const outString = String(outStream.read());
  assert.include(
    outString,
    ansiStyles[color].open + state + ansiStyles[color].close,
  );
  assert.strictEqual(errStream.read(), null);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
prints started in yellow if interactive
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const allArgs = RUNTIME_ARGS.concat(['--interactive']);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.strictEqual(code, 0);
  const outString = String(outStream.read());
  assert.include(
    outString,
    ansiStyles[color].open + state + ansiStyles[color].close,
  );
  assert.strictEqual(errStream.read(), null);
  done();
});
travisStatus.yield(
  null,
  apiResponses.repo({ state }),
);
prints error messages in red if interactive
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const errMsg = 'super duper test error';
const allArgs = RUNTIME_ARGS.concat(['--interactive']);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.isAtLeast(code, 1);
  assert.strictEqual(outStream.read(), null);
  const errString = String(errStream.read());
  assert.include(errString, ansiStyles.red.open);
  assert.include(errString, errMsg);
  done();
});
travisStatus.yield(new Error(errMsg));
returns TypeError for non-array-like args
travisStatusCmd({}, (err) => {
  assert.instanceOf(err, TypeError);
  assert.match(err.message, /\bargs\b/);
  done();
});
returns RangeError for single argument
travisStatusCmd(['node'], (err) => {
  assert.instanceOf(err, RangeError);
  assert.match(err.message, /\bargs\b/);
  done();
});
throws for non-function callback
assert.throws(
  () => { travisStatusCmd(RUNTIME_ARGS, {}, true); },
  TypeError,
  /\bcallback\b/,
);
returns Error for non-object options
travisStatusCmd([], true, (err) => {
  assert.instanceOf(err, TypeError);
  assert.match(err.message, /\boptions\b/);
  done();
});
returns Error for non-Readable in
travisStatusCmd([], { in: new stream.Writable() }, (err) => {
  assert.instanceOf(err, TypeError);
  assert.match(err.message, /\boptions.in\b/);
  done();
});
returns Error for non-Writable out
travisStatusCmd([], { out: new stream.Readable() }, (err) => {
  assert.instanceOf(err, TypeError);
  assert.match(err.message, /\boptions.out\b/);
  done();
});
returns Error for non-Writable err
travisStatusCmd([], { err: new stream.Readable() }, (err) => {
  assert.instanceOf(err, TypeError);
  assert.match(err.message, /\boptions.err\b/);
  done();
});
prints error messages in red if interactive
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const errMsg = 'super duper test error';
const allArgs = RUNTIME_ARGS.concat(['--interactive']);
travisStatusCmd(allArgs, options, (err, code) => {
  assert.ifError(err);
  assert.isAtLeast(code, 1);
  assert.strictEqual(outStream.read(), null);
  const errString = String(errStream.read());
  assert.include(errString, ansiStyles.red.open);
  assert.include(errString, errMsg);
  done();
});
travisStatus.yield(new Error(errMsg));
prints error messages without color if not interactive
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
const errMsg = 'super duper test error';
travisStatusCmd(RUNTIME_ARGS, options, (err, code) => {
  assert.ifError(err);
  assert.isAtLeast(code, 1);
  assert.strictEqual(outStream.read(), null);
  const errString = String(errStream.read());
  assert(!hasAnsi(errString), 'string has color');
  assert.include(errString, errMsg);
  done();
});
travisStatus.yield(new Error(errMsg));
prints a help message for SlugDetectionError
travisStatus = sinon.stub();
const outStream = new stream.PassThrough();
const errStream = new stream.PassThrough();
const options = {
  out: outStream,
  err: errStream,
};
travisStatusCmd(RUNTIME_ARGS, options, (err, code) => {
  assert.ifError(err);
  assert.isAtLeast(code, 1);
  assert.strictEqual(outStream.read(), null);
  assert.match(String(errStream.read()), /\brepo name\b.*-r/i);
  done();
});
travisStatus.yield(new SlugDetectionError('oops'));
returns a Promise when called without a function
travisStatus = sinon.stub();
const result = travisStatusCmd(RUNTIME_ARGS);
assert(result instanceof Promise);
returned Promise is resolved with exit code
travisStatus = sinon.stub();
const options = {
  out: new stream.PassThrough(),
  err: new stream.PassThrough(),
};
const result = travisStatusCmd(RUNTIME_ARGS, options);
travisStatus.yield(
  null,
  apiResponses.repo(),
);
return result.then((code) => {
  assert.strictEqual(code, 0);
});
returned Promise is rejected with Error
travisStatus = sinon.stub();
const result = travisStatusCmd(RUNTIME_ARGS, true);
return result.then(
  sinon.mock().never(),
  (err) => { assert.instanceOf(err, TypeError); },
);

TravisStatusHttp

throws TypeError for non-string endpoint
assert.throws(
  // eslint-disable-next-line no-new
  () => { new TravisStatusHttp(true); },
  TypeError,
  /\bendpoint\b/,
);
throws TypeError for non-object options
assert.throws(
  // eslint-disable-next-line no-new
  () => { new TravisStatusHttp(null, true); },
  TypeError,
  /\boptions\b/,
);

#request()

accepts Travis and JSON media types by default
const status = new TravisStatusHttp();
request = sinon.mock()
  .once()
  .withArgs(match({
    headers: match({
      Accept: match((accept) => {
        const travisRE = /^application\/vnd\.travis-ci\.2\+json(?:,|$)/;
        return travisRE.test(accept)
          && / application\/json(?:,|$)/.test(accept);
      }, 'match Travis and JSON media types'),
    }),
  }));
status.request('GET', '/repos', () => {});
request.verify();
can send custom accept header
const testAccept = 'text/plain';
// Note:  Testing lower case properly replaces upper
const status =
  new TravisStatusHttp(null, { headers: { accept: testAccept } });
request = sinon.mock()
  .once()
  .withArgs(match({
    headers: match({
      Accept: undefined,
      accept: testAccept,
    }),
  }));
status.request('GET', '/repos', () => {});
request.verify();
supports gzip by default
const status = new TravisStatusHttp();
request = sinon.mock()
  .once()
  .withArgs(match({ gzip: true }));
status.request('GET', '/repos', () => {});
request.verify();
sends User-Agent including module version by default
const uaVersionRE = new RegExp(`node-travis-status/${
  packageJson.version.replace(/\./g, '\\.')}`);
const status = new TravisStatusHttp();
request = sinon.mock()
  .once()
  .withArgs(match({
    headers: match({
      'User-Agent': match(uaVersionRE),
    }),
  }));
status.request('GET', '/repos', () => {});
request.verify();
can send custom user-agent header
const testUA = 'Test Agent';
// Note:  Testing lower case properly replaces upper
const status =
  new TravisStatusHttp(null, { headers: { 'user-agent': testUA } });
request = sinon.mock()
  .once()
  .withArgs(match({
    headers: match({
      'User-Agent': undefined,
      'user-agent': testUA,
    }),
  }));
status.request('GET', '/repos', () => {});
request.verify();
builds Authorization header from setAccessToken
const testToken = '12345';
const status = new TravisStatusHttp();
status.setAccessToken(testToken);
request = sinon.mock()
  .once()
  .withArgs(match({
    headers: match({
      Authorization: `token ${testToken}`,
    }),
  }));
status.request('GET', '/repos', () => {});
request.verify();
builds quoted Authorization header from setAccessToken
const testToken = '12345"67\\89';
const quotedToken = '"12345\\"67\\\\89"';
const status = new TravisStatusHttp();
status.setAccessToken(testToken);
request = sinon.mock()
  .once()
  .withArgs(match({
    headers: match({
      Authorization: `token ${quotedToken}`,
    }),
  }));
status.request('GET', '/repos', () => {});
request.verify();
setAccessToken overrides options.headers.Authorization
const testToken = '12345';
const status =
  new TravisStatusHttp(null, { headers: { Authorization: 'foo' } });
status.setAccessToken(testToken);
request = sinon.mock()
  .once()
  .withArgs(match({
    headers: match({
      Authorization: `token ${testToken}`,
    }),
  }));
status.request('GET', '/repos', () => {});
request.verify();
returns errors from request
const errTest = new Error('Test request error');
const status = new TravisStatusHttp();
request = sinon.mock().once().yields(errTest);
status.request('GET', '/repos', (err) => {
  assert.strictEqual(err, errTest);
});
request.verify();
returns errors for HTTP status >= 400
const status = new TravisStatusHttp();
const errProps = {
  statusCode: 400,
  statusMessage: 'Test Message',
  headers: {
    'Content-Type': 'application/json',
    test: 'ok',
  },
};
const testBody = { test: 'stuff' };
const testBodyStr = JSON.stringify(testBody);
const response = new http.IncomingMessage();
Object.assign(response, errProps);
request = sinon.mock().once().yields(null, response, testBodyStr);
status.request('GET', '/repos', (err) => {
  assert.strictEqual(err.message, errProps.statusMessage);
  assert.deepEqual(
    { ...err },
    { body: testBody, ...errProps },
  );
});
request.verify();
returns errors for non-JSON
const status = new TravisStatusHttp();
const errProps = {
  statusCode: 200,
  statusMessage: 'Test Message',
  headers: {
    'Content-Type': 'text/plain',
    test: 'ok',
  },
};
const testBody = 'Body?';
let testErr;
try { JSON.parse(testBody); } catch (errJson) { testErr = errJson; }
const response = new http.IncomingMessage();
Object.assign(response, errProps);
request = sinon.mock().once().yields(null, response, testBody);
status.request('GET', '/repos', (err) => {
  assert.strictEqual(err.message, testErr.message);
  assert.deepEqual(
    { ...err },
    { body: testBody, ...errProps },
  );
});
request.verify();
returns HTTP errors in preference to JSON
const status = new TravisStatusHttp();
const errProps = {
  statusCode: 400,
  statusMessage: 'Test Message',
  headers: {
    'Content-Type': 'text/plain',
    test: 'ok',
  },
};
const testBody = 'Body?';
const response = new http.IncomingMessage();
Object.assign(response, errProps);
request = sinon.mock().once().yields(null, response, testBody);
status.request('GET', '/repos', (err) => {
  assert.strictEqual(err.message, errProps.statusMessage);
  assert.deepEqual(
    { ...err },
    { body: testBody, ...errProps },
  );
});
request.verify();
returns body JSON without Error
const status = new TravisStatusHttp();
const errProps = {
  statusCode: 200,
  statusMessage: 'Test Message',
  headers: {
    'Content-Type': 'application/json',
    test: 'ok',
  },
};
const testBody = { prop: 'OK' };
const testBodyStr = JSON.stringify(testBody);
const response = new http.IncomingMessage();
Object.assign(response, errProps);
request = sinon.mock().once().yields(null, response, testBodyStr);
status.request('GET', '/repos', (err, body) => {
  assert.deepEqual(body, testBody);
});
request.verify();

travisStatus

fetches current repo without storing by default
const testSlug = 'foo/bar';
const testResult = {};
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('findSlug')
  .once().withExactArgs().returns(Promise.resolve(testSlug));
gitCheckerMock.expects('storeSlug').never();
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo')
  .once().withArgs(testSlug).returns(Promise.resolve(testResult));
travisCheckerMock.expects('getBranch').never();
travisCheckerMock.expects('getBuild').never();
return travisStatus().then((result) => {
  assert.deepEqual(result, testResult);
  gitCheckerMock.verify();
  travisCheckerMock.verify();
});
fetches current repo and stores if interactive
const testSlug = 'foo/bar';
const testResult = {};
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('findSlug')
  .once().withExactArgs().returns(Promise.resolve(testSlug));
gitCheckerMock.expects('storeSlug')
  .once().withExactArgs(testSlug).returns(Promise.resolve(testSlug));
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo')
  .once().withArgs(testSlug).returns(Promise.resolve(testResult));
travisCheckerMock.expects('getBranch').never();
travisCheckerMock.expects('getBuild').never();
return travisStatus({ interactive: true }).then((result) => {
  assert.deepEqual(result, testResult);
  gitCheckerMock.verify();
  travisCheckerMock.verify();
});
fetches options.repo
const testSlug = 'foo/bar';
const testResult = {};
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('findSlug').never();
gitCheckerMock.expects('storeSlug').never();
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo')
  .once().withArgs(testSlug).returns(Promise.resolve(testResult));
travisCheckerMock.expects('getBranch').never();
travisCheckerMock.expects('getBuild').never();
return travisStatus({ repo: testSlug }).then((result) => {
  assert.deepEqual(result, testResult);
  gitCheckerMock.verify();
  travisCheckerMock.verify();
});
fetches and stores options.storeRepo
const testSlug = 'foo/bar';
const testResult = {};
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('storeSlug')
  .once().withExactArgs(testSlug).returns(Promise.resolve(testSlug));
gitCheckerMock.expects('findSlug').never();
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo')
  .once().withArgs(testSlug).returns(Promise.resolve(testResult));
travisCheckerMock.expects('getBranch').never();
travisCheckerMock.expects('getBuild').never();
return travisStatus({ storeRepo: testSlug }).then((result) => {
  assert.deepEqual(result, testResult);
  gitCheckerMock.verify();
  travisCheckerMock.verify();
});
fetches options.repo, stores options.storeRepo
const testSlug = 'foo/bar';
const testSlug2 = 'baz/quux';
const testResult = {};
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('storeSlug')
  .once().withExactArgs(testSlug2).returns(Promise.resolve(testSlug2));
gitCheckerMock.expects('findSlug').never();
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo')
  .once().withArgs(testSlug).returns(Promise.resolve(testResult));
travisCheckerMock.expects('getBranch').never();
travisCheckerMock.expects('getBuild').never();
const options = { repo: testSlug, storeRepo: testSlug2 };
return travisStatus(options).then((result) => {
  assert.deepEqual(result, testResult);
  gitCheckerMock.verify();
  travisCheckerMock.verify();
});
fetches named branch for options.branch
const testSlug = 'foo/bar';
const testBranch = 'branch1';
const testResult = {};
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('detectBranch').never();
gitCheckerMock.expects('findSlug').never();
gitCheckerMock.expects('storeSlug').never();
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo').never();
travisCheckerMock.expects('getBranch')
  .once().withArgs(testSlug, testBranch)
  .returns(Promise.resolve(testResult));
travisCheckerMock.expects('getBuild').never();
const options = { branch: testBranch, repo: testSlug };
return travisStatus(options).then((result) => {
  assert.deepEqual(result, testResult);
  gitCheckerMock.verify();
  travisCheckerMock.verify();
});
fetches current branch for true options.branch
const testSlug = 'foo/bar';
const testBranch = 'branch1';
const testResult = {};
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('detectBranch')
  .once().withExactArgs().returns(Promise.resolve(testBranch));
gitCheckerMock.expects('findSlug').never();
gitCheckerMock.expects('storeSlug').never();
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo').never();
travisCheckerMock.expects('getBranch')
  .once().withArgs(testSlug, testBranch)
  .returns(Promise.resolve(testResult));
travisCheckerMock.expects('getBuild').never();
const options = { branch: true, repo: testSlug };
return travisStatus(options).then((result) => {
  assert.deepEqual(result, testResult);
  gitCheckerMock.verify();
  travisCheckerMock.verify();
});
resolves combined result for same commit hash
const testSlug = 'foo/bar';
const testHash = '692064aac95441e2dae7f1780fccc536143a0863';
const apiHash = isSameHash ? testHash : `${testHash.slice(0, -1)}0`;
const testCommit = commitIsHash ? testHash : 'v2.0.0';
const testRepo = apiResponses.repo({
  slug: testSlug,
});
const testBuild = apiResponses.build({ sha: apiHash });
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('resolveHash')
  .once().withExactArgs(testCommit).returns(Promise.resolve(testHash));
gitCheckerMock.expects('findSlug').never();
gitCheckerMock.expects('storeSlug').never();
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo')
  .once().withArgs(testSlug).returns(Promise.resolve(testRepo));
travisCheckerMock.expects('getBranch').never();
travisCheckerMock.expects('getBuild')
  .once().withArgs(testSlug, testRepo.repo.last_build_id)
  .returns(Promise.resolve(testBuild));
const statusP = travisStatus({ commit: testCommit, repo: testSlug });
let testP;
if (isSameHash) {
  testP = statusP.then((result) => {
    assert.deepEqual(result, { ...testRepo, ...testBuild });
  });
} else {
  testP = statusP.then(
    sinon.mock().never(),
    (err) => {
      assert.match(err.message, /\bcommit\b/i);
      assert.include(err.message, testCommit);
      assert.include(err.message, testHash);
      assert.include(err.message, apiHash);
    },
  );
}
return testP.then(() => {
  gitCheckerMock.verify();
  travisCheckerMock.verify();
});
resolves combined result for matching commit name
const testSlug = 'foo/bar';
const testHash = '692064aac95441e2dae7f1780fccc536143a0863';
const apiHash = isSameHash ? testHash : `${testHash.slice(0, -1)}0`;
const testCommit = commitIsHash ? testHash : 'v2.0.0';
const testRepo = apiResponses.repo({
  slug: testSlug,
});
const testBuild = apiResponses.build({ sha: apiHash });
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('resolveHash')
  .once().withExactArgs(testCommit).returns(Promise.resolve(testHash));
gitCheckerMock.expects('findSlug').never();
gitCheckerMock.expects('storeSlug').never();
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo')
  .once().withArgs(testSlug).returns(Promise.resolve(testRepo));
travisCheckerMock.expects('getBranch').never();
travisCheckerMock.expects('getBuild')
  .once().withArgs(testSlug, testRepo.repo.last_build_id)
  .returns(Promise.resolve(testBuild));
const statusP = travisStatus({ commit: testCommit, repo: testSlug });
let testP;
if (isSameHash) {
  testP = statusP.then((result) => {
    assert.deepEqual(result, { ...testRepo, ...testBuild });
  });
} else {
  testP = statusP.then(
    sinon.mock().never(),
    (err) => {
      assert.match(err.message, /\bcommit\b/i);
      assert.include(err.message, testCommit);
      assert.include(err.message, testHash);
      assert.include(err.message, apiHash);
    },
  );
}
return testP.then(() => {
  gitCheckerMock.verify();
  travisCheckerMock.verify();
});
rejects with Error for same commit hash
const testSlug = 'foo/bar';
const testHash = '692064aac95441e2dae7f1780fccc536143a0863';
const apiHash = isSameHash ? testHash : `${testHash.slice(0, -1)}0`;
const testCommit = commitIsHash ? testHash : 'v2.0.0';
const testRepo = apiResponses.repo({
  slug: testSlug,
});
const testBuild = apiResponses.build({ sha: apiHash });
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('resolveHash')
  .once().withExactArgs(testCommit).returns(Promise.resolve(testHash));
gitCheckerMock.expects('findSlug').never();
gitCheckerMock.expects('storeSlug').never();
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo')
  .once().withArgs(testSlug).returns(Promise.resolve(testRepo));
travisCheckerMock.expects('getBranch').never();
travisCheckerMock.expects('getBuild')
  .once().withArgs(testSlug, testRepo.repo.last_build_id)
  .returns(Promise.resolve(testBuild));
const statusP = travisStatus({ commit: testCommit, repo: testSlug });
let testP;
if (isSameHash) {
  testP = statusP.then((result) => {
    assert.deepEqual(result, { ...testRepo, ...testBuild });
  });
} else {
  testP = statusP.then(
    sinon.mock().never(),
    (err) => {
      assert.match(err.message, /\bcommit\b/i);
      assert.include(err.message, testCommit);
      assert.include(err.message, testHash);
      assert.include(err.message, apiHash);
    },
  );
}
return testP.then(() => {
  gitCheckerMock.verify();
  travisCheckerMock.verify();
});
rejects with Error for matching commit name
const testSlug = 'foo/bar';
const testHash = '692064aac95441e2dae7f1780fccc536143a0863';
const apiHash = isSameHash ? testHash : `${testHash.slice(0, -1)}0`;
const testCommit = commitIsHash ? testHash : 'v2.0.0';
const testRepo = apiResponses.repo({
  slug: testSlug,
});
const testBuild = apiResponses.build({ sha: apiHash });
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('resolveHash')
  .once().withExactArgs(testCommit).returns(Promise.resolve(testHash));
gitCheckerMock.expects('findSlug').never();
gitCheckerMock.expects('storeSlug').never();
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo')
  .once().withArgs(testSlug).returns(Promise.resolve(testRepo));
travisCheckerMock.expects('getBranch').never();
travisCheckerMock.expects('getBuild')
  .once().withArgs(testSlug, testRepo.repo.last_build_id)
  .returns(Promise.resolve(testBuild));
const statusP = travisStatus({ commit: testCommit, repo: testSlug });
let testP;
if (isSameHash) {
  testP = statusP.then((result) => {
    assert.deepEqual(result, { ...testRepo, ...testBuild });
  });
} else {
  testP = statusP.then(
    sinon.mock().never(),
    (err) => {
      assert.match(err.message, /\bcommit\b/i);
      assert.include(err.message, testCommit);
      assert.include(err.message, testHash);
      assert.include(err.message, apiHash);
    },
  );
}
return testP.then(() => {
  gitCheckerMock.verify();
  travisCheckerMock.verify();
});
rejects with TypeError for non-object options
travisStatus(true)
    .then(
      sinon.mock().never(),
      (err) => {
        assert.strictEqual(err.name, 'TypeError');
        assert.match(err.message, /\boptions\b/);
      },
    )
throws TypeError for non-function callback
assert.throws(
  () => { travisStatus({}, true); },
  TypeError,
  /\bcallback\b/,
);
rejects with InvalidSlugError for invalid options.repo
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('storeSlug').never();
gitCheckerMock.expects('findSlug').never();
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo').never();
travisCheckerMock.expects('getBranch').never();
travisCheckerMock.expects('getBuild').never();
return travisStatus({ repo: 'invalid' }).then(
  sinon.mock().never(),
  (err) => {
    assert.strictEqual(err.name, 'InvalidSlugError');
    gitCheckerMock.verify();
    travisCheckerMock.verify();
  },
);
rejects with InvalidSlugError for invalid options.storeRepo
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('storeSlug').never();
gitCheckerMock.expects('findSlug').never();
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo').never();
travisCheckerMock.expects('getBranch').never();
travisCheckerMock.expects('getBuild').never();
return travisStatus({ storeRepo: 'invalid' }).then(
  sinon.mock().never(),
  (err) => {
    assert.strictEqual(err.name, 'InvalidSlugError');
    gitCheckerMock.verify();
    travisCheckerMock.verify();
  },
);
yields result to callback without returning Promise
const testSlug = 'foo/bar';
const testResult = {};
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('findSlug')
  .once().withExactArgs().returns(Promise.resolve(testSlug));
gitCheckerMock.expects('storeSlug').never();
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo')
  .once().withArgs(testSlug).returns(Promise.resolve(testResult));
travisCheckerMock.expects('getBranch').never();
travisCheckerMock.expects('getBuild').never();
const retVal = travisStatus((err, result) => {
  assert.strictEqual(err, null);
  assert.deepEqual(result, testResult);
  gitCheckerMock.verify();
  travisCheckerMock.verify();
  done();
});
assert.strictEqual(retVal, undefined);
yields Error to callback without returning Promise
gitChecker = new GitStatusChecker();
const gitCheckerMock = sinon.mock(gitChecker);
gitCheckerMock.expects('storeSlug').never();
gitCheckerMock.expects('findSlug').never();
travisChecker = new TravisStatusChecker();
const travisCheckerMock = sinon.mock(travisChecker);
travisCheckerMock.expects('getRepo').never();
travisCheckerMock.expects('getBranch').never();
travisCheckerMock.expects('getBuild').never();
const retVal = travisStatus({ repo: 'invalid' }, (err) => {
  assert.strictEqual(err.name, 'InvalidSlugError');
  gitCheckerMock.verify();
  travisCheckerMock.verify();
  done();
});
assert.strictEqual(retVal, undefined);