Skip to content

Commit

Permalink
refactor: rename option "tmpDir" to "cacheDir"
Browse files Browse the repository at this point in the history
- the new naming is more concise than "tmpDir"
- increase test-coverage to 100% (seemingly, test for the "prepare" scripts are still missing
- chore: make "yarn test" test the coverage as well

BREAKING CHANGE: rename option "tmpDir" to "cacheDir"
  • Loading branch information
nknapp committed Jul 5, 2020
1 parent 36bc1df commit 3956654
Show file tree
Hide file tree
Showing 11 changed files with 272 additions and 25 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,5 @@ coverage/
.eslintcache
/node_modules
/tmp
/testtmp
/test/fixtures/chisel/1.5.2/
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@
"test:jest": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage",
"pretest": "npm run lint",
"test": "npm run test:jest --",
"pretest": "npm run lint && node ./test/prepare-fixtures.js",
"test": "npm run test:coverage --",
"posttest": "npm run format",
"format": "prettier --loglevel warn --write \"**/*.{js,ts,d.ts,css,md}\"",
"lint": "eslint . --cache --fix",
Expand Down
10 changes: 1 addition & 9 deletions prepare/lib/collection-utils.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
module.exports = { uniqueIndexBy, mapValues, arrayToObject };
module.exports = { uniqueIndexBy, arrayToObject };

function uniqueIndexBy(array, keyFunction) {
const result = {};
Expand All @@ -12,14 +12,6 @@ function uniqueIndexBy(array, keyFunction) {
return result;
}

function mapValues(object, mapValueFunction) {
const result = {};
Object.keys(object).forEach((key) => {
result[key] = mapValueFunction(object[key]);
});
return result;
}

function arrayToObject(array, mapToEntry) {
const result = {};
array.forEach((item) => {
Expand Down
20 changes: 10 additions & 10 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const fs = require("fs-extra");
const path = require("path");
const defaultTmpDir = path.resolve(__dirname, "..", "tmp");
const defaultCacheDir = path.resolve(__dirname, "..", "chisel-cache");
const { sha256, download, gunzip } = require("./lib/file-operations");

const { lookupAssets } = require("./lib/lookup-assets");
Expand All @@ -10,22 +10,22 @@ module.exports = { downloadChisel };
/**
* Download chisel for a given range of versions
* @param {string} semverRange
* @param {object=} userOptions
* @param {string=} userOptions.tmpDir
* @param {object=} options
* @param {string=} options.cacheDir
* @return {Promise<string>} the downloaded chisel executable
*/
async function downloadChisel(semverRange, userOptions) {
const options = {
tmpDir: defaultTmpDir,
...userOptions,
async function downloadChisel(semverRange, options) {
const optionsWithDefaults = {
cacheDir: defaultCacheDir,
...options,
};
const asset = lookupAssets(semverRange);
const zippedFileName = await downloadAndVerify(asset, options.tmpDir);
const zippedFileName = await downloadToCacheAndVerify(asset, optionsWithDefaults.cacheDir);
return await extractFile(zippedFileName);
}

async function downloadAndVerify(asset, tmpDir) {
const zippedFileName = path.join(tmpDir, asset.name);
async function downloadToCacheAndVerify(asset, cacheDir) {
const zippedFileName = path.join(cacheDir, asset.name);
if (await needToDownload(zippedFileName, asset.checksum)) {
await download(asset.url, zippedFileName);
}
Expand Down
99 changes: 95 additions & 4 deletions src/index.test.js
Original file line number Diff line number Diff line change
@@ -1,16 +1,107 @@
const chiselTunnel = require("./index");
const fs = require("fs-extra");
const cp = require("child_process");
const path = require("path");
const nock = require("nock");
const { getChisel_1_5_2_executable } = require("../test/utils/chisel-fixtures");

const defaultCachedir = "chisel-cache";
const {
urlOrigin,
urlPath,
fixturePath,
executableName,
gzFilename,
} = getChisel_1_5_2_executable();

describe("the chisel-tunnel", () => {
let nockMock;

beforeEach(async () => {
await fs.remove("tmp");
nockMock = nock(urlOrigin)
.get(urlPath)
.reply(200, () => fs.createReadStream(fixturePath));

await fs.remove(defaultCachedir);
await fs.remove("testtmp");
});

afterEach(() => {
nock.cleanAll();
});

describe("downloads a chisel binary by version range", () => {
it("to the default cache-directory", async () => {
let filename = await chiselTunnel.downloadChisel("~1.5.0");
expectVersion1_5_2(filename);
expect(nockMock.isDone()).toBe(true);
expectToBeChisel_1_5_2_inDirectory(filename, defaultCachedir);
});

it("to the specified custom temp-directory", async () => {
let filename = await chiselTunnel.downloadChisel("~1.5.0", {
cacheDir: "./testtmp",
});

expectVersion1_5_2(filename);
expect(nockMock.isDone()).toBe(true);
expectToBeChisel_1_5_2_inDirectory(filename, "testtmp");
});
});

it("should download a chisel binary by version range", async () => {
it("if a valid file exists in the cache, it should not be downloaded again", async () => {
const existingChisel = path.join(defaultCachedir, gzFilename);
await fs.copy(fixturePath, existingChisel);

let filename = await chiselTunnel.downloadChisel("~1.5.0");

const { stdout } = cp.spawnSync(filename, ["--version"], { encoding: "utf-8" });
expect(stdout.trim()).toEqual("1.5.2");
expectVersion1_5_2(filename);
expect(nockMock.isDone()).toBe(false);
expectToBeChisel_1_5_2_inDirectory(filename, defaultCachedir);
});

it("if an invalid file exists in the cache, it should be downloaded again ", async () => {
const existingChisel = path.join(defaultCachedir, gzFilename);
await fs.mkdirp(defaultCachedir);
await fs.writeFile(existingChisel, "invalid file contents");

let filename = await chiselTunnel.downloadChisel("~1.5.0");

expectVersion1_5_2(filename);
expect(nockMock.isDone()).toBe(true);
expectToBeChisel_1_5_2_inDirectory(filename, defaultCachedir);
});

it("if the downloaded file is invalid, an error is throw ", async () => {
nock.cleanAll();
nock(urlOrigin).get(urlPath).reply(200, "invalid file contents");

await expect(() => chiselTunnel.downloadChisel("~1.5.0")).rejects.toThrow(/Checksum mismatch/);

expect(nockMock.isDone()).toBe(true);
});

it("if a directory exists in the cache instead of a file, an error is thrown", async () => {
const existingChisel = path.join(defaultCachedir, gzFilename);
await fs.mkdirp(existingChisel);

await expect(() => chiselTunnel.downloadChisel("~1.5.0")).rejects.toThrow(/EISDIR/);

expect(nockMock.isDone()).toBe(false);
});

it("throws an error if the requested version-range is empty", async () => {
await expect(() => chiselTunnel.downloadChisel("0.1.1")).rejects.toThrow(
"No version found for range 0.1.1"
);
});
});

function expectVersion1_5_2(filename) {
const { stdout } = cp.spawnSync(filename, ["--version"], { encoding: "utf-8" });
expect(stdout.trim()).toEqual("1.5.2");
}

function expectToBeChisel_1_5_2_inDirectory(filename, directory) {
expect(path.relative(".", filename)).toEqual(path.join(directory, executableName));
}
27 changes: 27 additions & 0 deletions src/lib/lookup-assets.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const { lookupAssets } = require("./lookup-assets");

describe("the lookup-assets function", () => {
it("resolves the semver range across the existing versions", () => {
expect(lookupAssets("^1.4.0").name).toMatch(/1\.6\.0/);
expect(lookupAssets("~1.4.0").name).toMatch(/1\.4\.0/);
expect(lookupAssets("~1.5.0").name).toMatch(/1\.5\.2/);
});

it("throws if no version matches the semver-range", () => {
expect(() => lookupAssets("^10000.1.1")).toThrow("No version found for range ^10000.1.1");
});

it("throws if no the platform is not known", () => {
const originalPlatform = process.platform;
try {
Object.defineProperty(process, "platform", {
value: "unkown-os",
});
expect(() => lookupAssets("1.5.0")).toThrow('No chisel binary found for "unkown-os--x64"');
} finally {
Object.defineProperty(process, "platform", {
value: originalPlatform,
});
}
});
});
3 changes: 3 additions & 0 deletions test/prepare-fixtures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
const { downloadFixtures } = require("./utils/chisel-fixtures");

downloadFixtures().catch((error) => console.error(error.message + "\n" + error.stack));
97 changes: 97 additions & 0 deletions test/utils/chisel-fixtures.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/* istanbul ignore file */

const fs = require("fs-extra");
const got = require("got");
const path = require("path");
const { URL } = require("url");

const { promisify } = require("util");
const stream = require("stream");
const pipeline = promisify(stream.pipeline);

const fixtureDirectory = path.relative(
".",
path.resolve(__dirname, "..", "fixtures", "chisel", "1.5.2")
);

module.exports = {
downloadFixtures,
getChisel_1_5_2_checksums,
getChisel_1_5_2_executable,
};

async function downloadFixtures() {
await fs.mkdirp(fixtureDirectory);

await download(getChisel_1_5_2_checksums({ ignoreMissing: true }));
await download(getChisel_1_5_2_executable({ ignoreMissing: true }));
}

async function download({ url, uncheckedFixturePath }) {
let downloadStream = got.stream(url);
let writeStream = fs.createWriteStream(uncheckedFixturePath);
try {
console.log("starting download of " + url + " to " + uncheckedFixturePath);
await pipeline(downloadStream, writeStream);
console.log("done downloading " + url);
} catch (error) {
console.log(error.message + "\n" + error.stack);
}
}

function getChisel_1_5_2_executable() {
const chisel_1_5_2_urls = {
"linux--ia32":
"https://github.com/jpillora/chisel/releases/download/v1.5.2/chisel_1.5.2_linux_386.gz",
"linux--x64":
"https://github.com/jpillora/chisel/releases/download/v1.5.2/chisel_1.5.2_linux_amd64.gz",
"darwin--ia32":
"https://github.com/jpillora/chisel/releases/download/v1.5.2/chisel_1.5.2_darwin_386.gz",
"darwin--x64":
"https://github.com/jpillora/chisel/releases/download/v1.5.2/chisel_1.5.2_darwin_amd64.gz",
"windows-ia32":
"https://github.com/jpillora/chisel/releases/download/v1.5.2/chisel_1.5.2_windows_386.gz",
"windows-x64":
"https://github.com/jpillora/chisel/releases/download/v1.5.2/chisel_1.5.2_windows_amd64.gz",
};

const key = process.platform + "--" + process.arch;
let url = chisel_1_5_2_urls[key];
if (url == null) {
throw new Error(
`Could not find value for key "${key}" in ${JSON.stringify(chisel_1_5_2_urls, null, 2)}`
);
}
return asChiselTestDetails(url);
}

function getChisel_1_5_2_checksums() {
return asChiselTestDetails(
"https://github.com/jpillora/chisel/releases/download/v1.5.2/chisel_1.5.2_checksums.txt"
);
}

function asChiselTestDetails(url) {
const gzFilename = url.match(/[^/]+$/)[0];
const fixturePath = path.join(fixtureDirectory, gzFilename);
const executableName = gzFilename.replace(/\.gz$/, "");
const { origin, pathname } = new URL(url);
return {
url,
urlOrigin: origin,
urlPath: pathname,
gzFilename,
uncheckedFixturePath: fixturePath,
get fixturePath() {
if (fs.existsSync(fixturePath)) {
return fixturePath;
}
throw new Error(
"Fixture " +
fixturePath +
" does not exist. Please call `yarn pretest` before running the tests"
);
},
executableName,
};
}
10 changes: 10 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
declare module "chisel-tunnel" {
export function downloadChisel(
semverRange: string,
options?: DownloadChiselOptions
): Promise<string>;

export interface DownloadChiselOptions {
cacheDir?: string;
}
}
11 changes: 11 additions & 0 deletions types/test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { downloadChisel } from "chisel-tunnel";

async function simpleTest() {
const path: string = await downloadChisel("1.6.0");
}

async function withCustomTmpDir() {
const path: string = await downloadChisel("1.6.0", {
cacheDir: "./tmp",
});
}
14 changes: 14 additions & 0 deletions types/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
{
"compilerOptions": {
"module": "commonjs",
"lib": ["es6"],
"noImplicitAny": true,
"noImplicitThis": true,
"strictNullChecks": true,
"strictFunctionTypes": true,
"noEmit": true,

"baseUrl": ".",
"paths": { "child-service": ["."] }
}
}

0 comments on commit 3956654

Please sign in to comment.