diff --git a/__tests__/limitFetcher.test.ts b/__tests__/limitFetcher.test.ts new file mode 100644 index 0000000..bc8f706 --- /dev/null +++ b/__tests__/limitFetcher.test.ts @@ -0,0 +1,84 @@ +/** + * Unit tests for src/processThreshold.ts + */ + +import * as core from '@actions/core' +import nock from 'nock'; // Import the nock library for mocking HTTP requests +import { fetchRateLimit } from '../src/limitFetcher'; +import { expect } from '@jest/globals' + +// Mock the setFailed function from '@actions/core' +jest.mock('@actions/core', () => ({ + setFailed: jest.fn(), +})); + +describe('limitFetcher.ts', () => { + beforeAll(() => { + // Set up a mock for the GitHub API + nock('https://api.github.com') + .get('/rate_limit') + .reply(200, { + data: { + resources: { + core: { + limit: 5000, + remaining: 4300, + reset: 1696896000, + used: 700, + }, + search: { + limit: 30, + remaining: 18, + reset: 1696896400, + used: 12, + }, + }, + }, + }); + }); + + afterAll(() => { + // Clean up the nock mocks + nock.cleanAll(); + }); + + it('should fetch and return the core rate limit', async () => { + const token = 'some_fake_github_token'; + const resource = 'core'; + + const result = await fetchRateLimit(token, resource); + + expect(core.setFailed).not.toHaveBeenCalled(); + expect(result.limit).toBe(5000); + expect(result.remaining).toBe(4300); + expect(result.reset).toBe(1696896000); + }); + + it('should fetch and return the search rate limit', async () => { + const token = 'some_fake_github_token'; + const resource = 'search'; + + const result = await fetchRateLimit(token, resource); + + expect(core.setFailed).not.toHaveBeenCalled(); + expect(result.limit).toBe(30); + expect(result.remaining).toBe(18); + expect(result.reset).toBe(1696896400); + }); + + it('should set a failed message on API error', async () => { + // Mock the GitHub API to return an error (e.g., 404 Not Found) + nock('https://api.github.com') + .get('/rate_limit') + .reply(404); + + const token = 'some_fake_github_token'; + const resource = 'core'; + + await fetchRateLimit(token, resource); + + expect(core.setFailed).toHaveBeenCalledWith('Github API rateLimit could not be retrieved.'); + }); + +}); + diff --git a/__tests__/processThreshold.test.ts b/__tests__/processThreshold.test.ts new file mode 100644 index 0000000..b2357cd --- /dev/null +++ b/__tests__/processThreshold.test.ts @@ -0,0 +1,73 @@ +/** + * Unit tests for src/processThreshold.ts + */ + +import * as core from '@actions/core' +import { processThreshold } from '../src/processThreshold'; +import { expect } from '@jest/globals' + +// Mock the setFailed function from '@actions/core' +jest.mock('@actions/core', () => ({ + setFailed: jest.fn(), +})); + + +describe('processThreshold.ts', () => { + it('should process an absolute threshold correctly', () => { + const thresholdAsString = '26'; + const result = processThreshold(thresholdAsString); + + expect(core.setFailed).not.toHaveBeenCalled(); + expect(result.thresholdAsAbsolute).toBe(26); + expect(result.thresholdAsFraction).toBeUndefined(); + }); + + it('should process a fractional threshold correctly', () => { + const thresholdAsString = '0.3'; + const result = processThreshold(thresholdAsString); + + expect(core.setFailed).not.toHaveBeenCalled(); + expect(result.thresholdAsAbsolute).toBeUndefined(); + expect(result.thresholdAsFraction).toBe(0.3); + }); + + it('should process a percent threshold correctly', () => { + const thresholdAsString = '60%'; + const result = processThreshold(thresholdAsString); + + expect(core.setFailed).not.toHaveBeenCalled(); + expect(result.thresholdAsAbsolute).toBeUndefined(); + expect(result.thresholdAsFraction).toBe(0.6); + }); + + it('should set an error for an invalid threshold (negative number)', () => { + const thresholdAsString = '-0.1'; + const result = processThreshold(thresholdAsString); + + expect(core.setFailed).toHaveBeenCalledWith( + 'The threshold must be a positive number, but -0.1 was provided.' + ); + expect(result.thresholdAsAbsolute).toBeUndefined(); + expect(result.thresholdAsFraction).toBeUndefined(); + }); + + it('should handle invalid input (not a number)', () => { + const thresholdAsString = 'invalid'; + const result = processThreshold(thresholdAsString); + + expect(core.setFailed).toHaveBeenCalledWith( + 'The threshold must be a positive number, but invalid was provided.' + ); + expect(result.thresholdAsAbsolute).toBeUndefined(); + expect(result.thresholdAsFraction).toBeUndefined(); + }); + + it('should ignore a blank between number and percent sign', () => { + const thresholdAsString = '50 %'; + const result = processThreshold(thresholdAsString); + + expect(core.setFailed).not.toHaveBeenCalled(); + expect(result.thresholdAsAbsolute).toBeUndefined(); + expect(result.thresholdAsFraction).toBe(50); + }); +}) diff --git a/__tests__/validateResource.test.ts b/__tests__/validateResource.test.ts index 1d0ce88..40b048a 100644 --- a/__tests__/validateResource.test.ts +++ b/__tests__/validateResource.test.ts @@ -12,32 +12,32 @@ jest.mock('@actions/core', () => ({ })); describe('validateResource.ts', () => { - it('Accepts core as valid input', async () => { + it('accepts core as valid input', async () => { validateResource('core'); expect(core.setFailed).not.toHaveBeenCalled(); }) - it('Accepts graphql as valid input', async () => { + it('accepts graphql as valid input', async () => { validateResource('graphql'); expect(core.setFailed).not.toHaveBeenCalled(); }) - it('Accepts search as valid input', async () => { + it('accepts search as valid input', async () => { validateResource('search'); expect(core.setFailed).not.toHaveBeenCalled(); }) - it('Accepts integration_manifest as valid input', async () => { + it('accepts integration_manifest as valid input', async () => { validateResource('integration_manifest'); expect(core.setFailed).not.toHaveBeenCalled(); }) - it('Accepts code_scanning_upload as valid input', async () => { + it('accepts code_scanning_upload as valid input', async () => { validateResource('code_scanning_upload'); expect(core.setFailed).not.toHaveBeenCalled(); }) - it('Fails the step for an invalid resource', () => { + it('fails the step for an invalid resource', () => { validateResource('invalid_resource'); expect(core.setFailed).toHaveBeenCalledWith( 'The resource must be either core, graphql, search, integration_manifest, or code_scanning_upload.' diff --git a/package-lock.json b/package-lock.json index 3e2afc7..4522c17 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "typescript-action", + "name": "gh-api-confine", "version": "0.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "typescript-action", + "name": "gh-api-confine", "version": "0.0.0", "license": "MIT", "dependencies": { @@ -26,6 +26,7 @@ "jest": "^29.7.0", "js-yaml": "^4.1.0", "make-coverage-badge": "^1.2.0", + "nock": "^13.3.4", "prettier": "^3.0.3", "prettier-eslint": "^15.0.1", "ts-jest": "^29.1.1", @@ -5238,6 +5239,12 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==", + "dev": true + }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", @@ -5622,6 +5629,21 @@ "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, + "node_modules/nock": { + "version": "13.3.4", + "resolved": "https://registry.npmjs.org/nock/-/nock-13.3.4.tgz", + "integrity": "sha512-DDpmn5oLEdCTclEqweOT4U7bEpuoifBMFUXem9sA4turDAZ5tlbrEoWqCorwXey8CaAw44mst5JOQeVNiwtkhw==", + "dev": true, + "dependencies": { + "debug": "^4.1.0", + "json-stringify-safe": "^5.0.1", + "lodash": "^4.17.21", + "propagate": "^2.0.0" + }, + "engines": { + "node": ">= 10.13" + } + }, "node_modules/node-fetch": { "version": "2.7.0", "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", @@ -6305,6 +6327,15 @@ "node": ">= 6" } }, + "node_modules/propagate": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/propagate/-/propagate-2.0.1.tgz", + "integrity": "sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, "node_modules/punycode": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", diff --git a/package.json b/package.json index a26e820..26e2052 100644 --- a/package.json +++ b/package.json @@ -85,6 +85,7 @@ "jest": "^29.7.0", "js-yaml": "^4.1.0", "make-coverage-badge": "^1.2.0", + "nock": "^13.3.4", "prettier": "^3.0.3", "prettier-eslint": "^15.0.1", "ts-jest": "^29.1.1",