Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: track IaC local execution tests [CC-972]
Call new endpoint introduced in https://github.com/snyk/registry/pull/22111 to track billing usage for IaC tests, which by default run in "local exec" mode.
- Loading branch information
Craig Furman
committed
Jul 22, 2021
1 parent
9e7f79a
commit f7f2961
Showing
6 changed files
with
178 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
src/cli/commands/test/iac-local-execution/usage-tracking.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
import request = require('../../../../lib/request'); | ||
import config = require('../../../../lib/config'); | ||
import { api as getApiToken } from '../../../../lib/api-token'; | ||
import { CustomError } from '../../../../lib/errors'; | ||
|
||
export async function trackUsage( | ||
formattedResults: TrackableResult[], | ||
): Promise<void> { | ||
const trackingData = formattedResults.map((res) => { | ||
return { | ||
isPrivate: res.meta.isPrivate, | ||
issuesPrevented: res.result.cloudConfigResults.length, | ||
}; | ||
}); | ||
const trackingResponse = await request({ | ||
method: 'POST', | ||
headers: { | ||
Authorization: `token ${getApiToken()}`, | ||
}, | ||
url: `${config.API}/track-iac-usage/cli`, | ||
body: { results: trackingData }, | ||
gzip: true, | ||
json: true, | ||
}); | ||
switch (trackingResponse.res.statusCode) { | ||
case 200: | ||
break; | ||
case 429: | ||
throw new TestLimitReachedError(); | ||
default: | ||
throw new CustomError( | ||
'An error occurred while attempting to track test usage: ' + | ||
JSON.stringify(trackingResponse.res.body), | ||
); | ||
} | ||
} | ||
|
||
export class TestLimitReachedError extends CustomError { | ||
constructor() { | ||
super( | ||
'Test limit reached! You have exceeded your infrastructure as code test allocation for this billing period.', | ||
); | ||
} | ||
} | ||
|
||
// Sub-interface of FormattedResult that we really only use to make test | ||
// fixtures easier to create. | ||
export interface TrackableResult { | ||
meta: { | ||
isPrivate: boolean; | ||
}; | ||
result: { | ||
cloudConfigResults: any[]; | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,95 @@ | ||
import { | ||
trackUsage, | ||
TestLimitReachedError, | ||
} from '../../../../src/cli/commands/test/iac-local-execution/usage-tracking'; | ||
import { mocked } from 'ts-jest/utils'; | ||
import { NeedleResponse } from 'needle'; | ||
import makeRequest = require('../../../../src/lib/request/request'); | ||
import { CustomError } from '../../../../src/lib/errors'; | ||
|
||
jest.mock('../../../../src/lib/request/request'); | ||
const mockedMakeRequest = mocked(makeRequest); | ||
|
||
const results = [ | ||
{ | ||
meta: { | ||
isPrivate: true, | ||
}, | ||
result: { | ||
cloudConfigResults: ['an issue'], | ||
}, | ||
}, | ||
{ | ||
meta: { | ||
isPrivate: false, | ||
}, | ||
result: { | ||
cloudConfigResults: [], | ||
}, | ||
}, | ||
]; | ||
|
||
describe('tracking IaC test usage', () => { | ||
afterEach(() => { | ||
jest.clearAllMocks(); | ||
}); | ||
|
||
it('does not throw an error when backend returns HTTP 200', async () => { | ||
mockedMakeRequest.mockImplementationOnce(() => { | ||
return Promise.resolve({ | ||
res: { statusCode: 200 } as NeedleResponse, | ||
body: { | ||
foo: 'bar', | ||
}, | ||
}); | ||
}); | ||
|
||
await trackUsage(results); | ||
|
||
expect(mockedMakeRequest.mock.calls.length).toEqual(1); | ||
expect(mockedMakeRequest.mock.calls[0][0].body).toEqual({ | ||
results: [ | ||
{ | ||
isPrivate: true, | ||
issuesPrevented: 1, | ||
}, | ||
{ | ||
isPrivate: false, | ||
issuesPrevented: 0, | ||
}, | ||
], | ||
}); | ||
}); | ||
|
||
it('throws TestLimitReachedError when backend returns HTTP 429', async () => { | ||
mockedMakeRequest.mockImplementationOnce(() => { | ||
return Promise.resolve({ | ||
res: { statusCode: 429 } as NeedleResponse, | ||
body: { | ||
foo: 'bar', | ||
}, | ||
}); | ||
}); | ||
|
||
await expect(trackUsage(results)).rejects.toThrow( | ||
new TestLimitReachedError(), | ||
); | ||
}); | ||
|
||
it('throws CustomError when backend returns HTTP 500', async () => { | ||
mockedMakeRequest.mockImplementationOnce(() => { | ||
return Promise.resolve({ | ||
res: { statusCode: 500, body: { foo: 'bar' } } as NeedleResponse, | ||
body: { | ||
foo: 'bar', | ||
}, | ||
}); | ||
}); | ||
|
||
await expect(trackUsage(results)).rejects.toThrow( | ||
new CustomError( | ||
'An error occurred while attempting to track test usage: {"foo":"bar"}', | ||
), | ||
); | ||
}); | ||
}); |