Skip to content

Commit

Permalink
Refactor utils.fetch to a module and mock it with nock
Browse files Browse the repository at this point in the history
Add redirect support in utils.fetch
  • Loading branch information
shivammathur committed Feb 6, 2022
1 parent 7655557 commit 4dc94c2
Show file tree
Hide file tree
Showing 10 changed files with 292 additions and 129 deletions.
39 changes: 39 additions & 0 deletions __tests__/fetch.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import * as fetch from '../src/fetch';
import nock = require('nock');

it('checking fetch', async () => {
const host_url = 'https://example.com';
const manifest_url = host_url + '/manifest';
const ping_url = host_url + '/ping';

nock(host_url)
.get('/manifest')
.reply(200, {latest: 'latest'})
.get('/manifest', '', {
reqheaders: {authorization: 'Bearer invalid_token'}
})
.reply(401, {error: '401: Unauthorized'})
.get('/ping')
.twice()
.reply(301, undefined, {
Location: host_url + '/pong'
})
.get('/pong')
.reply(200, 'pong');

let response: Record<string, string> = await fetch.fetch(manifest_url);
expect(response.error).toBe(undefined);
expect(response.data).toContain('latest');

response = await fetch.fetch(ping_url, '', 1);
expect(response.error).toBe(undefined);
expect(response.data).toContain('pong');

response = await fetch.fetch(ping_url, '', 0);
expect(response.error).toBe('301: Redirect error');
expect(response.data).toBe(undefined);

response = await fetch.fetch(manifest_url, 'invalid_token');
expect(response.error).not.toBe(undefined);
expect(response.data).toBe(undefined);
});
9 changes: 9 additions & 0 deletions __tests__/install.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,15 @@ jest.mock('../src/install', () => ({
})
}));

/**
* Mock fetch.ts
*/
jest.mock('../src/fetch', () => ({
fetch: jest.fn().mockImplementation(() => {
return {data: '{ "latest": "8.1", "5.x": "5.6" }'};
})
}));

describe('Install', () => {
it.each`
version | os | extension_csv | ini_file | ini_values_csv | coverage_driver | tools | output
Expand Down
50 changes: 27 additions & 23 deletions __tests__/tools.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import * as tools from '../src/tools';
import * as utils from '../src/utils';

interface IData {
tool: string;
Expand Down Expand Up @@ -38,29 +37,34 @@ function getData(data: IData): Record<string, string> {
};
}

jest
.spyOn(utils, 'fetch')
.mockImplementation(
async (url: string, token?: string): Promise<Record<string, string>> => {
if (url.includes('atom') && !url.includes('no-')) {
return {
data: '"releases/tag/1.2.3", "releases/tag/3.2.1", "releases/tag/2.3.1"'
};
} else if (url.includes('no-data')) {
return {};
} else if (url.includes('no-release')) {
return {data: 'no-release'};
} else if (!token || token === 'valid_token') {
return {data: `[{"ref": "refs/tags/1.2.3", "url": "${url}"}]`};
} else if (token === 'beta_token') {
return {data: `[{"ref": "refs/tags/1.2.3-beta1", "url": "${url}"}]`};
} else if (token === 'no_data') {
return {data: '[]'};
} else {
return {error: 'Invalid token'};
/**
* Mock fetch.ts
*/
jest.mock('../src/fetch', () => ({
fetch: jest
.fn()
.mockImplementation(
async (url: string, token?: string): Promise<Record<string, string>> => {
if (url.includes('atom') && !url.includes('no-')) {
return {
data: '"releases/tag/1.2.3", "releases/tag/3.2.1", "releases/tag/2.3.1"'
};
} else if (url.includes('no-data')) {
return {};
} else if (url.includes('no-release')) {
return {data: 'no-release'};
} else if (!token || token === 'valid_token') {
return {data: `[{"ref": "refs/tags/1.2.3", "url": "${url}"}]`};
} else if (token === 'beta_token') {
return {data: `[{"ref": "refs/tags/1.2.3-beta1", "url": "${url}"}]`};
} else if (token === 'no_data') {
return {data: '[]'};
} else {
return {error: 'Invalid token'};
}
}
}
);
)
}));

describe('Tools tests', () => {
it.each`
Expand Down
34 changes: 12 additions & 22 deletions __tests__/utils.test.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,24 @@
import * as path from 'path';
import * as utils from '../src/utils';

/**
* Mock @actions/core
*/
jest.mock('@actions/core', () => ({
getInput: jest.fn().mockImplementation(key => {
return ['setup-php'].indexOf(key) !== -1 ? key : '';
})
}));

/**
* Mock fetch.ts
*/
jest.mock('../src/fetch', () => ({
fetch: jest.fn().mockImplementation(() => {
return {data: '{ "latest": "8.1", "5.x": "5.6" }'};
})
}));

describe('Utils tests', () => {
it('checking readEnv', async () => {
process.env['test'] = 'setup-php';
Expand All @@ -28,33 +40,11 @@ describe('Utils tests', () => {
}).rejects.toThrow('Input required and not supplied: DoesNotExist');
});

it('checking fetch', async () => {
const manifest = await utils.getManifestURL();
let response: Record<string, string> = await utils.fetch(manifest);
expect(response.error).toBe(undefined);
expect(response.data).toContain('latest');

response = await utils.fetch(manifest, 'invalid_token');
expect(response.error).not.toBe(undefined);
expect(response.data).toBe(undefined);
});

it('checking getManifestURL', async () => {
expect(await utils.getManifestURL()).toContain('php-versions.json');
});

it('checking parseVersion', async () => {
jest
.spyOn(utils, 'fetch')
.mockImplementation(
async (url, token?): Promise<Record<string, string>> => {
if (!token || token === 'valid_token') {
return {data: `{ "latest": "8.0", "5.x": "5.6", "url": "${url}" }`};
} else {
return {error: 'Invalid token'};
}
}
);
expect(await utils.parseVersion('latest')).toBe('8.1');
expect(await utils.parseVersion('7')).toBe('7.0');
expect(await utils.parseVersion('7.4')).toBe('7.4');
Expand Down
114 changes: 77 additions & 37 deletions dist/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,76 @@ exports.addExtension = addExtension;

/***/ }),

/***/ 387:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {

"use strict";

var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
__setModuleDefault(result, mod);
return result;
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.fetch = void 0;
const https = __importStar(__nccwpck_require__(687));
const url = __importStar(__nccwpck_require__(310));
async function fetch(input_url, auth_token, redirect_count = 5) {
const fetch_promise = new Promise(resolve => {
const url_object = new url.URL(input_url);
const headers = {
'User-Agent': `Mozilla/5.0 (${process.platform} ${process.arch}) setup-php`
};
if (auth_token) {
headers.authorization = 'Bearer ' + auth_token;
}
const options = {
hostname: url_object.hostname,
path: url_object.pathname,
headers: headers
};
const req = https.get(options, (res) => {
if (res.statusCode === 200) {
let body = '';
res.setEncoding('utf8');
res.on('data', chunk => (body += chunk));
res.on('end', () => resolve({ data: `${body}` }));
}
else if ([301, 302, 303, 307, 308].includes(res.statusCode)) {
if (redirect_count > 0 && res.headers.location) {
fetch(res.headers.location, auth_token, redirect_count--).then(resolve);
}
else {
resolve({ error: `${res.statusCode}: Redirect error` });
}
}
else {
resolve({ error: `${res.statusCode}: ${res.statusMessage}` });
}
});
req.end();
});
return await fetch_promise;
}
exports.fetch = fetch;
//# sourceMappingURL=fetch.js.map

/***/ }),

/***/ 39:
/***/ (function(__unused_webpack_module, exports, __nccwpck_require__) {

Expand Down Expand Up @@ -519,15 +589,16 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.addTools = exports.functionRecord = exports.getData = exports.addWPCLI = exports.addPHPUnitTools = exports.addPhive = exports.addPhing = exports.addPECL = exports.addDevTools = exports.addDeployer = exports.addComposer = exports.addBlackfirePlayer = exports.addPackage = exports.addArchive = exports.getPharUrl = exports.getUrl = exports.filterList = exports.getRelease = exports.getVersion = exports.getLatestVersion = exports.getSemverVersion = void 0;
const utils = __importStar(__nccwpck_require__(918));
const path_1 = __importDefault(__nccwpck_require__(17));
const fs_1 = __importDefault(__nccwpck_require__(147));
const fetch = __importStar(__nccwpck_require__(387));
const utils = __importStar(__nccwpck_require__(918));
async function getSemverVersion(data) {
var _a;
const search = data['version_prefix'] + data['version'];
const url = `https://api.github.com/repos/${data['repository']}/git/matching-refs/tags%2F${search}.`;
const token = await utils.readEnv('COMPOSER_TOKEN');
const response = await utils.fetch(url, token);
const response = await fetch.fetch(url, token);
if (response.error || response.data === '[]') {
data['error'] = (_a = response.error) !== null && _a !== void 0 ? _a : `No version found with prefix ${search}.`;
return data['version'];
Expand All @@ -544,7 +615,7 @@ async function getLatestVersion(data) {
if (!data['version'] && data['fetch_latest'] === 'false') {
return 'latest';
}
const resp = await utils.fetch(`${data['github']}/${data['repository']}/releases.atom`);
const resp = await fetch.fetch(`${data['github']}/${data['repository']}/releases.atom`);
if (resp['data']) {
const releases = [
...resp['data'].matchAll(/releases\/tag\/([a-zA-Z]*)?(\d+.\d+.\d+)"/g)
Expand Down Expand Up @@ -889,11 +960,10 @@ var __importStar = (this && this.__importStar) || function (mod) {
return result;
};
Object.defineProperty(exports, "__esModule", ({ value: true }));
exports.parseExtensionSource = exports.customPackage = exports.scriptTool = exports.scriptExtension = exports.joins = exports.getCommand = exports.getUnsupportedLog = exports.suppressOutput = exports.getExtensionPrefix = exports.CSVArray = exports.extensionArray = exports.addLog = exports.stepLog = exports.log = exports.color = exports.asyncForEach = exports.parseIniFile = exports.parseVersion = exports.getManifestURL = exports.fetch = exports.getInput = exports.readEnv = void 0;
const https = __importStar(__nccwpck_require__(687));
exports.parseExtensionSource = exports.customPackage = exports.scriptTool = exports.scriptExtension = exports.joins = exports.getCommand = exports.getUnsupportedLog = exports.suppressOutput = exports.getExtensionPrefix = exports.CSVArray = exports.extensionArray = exports.addLog = exports.stepLog = exports.log = exports.color = exports.asyncForEach = exports.parseIniFile = exports.parseVersion = exports.getManifestURL = exports.getInput = exports.readEnv = void 0;
const path = __importStar(__nccwpck_require__(17));
const url = __importStar(__nccwpck_require__(310));
const core = __importStar(__nccwpck_require__(186));
const fetch = __importStar(__nccwpck_require__(387));
async function readEnv(property) {
const property_lc = property.toLowerCase();
const property_uc = property.toUpperCase();
Expand All @@ -920,36 +990,6 @@ async function getInput(name, mandatory) {
}
}
exports.getInput = getInput;
async function fetch(input_url, auth_token) {
const fetch_promise = new Promise(resolve => {
const url_object = new url.URL(input_url);
const headers = {
'User-Agent': `Mozilla/5.0 (${process.platform} ${process.arch}) setup-php`
};
if (auth_token) {
headers.authorization = 'Bearer ' + auth_token;
}
const options = {
hostname: url_object.hostname,
path: url_object.pathname,
headers: headers
};
const req = https.get(options, (res) => {
if (res.statusCode != 200) {
resolve({ error: `${res.statusCode}: ${res.statusMessage}` });
}
else {
let body = '';
res.setEncoding('utf8');
res.on('data', chunk => (body += chunk));
res.on('end', () => resolve({ data: `${body}` }));
}
});
req.end();
});
return await fetch_promise;
}
exports.fetch = fetch;
async function getManifestURL() {
return 'https://raw.githubusercontent.com/shivammathur/setup-php/develop/src/configs/php-versions.json';
}
Expand All @@ -958,7 +998,7 @@ async function parseVersion(version) {
const manifest = await getManifestURL();
switch (true) {
case /^(latest|nightly|\d+\.x)$/.test(version):
return JSON.parse((await fetch(manifest))['data'])[version];
return JSON.parse((await fetch.fetch(manifest))['data'])[version];
default:
switch (true) {
case version.length > 1:
Expand Down
Loading

0 comments on commit 4dc94c2

Please sign in to comment.