Skip to content

Commit 946e4e8

Browse files
authored
feat(cli): add test for readSocketPath (#4284)
* fix: update isNodeJSErrnoException * refactor(cli): export and purify readSocketPath * feat: add tests for readSocketPath * fix(ci): temporarily disable install deps from cache
1 parent 49c9c19 commit 946e4e8

File tree

4 files changed

+80
-26
lines changed

4 files changed

+80
-26
lines changed

.github/workflows/ci.yaml

+12-8
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,18 @@ jobs:
3131
- name: Install helm
3232
uses: azure/[email protected]
3333

34-
- name: Fetch dependencies from cache
35-
id: cache-yarn
36-
uses: actions/cache@v2
37-
with:
38-
path: "**/node_modules"
39-
key: yarn-build-${{ hashFiles('**/yarn.lock') }}
40-
restore-keys: |
41-
yarn-build-
34+
# NOTE@jsjoeio
35+
# disabling this until we can audit the build process
36+
# and the usefulness of this step
37+
# See: https://github.com/cdr/code-server/issues/4287
38+
# - name: Fetch dependencies from cache
39+
# id: cache-yarn
40+
# uses: actions/cache@v2
41+
# with:
42+
# path: "**/node_modules"
43+
# key: yarn-build-${{ hashFiles('**/yarn.lock') }}
44+
# restore-keys: |
45+
# yarn-build-
4246

4347
- name: Install dependencies
4448
# if: steps.cache-yarn.outputs.cache-hit != 'true'

src/node/cli.ts

+26-14
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,9 @@ import { promises as fs } from "fs"
33
import yaml from "js-yaml"
44
import * as os from "os"
55
import * as path from "path"
6-
import { canConnect, generateCertificate, generatePassword, humanPath, paths } from "./util"
6+
import { canConnect, generateCertificate, generatePassword, humanPath, paths, isNodeJSErrnoException } from "./util"
7+
8+
const DEFAULT_SOCKET_PATH = path.join(os.tmpdir(), "vscode-ipc")
79

810
export enum Feature {
911
// No current experimental features!
@@ -657,6 +659,27 @@ function bindAddrFromAllSources(...argsConfig: Args[]): Addr {
657659
return addr
658660
}
659661

662+
/**
663+
* Reads the socketPath based on path passed in.
664+
*
665+
* The one usually passed in is the DEFAULT_SOCKET_PATH.
666+
*
667+
* If it can't read the path, it throws an error and returns undefined.
668+
*/
669+
export async function readSocketPath(path: string): Promise<string | undefined> {
670+
try {
671+
return await fs.readFile(path, "utf8")
672+
} catch (error) {
673+
// If it doesn't exist, we don't care.
674+
// But if it fails for some reason, we should throw.
675+
// We want to surface that to the user.
676+
if (!isNodeJSErrnoException(error) || error.code !== "ENOENT") {
677+
throw error
678+
}
679+
}
680+
return undefined
681+
}
682+
660683
/**
661684
* Determine if it looks like the user is trying to open a file or folder in an
662685
* existing instance. The arguments here should be the arguments the user
@@ -668,32 +691,21 @@ export const shouldOpenInExistingInstance = async (args: Args): Promise<string |
668691
return process.env.VSCODE_IPC_HOOK_CLI
669692
}
670693

671-
const readSocketPath = async (): Promise<string | undefined> => {
672-
try {
673-
return await fs.readFile(path.join(os.tmpdir(), "vscode-ipc"), "utf8")
674-
} catch (error: any) {
675-
if (error.code !== "ENOENT") {
676-
throw error
677-
}
678-
}
679-
return undefined
680-
}
681-
682694
// If these flags are set then assume the user is trying to open in an
683695
// existing instance since these flags have no effect otherwise.
684696
const openInFlagCount = ["reuse-window", "new-window"].reduce((prev, cur) => {
685697
return args[cur as keyof Args] ? prev + 1 : prev
686698
}, 0)
687699
if (openInFlagCount > 0) {
688-
return readSocketPath()
700+
return readSocketPath(DEFAULT_SOCKET_PATH)
689701
}
690702

691703
// It's possible the user is trying to spawn another instance of code-server.
692704
// Check if any unrelated flags are set (check against one because `_` always
693705
// exists), that a file or directory was passed, and that the socket is
694706
// active.
695707
if (Object.keys(args).length === 1 && args._.length > 0) {
696-
const socketPath = await readSocketPath()
708+
const socketPath = await readSocketPath(DEFAULT_SOCKET_PATH)
697709
if (socketPath && (await canConnect(socketPath))) {
698710
return socketPath
699711
}

src/node/util.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -490,7 +490,7 @@ export function escapeHtml(unsafe: string): string {
490490
* it has a .code property.
491491
*/
492492
export function isNodeJSErrnoException(error: unknown): error is NodeJS.ErrnoException {
493-
return error instanceof Error && (error as NodeJS.ErrnoException).code !== undefined
493+
return error !== undefined && (error as NodeJS.ErrnoException).code !== undefined
494494
}
495495

496496
// TODO: Replace with proper templating system.

test/unit/node/cli.test.ts

+41-3
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,11 @@ import {
1111
setDefaults,
1212
shouldOpenInExistingInstance,
1313
splitOnFirstEquals,
14+
readSocketPath,
1415
} from "../../../src/node/cli"
15-
import { tmpdir } from "../../../src/node/constants"
1616
import { shouldSpawnCliProcess } from "../../../src/node/main"
1717
import { generatePassword, paths } from "../../../src/node/util"
18-
import { useEnv } from "../../utils/helpers"
18+
import { useEnv, tmpdir } from "../../utils/helpers"
1919

2020
type Mutable<T> = {
2121
-readonly [P in keyof T]: T[P]
@@ -390,10 +390,11 @@ describe("parser", () => {
390390

391391
describe("cli", () => {
392392
let args: Mutable<Args> = { _: [] }
393-
const testDir = path.join(tmpdir, "tests/cli")
393+
let testDir: string
394394
const vscodeIpcPath = path.join(os.tmpdir(), "vscode-ipc")
395395

396396
beforeAll(async () => {
397+
testDir = await tmpdir("cli")
397398
await fs.rmdir(testDir, { recursive: true })
398399
await fs.mkdir(testDir, { recursive: true })
399400
})
@@ -667,3 +668,40 @@ password: ${password}
667668
cert: false`)
668669
})
669670
})
671+
672+
describe("readSocketPath", () => {
673+
const fileContents = "readSocketPath file contents"
674+
let tmpDirPath: string
675+
let tmpFilePath: string
676+
677+
beforeEach(async () => {
678+
tmpDirPath = await tmpdir("readSocketPath")
679+
tmpFilePath = path.join(tmpDirPath, "readSocketPath.txt")
680+
await fs.writeFile(tmpFilePath, fileContents)
681+
})
682+
683+
afterEach(async () => {
684+
await fs.rmdir(tmpDirPath, { recursive: true })
685+
})
686+
687+
it("should throw an error if it can't read the file", async () => {
688+
// TODO@jsjoeio - implement
689+
// Test it on a directory.... ESDIR
690+
// TODO@jsjoeio - implement
691+
expect(() => readSocketPath(tmpDirPath)).rejects.toThrow("EISDIR")
692+
})
693+
it("should return undefined if it can't read the file", async () => {
694+
// TODO@jsjoeio - implement
695+
const socketPath = await readSocketPath(path.join(tmpDirPath, "not-a-file"))
696+
expect(socketPath).toBeUndefined()
697+
})
698+
it("should return the file contents", async () => {
699+
const contents = await readSocketPath(tmpFilePath)
700+
expect(contents).toBe(fileContents)
701+
})
702+
it("should return the same file contents for two different calls", async () => {
703+
const contents1 = await readSocketPath(tmpFilePath)
704+
const contents2 = await readSocketPath(tmpFilePath)
705+
expect(contents2).toBe(contents1)
706+
})
707+
})

0 commit comments

Comments
 (0)