Files
shields/services/github/github-api-provider.spec.js
chris48s c586960ce8 allow calling [github] without auth (#9427)
* allow calling [github] without auth

* set a default for authType, fix broken tests
2023-08-07 14:51:18 +00:00

176 lines
6.3 KiB
JavaScript

import { expect } from 'chai'
import sinon from 'sinon'
import GithubApiProvider from './github-api-provider.js'
describe('Github API provider', function () {
const baseUrl = 'https://github-api.example.com'
const reserveFraction = 0.333
let mockStandardToken, mockSearchToken, mockGraphqlToken, provider
beforeEach(function () {
provider = new GithubApiProvider({
baseUrl,
authType: GithubApiProvider.AUTH_TYPES.TOKEN_POOL,
reserveFraction,
})
mockStandardToken = { update: sinon.spy(), invalidate: sinon.spy() }
sinon.stub(provider.standardTokens, 'next').returns(mockStandardToken)
mockSearchToken = { update: sinon.spy(), invalidate: sinon.spy() }
sinon.stub(provider.searchTokens, 'next').returns(mockSearchToken)
mockGraphqlToken = { update: sinon.spy(), invalidate: sinon.spy() }
sinon.stub(provider.graphqlTokens, 'next').returns(mockGraphqlToken)
})
context('a search API request', function () {
it('should obtain an appropriate token', async function () {
const mockResponse = { res: { headers: {} } }
const mockRequest = sinon.stub().resolves(mockResponse)
await provider.fetch(mockRequest, '/search', {})
expect(provider.searchTokens.next).to.have.been.calledOnce
expect(provider.standardTokens.next).not.to.have.been.called
expect(provider.graphqlTokens.next).not.to.have.been.called
})
})
context('a graphql API request', function () {
it('should obtain an appropriate token', async function () {
const mockResponse = { res: { headers: {} } }
const mockRequest = sinon.stub().resolves(mockResponse)
await provider.fetch(mockRequest, '/graphql', {})
expect(provider.searchTokens.next).not.to.have.been.called
expect(provider.standardTokens.next).not.to.have.been.called
expect(provider.graphqlTokens.next).to.have.been.calledOnce
})
})
context('a core API request', function () {
it('should obtain an appropriate token', async function () {
const mockResponse = { res: { headers: {} } }
const mockRequest = sinon.stub().resolves(mockResponse)
await provider.fetch(mockRequest, '/repo', {})
expect(provider.searchTokens.next).not.to.have.been.called
expect(provider.standardTokens.next).to.have.been.calledOnce
expect(provider.graphqlTokens.next).not.to.have.been.called
})
})
context('a valid V3 API response', function () {
const rateLimit = 12500
const remaining = 7955
const nextReset = 123456789
const mockResponse = {
res: {
statusCode: 200,
headers: {
'x-ratelimit-limit': rateLimit,
'x-ratelimit-remaining': remaining,
'x-ratelimit-reset': nextReset,
},
buffer: Buffer.alloc(0),
},
}
const mockRequest = sinon.stub().resolves(mockResponse)
it('should return the response', async function () {
const res = await provider.fetch(mockRequest, '/repo', {})
expect(Object.is(res, mockResponse)).to.be.true
})
it('should update the token with the expected values', async function () {
await provider.fetch(mockRequest, '/foo', {})
const expectedUsesRemaining =
remaining - Math.ceil(reserveFraction * rateLimit)
expect(mockStandardToken.update).to.have.been.calledWith(
expectedUsesRemaining,
nextReset,
)
expect(mockStandardToken.invalidate).not.to.have.been.called
})
})
context('a valid V4 API response', function () {
const rateLimit = 12500
const remaining = 7955
const nextReset = 123456789
const mockResponse = {
res: {
statusCode: 200,
headers: {},
body: `{
"data": {
"rateLimit": {
"limit": 12500,
"cost": 1,
"remaining": 7955,
"resetAt": "1973-11-29T21:33:09Z"
}
}
}`,
},
}
const mockRequest = sinon.stub().resolves(mockResponse)
it('should return the response', async function () {
const res = await provider.fetch(mockRequest, '/graphql', {})
expect(Object.is(res, mockResponse)).to.be.true
})
it('should update the token with the expected values', async function () {
await provider.fetch(mockRequest, '/graphql', {})
const expectedUsesRemaining =
remaining - Math.ceil(reserveFraction * rateLimit)
expect(mockGraphqlToken.update).to.have.been.calledWith(
expectedUsesRemaining,
nextReset,
)
expect(mockGraphqlToken.invalidate).not.to.have.been.called
})
})
context('unauthorized API responses', function () {
it('should invoke the callback and update the token with the expected values (unauthorized, v3)', async function () {
const mockResponse = { res: { statusCode: 401, headers: {} } }
const mockRequest = sinon.stub().resolves(mockResponse)
await provider.fetch(mockRequest, '/foo', {})
expect(mockStandardToken.invalidate).to.have.been.calledOnce
expect(mockStandardToken.update).not.to.have.been.called
})
it('should invoke the callback and update the token with the expected values (unauthorized, v4)', async function () {
const mockResponse = { res: { statusCode: 401, body: {} } }
const mockRequest = sinon.stub().resolves(mockResponse)
await provider.fetch(mockRequest, '/graphql', {})
expect(mockGraphqlToken.invalidate).to.have.been.calledOnce
expect(mockGraphqlToken.update).not.to.have.been.called
})
it('should invoke the callback and update the token with the expected values (suspended, v4)', async function () {
const mockResponse = {
res: {
statusCode: 200,
body: '{ "message": "Sorry. Your account was suspended." }',
},
}
const mockRequest = sinon.stub().resolves(mockResponse)
await provider.fetch(mockRequest, '/graphql', {})
expect(mockGraphqlToken.invalidate).to.have.been.calledOnce
expect(mockGraphqlToken.update).not.to.have.been.called
})
})
context('a connection error', function () {
it('should throw an exception', function () {
const msg = 'connection timeout'
const requestError = new Error(msg)
const mockRequest = sinon.stub().rejects(requestError)
return expect(provider.fetch(mockRequest, '/foo', {})).to.be.rejectedWith(
Error,
'connection timeout',
)
})
})
})