From a159165fe5f9fe53c3593af707888a7efc859d14 Mon Sep 17 00:00:00 2001 From: Chris Knight Date: Sun, 15 Mar 2020 03:01:34 +0000 Subject: [PATCH] Node polyfill for fs.chown and fs.close (#4377) --- std/node/_fs/_fs_chown.ts | 37 +++++++++++++++++++++++++ std/node/_fs/_fs_chown_test.ts | 50 ++++++++++++++++++++++++++++++++++ std/node/_fs/_fs_close.ts | 24 ++++++++++++++++ std/node/_fs/_fs_close_test.ts | 42 ++++++++++++++++++++++++++++ std/node/querystring.ts | 2 ++ 5 files changed, 155 insertions(+) create mode 100644 std/node/_fs/_fs_chown.ts create mode 100644 std/node/_fs/_fs_chown_test.ts create mode 100644 std/node/_fs/_fs_close.ts create mode 100644 std/node/_fs/_fs_close_test.ts diff --git a/std/node/_fs/_fs_chown.ts b/std/node/_fs/_fs_chown.ts new file mode 100644 index 0000000000..008b30c47e --- /dev/null +++ b/std/node/_fs/_fs_chown.ts @@ -0,0 +1,37 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +import { CallbackWithError } from "./_fs_common.ts"; + +/** + * TODO: Also accept 'path' parameter as a Node polyfill Buffer or URL type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 + */ +export function chown( + path: string, + uid: number, + gid: number, + callback: CallbackWithError +): void { + new Promise(async (resolve, reject) => { + try { + await Deno.chown(path, uid, gid); + resolve(); + } catch (err) { + reject(err); + } + }) + .then(() => { + callback(); + }) + .catch(err => { + callback(err); + }); +} + +/** + * TODO: Also accept 'path' parameter as a Node polyfill Buffer or URL type once these + * are implemented. See https://github.com/denoland/deno/issues/3403 + */ +export function chownSync(path: string, uid: number, gid: number): void { + Deno.chownSync(path, uid, gid); +} diff --git a/std/node/_fs/_fs_chown_test.ts b/std/node/_fs/_fs_chown_test.ts new file mode 100644 index 0000000000..51a463d887 --- /dev/null +++ b/std/node/_fs/_fs_chown_test.ts @@ -0,0 +1,50 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +const { test } = Deno; +import { fail, assertEquals } from "../../testing/asserts.ts"; +import { chown, chownSync } from "./_fs_chown.ts"; + +if (Deno.build.os !== "win") { + //chown is difficult to test. Best we can do is set the existing user id/group id again + test({ + name: "ASYNC: setting existing uid/gid works as expected (non-Windows)", + async fn() { + const tempFile: string = await Deno.makeTempFile(); + const originalUserId: number | null = (await Deno.lstat(tempFile)).uid; + const originalGroupId: number | null = (await Deno.lstat(tempFile)).gid; + await new Promise((resolve, reject) => { + chown(tempFile, originalUserId!, originalGroupId!, err => { + if (err) reject(err); + else resolve(); + }); + }) + .then(() => { + const newUserId: number | null = Deno.lstatSync(tempFile).uid; + const newGroupId: number | null = Deno.lstatSync(tempFile).gid; + assertEquals(newUserId, originalUserId); + assertEquals(newGroupId, originalGroupId); + }) + .catch(() => { + fail(); + }) + .finally(() => { + Deno.removeSync(tempFile); + }); + } + }); + + test({ + name: "SYNC: setting existing uid/gid works as expected (non-Windows)", + fn() { + const tempFile: string = Deno.makeTempFileSync(); + const originalUserId: number | null = Deno.lstatSync(tempFile).uid; + const originalGroupId: number | null = Deno.lstatSync(tempFile).gid; + chownSync(tempFile, originalUserId!, originalGroupId!); + + const newUserId: number | null = Deno.lstatSync(tempFile).uid; + const newGroupId: number | null = Deno.lstatSync(tempFile).gid; + assertEquals(newUserId, originalUserId); + assertEquals(newGroupId, originalGroupId); + Deno.removeSync(tempFile); + } + }); +} diff --git a/std/node/_fs/_fs_close.ts b/std/node/_fs/_fs_close.ts new file mode 100644 index 0000000000..e19eb932ec --- /dev/null +++ b/std/node/_fs/_fs_close.ts @@ -0,0 +1,24 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + +import { CallbackWithError } from "./_fs_common.ts"; + +export function close(fd: number, callback: CallbackWithError): void { + new Promise(async (resolve, reject) => { + try { + Deno.close(fd); + resolve(); + } catch (err) { + reject(err); + } + }) + .then(() => { + callback(); + }) + .catch(err => { + callback(err); + }); +} + +export function closeSync(fd: number): void { + Deno.close(fd); +} diff --git a/std/node/_fs/_fs_close_test.ts b/std/node/_fs/_fs_close_test.ts new file mode 100644 index 0000000000..2c71172488 --- /dev/null +++ b/std/node/_fs/_fs_close_test.ts @@ -0,0 +1,42 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. +const { test } = Deno; +import { fail, assert } from "../../testing/asserts.ts"; +import { close, closeSync } from "./_fs_close.ts"; + +test({ + name: "ASYNC: File is closed", + async fn() { + const tempFile: string = await Deno.makeTempFile(); + const file: Deno.File = await Deno.open(tempFile); + + assert(Deno.resources()[file.rid]); + await new Promise((resolve, reject) => { + close(file.rid, err => { + if (err) reject(); + else resolve(); + }); + }) + .then(async () => { + assert(!Deno.resources()[file.rid]); + }) + .catch(() => { + fail("No error expected"); + }) + .finally(async () => { + await Deno.remove(tempFile); + }); + } +}); + +test({ + name: "SYNC: File is closed", + fn() { + const tempFile: string = Deno.makeTempFileSync(); + const file: Deno.File = Deno.openSync(tempFile); + + assert(Deno.resources()[file.rid]); + closeSync(file.rid); + assert(!Deno.resources()[file.rid]); + Deno.removeSync(tempFile); + } +}); diff --git a/std/node/querystring.ts b/std/node/querystring.ts index a5b2e7f056..fc5a00a13e 100644 --- a/std/node/querystring.ts +++ b/std/node/querystring.ts @@ -1,3 +1,5 @@ +// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license. + interface ParseOptions { decodeURIComponent?: (string: string) => string; maxKeys?: number;