From 8c0a0df93a62f57e1fa522dc349ecef721847396 Mon Sep 17 00:00:00 2001 From: Albert Cheng <38804567+ckairen@users.noreply.github.com> Date: Thu, 14 Dec 2023 13:40:44 -0800 Subject: [PATCH] [TypeSpecValidation] bug fix: drive letter inconsistency for windows (#27002) - Fixes #26557 --- eng/tools/typespec-validation/src/utils.ts | 10 ++- .../test/npm-prefix.test.ts | 68 +++++++++++++++++++ .../typespec-validation/test/tsv-test-host.ts | 11 ++- .../typespec-validation/test/util.test.ts | 27 +++++--- 4 files changed, 101 insertions(+), 15 deletions(-) create mode 100644 eng/tools/typespec-validation/test/npm-prefix.test.ts diff --git a/eng/tools/typespec-validation/src/utils.ts b/eng/tools/typespec-validation/src/utils.ts index 862bf7cf2d2a..9675a63882e3 100644 --- a/eng/tools/typespec-validation/src/utils.ts +++ b/eng/tools/typespec-validation/src/utils.ts @@ -1,6 +1,6 @@ import { access } from "fs/promises"; import { exec } from "child_process"; -import path from "path"; +import defaultPath, { PlatformPath } from "path"; export async function runCmd(cmd: string, cwd: string) { console.log(`run command:${cmd}`); @@ -22,6 +22,10 @@ export async function checkFileExists(file: string) { .catch(() => false); } -export function normalizePath(folder: string) { - return path.resolve(folder).split(path.sep).join("/"); +export function normalizePath(folder: string, path: PlatformPath = defaultPath) { + return path + .resolve(folder) + .split(path.sep) + .join("/") + .replace(/^([a-z]):/, (_match, driveLetter) => driveLetter.toUpperCase() + ":"); } diff --git a/eng/tools/typespec-validation/test/npm-prefix.test.ts b/eng/tools/typespec-validation/test/npm-prefix.test.ts new file mode 100644 index 000000000000..4808151d7236 --- /dev/null +++ b/eng/tools/typespec-validation/test/npm-prefix.test.ts @@ -0,0 +1,68 @@ +import { NpmPrefixRule } from "../src/rules/npm-prefix.js"; +import { IGitOperation, TsvTestHost } from "./tsv-test-host.js"; +import { strict as assert } from "node:assert"; +import path from "path"; + +describe("npm-prefix", function () { + it("should succeed if node returns inconsistent drive letter capitalization", async function () { + let host = new TsvTestHost(path.win32); + host.runCmd = async (cmd: string, _cwd: string): Promise<[Error | null, string, string]> => { + if (cmd.includes("npm prefix")) { + return [null, `C:${path.sep}Git${path.sep}azure-rest-api-specs`, ""]; + } else { + return [null, "", ""]; + } + }; + host.gitOperation = (_folder: string): IGitOperation => { + return { + status: () => { + return Promise.resolve({ + modified: [], + isClean: () => true, + }); + }, + diff: () => { + return Promise.resolve(""); + }, + revparse: () => { + return Promise.resolve("c:/Git/azure-rest-api-specs"); + }, + }; + }; + + const result = await new NpmPrefixRule().execute(host, TsvTestHost.folder); + + assert(result.success); + }); + + it("should fail if npm prefix mismatch", async function () { + let host = new TsvTestHost(); + host.runCmd = async (cmd: string, _cwd: string): Promise<[Error | null, string, string]> => { + if (cmd.includes("npm prefix")) { + return [null, "/Git/azure-rest-api-specs/specification/foo", ""]; + } else { + return [null, "", ""]; + } + }; + host.gitOperation = (_folder: string): IGitOperation => { + return { + status: () => { + return Promise.resolve({ + modified: [], + isClean: () => true, + }); + }, + diff: () => { + return Promise.resolve(""); + }, + revparse: () => { + return Promise.resolve("/Git/azure-rest-api-specs"); + }, + }; + }; + + const result = await new NpmPrefixRule().execute(host, TsvTestHost.folder); + + assert(!result.success); + }); +}); diff --git a/eng/tools/typespec-validation/test/tsv-test-host.ts b/eng/tools/typespec-validation/test/tsv-test-host.ts index 049c1d2c025f..3e846e5856b5 100644 --- a/eng/tools/typespec-validation/test/tsv-test-host.ts +++ b/eng/tools/typespec-validation/test/tsv-test-host.ts @@ -1,7 +1,16 @@ import { IGitOperation, TsvHost } from "../src/tsv-host.js"; import { normalizePath } from "../src/utils.js"; +import defaultPath, { PlatformPath } from "path"; + +export { IGitOperation } from "../src/tsv-host.js"; export class TsvTestHost implements TsvHost { + path: PlatformPath; + + constructor(path: PlatformPath = defaultPath) { + this.path = path; + } + static get folder() { return "specification/foo/Foo"; } @@ -36,7 +45,7 @@ export class TsvTestHost implements TsvHost { } normalizePath(folder: string): string { - return normalizePath(folder); + return normalizePath(folder, this.path); } async readTspConfig(_folder: string): Promise { diff --git a/eng/tools/typespec-validation/test/util.test.ts b/eng/tools/typespec-validation/test/util.test.ts index ff63c25cbac4..3afb4c7c8183 100644 --- a/eng/tools/typespec-validation/test/util.test.ts +++ b/eng/tools/typespec-validation/test/util.test.ts @@ -1,24 +1,29 @@ -import { TsvTestHost } from "./tsv-test-host.js"; +import { normalizePath } from "../src/utils.js"; import { strict as assert } from "node:assert"; import process from "process"; +import path from "path"; describe("normalize", function () { it("should succeed if normalized . and normalized cwd matches", async function () { - let host = new TsvTestHost(); - const dotResult = host.normalizePath("."); - const cwdResult = host.normalizePath(process.cwd()); + const dotResult = normalizePath("."); + const cwdResult = normalizePath(process.cwd()); assert(dotResult === cwdResult); }); it("should succeed if /foo/bar/ is normalized", async function () { - let host = new TsvTestHost(); - const result = host.normalizePath("/foo/bar/").replace(/^[a-zA-Z]:/g, ""); - assert(result === "/foo/bar"); + const result = normalizePath("/foo/bar/", path.posix); + assert.equal(result, "/foo/bar"); }); - it("should succeed if /foo/bar is normalized", async function () { - let host = new TsvTestHost(); - const result = host.normalizePath("/foo/bar").replace(/^[a-zA-Z]:/g, ""); - assert(result === "/foo/bar"); + it("should normalize windows drive letter", async function () { + const lowerResult = normalizePath("c:\\foo\\bar", path.win32); + const upperResult = normalizePath("C:\\foo\\bar", path.win32); + assert.equal(lowerResult, upperResult); + }); + + it("should distinguish different windows drive letters", async function () { + const lowerResult = normalizePath("c:\\foo\\bar", path.win32); + const upperResult = normalizePath("d:\\foo\\bar", path.win32); + assert.notEqual(lowerResult, upperResult); }); });