Skip to content

Commit 4f320ad

Browse files
authored
Merge pull request #3297 from code-asher/test-static
2 parents 2f2fd06 + e8443e2 commit 4f320ad

10 files changed

+212
-76
lines changed

test/e2e/terminal.test.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { test, expect } from "@playwright/test"
1+
import { expect, test } from "@playwright/test"
22
import * as cp from "child_process"
33
import * as fs from "fs"
4-
// import { tmpdir } from "os"
54
import * as path from "path"
65
import util from "util"
7-
import { STORAGE, tmpdir } from "../utils/constants"
6+
import { STORAGE } from "../utils/constants"
7+
import { tmpdir } from "../utils/helpers"
88
import { CodeServer } from "./models/CodeServer"
99

1010
test.describe("Integrated Terminal", () => {

test/unit/constants.test.ts

+19-47
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,22 @@
1-
import * as fs from "fs"
2-
import { tmpdir } from "../../test/utils/constants"
3-
import { loggerModule } from "../utils/helpers"
4-
5-
// jest.mock is hoisted above the imports so we must use `require` here.
6-
jest.mock("@coder/logger", () => require("../utils/helpers").loggerModule)
1+
import { createLoggerMock } from "../utils/helpers"
72

83
describe("constants", () => {
9-
beforeAll(() => {
10-
jest.clearAllMocks()
11-
jest.resetModules()
12-
})
4+
let constants: typeof import("../../src/node/constants")
5+
136
describe("with package.json defined", () => {
14-
const { getPackageJson } = require("../../src/node/constants")
15-
let mockPackageJson = {
7+
const loggerModule = createLoggerMock()
8+
const mockPackageJson = {
169
name: "mock-code-server",
1710
description: "Run VS Code on a remote server.",
1811
repository: "https://github.com/cdr/code-server",
1912
version: "1.0.0",
2013
commit: "f6b2be2838f4afb217c2fd8f03eafedd8d55ef9b",
2114
}
22-
let version = ""
23-
let commit = ""
2415

25-
beforeEach(() => {
16+
beforeAll(() => {
17+
jest.mock("@coder/logger", () => loggerModule)
2618
jest.mock("../../package.json", () => mockPackageJson, { virtual: true })
27-
commit = require("../../src/node/constants").commit
28-
version = require("../../src/node/constants").version
19+
constants = require("../../src/node/constants")
2920
})
3021

3122
afterAll(() => {
@@ -34,18 +25,18 @@ describe("constants", () => {
3425
})
3526

3627
it("should provide the commit", () => {
37-
expect(commit).toBe("f6b2be2838f4afb217c2fd8f03eafedd8d55ef9b")
28+
expect(constants.commit).toBe(mockPackageJson.commit)
3829
})
3930

4031
it("should return the package.json version", () => {
41-
expect(version).toBe(mockPackageJson.version)
32+
expect(constants.version).toBe(mockPackageJson.version)
4233
})
4334

4435
describe("getPackageJson", () => {
4536
it("should log a warning if package.json not found", () => {
4637
const expectedErrorMessage = "Cannot find module './package.json' from 'src/node/constants.ts'"
4738

48-
getPackageJson("./package.json")
39+
constants.getPackageJson("./package.json")
4940

5041
expect(loggerModule.logger.warn).toHaveBeenCalled()
5142
expect(loggerModule.logger.warn).toHaveBeenCalledWith(expectedErrorMessage)
@@ -54,51 +45,32 @@ describe("constants", () => {
5445
it("should find the package.json", () => {
5546
// the function calls require from src/node/constants
5647
// so to get the root package.json we need to use ../../
57-
const packageJson = getPackageJson("../../package.json")
58-
expect(Object.keys(packageJson).length).toBeGreaterThan(0)
59-
expect(packageJson.name).toBe("mock-code-server")
60-
expect(packageJson.description).toBe("Run VS Code on a remote server.")
61-
expect(packageJson.repository).toBe("https://github.com/cdr/code-server")
48+
const packageJson = constants.getPackageJson("../../package.json")
49+
expect(packageJson).toStrictEqual(mockPackageJson)
6250
})
6351
})
6452
})
6553

6654
describe("with incomplete package.json", () => {
67-
let mockPackageJson = {
55+
const mockPackageJson = {
6856
name: "mock-code-server",
6957
}
70-
let version = ""
71-
let commit = ""
7258

73-
beforeEach(() => {
59+
beforeAll(() => {
7460
jest.mock("../../package.json", () => mockPackageJson, { virtual: true })
75-
version = require("../../src/node/constants").version
76-
commit = require("../../src/node/constants").commit
61+
constants = require("../../src/node/constants")
7762
})
7863

79-
afterEach(() => {
64+
afterAll(() => {
8065
jest.clearAllMocks()
8166
jest.resetModules()
8267
})
8368

8469
it("version should return 'development'", () => {
85-
expect(version).toBe("development")
70+
expect(constants.version).toBe("development")
8671
})
8772
it("commit should return 'development'", () => {
88-
expect(commit).toBe("development")
89-
})
90-
})
91-
})
92-
93-
describe("test constants", () => {
94-
describe("tmpdir", () => {
95-
it("should return a temp directory", async () => {
96-
const testName = "temp-dir"
97-
const pathToTempDir = await tmpdir(testName)
98-
99-
expect(pathToTempDir).toContain(testName)
100-
101-
await fs.promises.rmdir(pathToTempDir)
73+
expect(constants.commit).toBe("development")
10274
})
10375
})
10476
})

test/unit/helpers.test.ts

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { promises as fs } from "fs"
2+
import { tmpdir } from "../../test/utils/helpers"
3+
4+
/**
5+
* This file is for testing test helpers (not core code).
6+
*/
7+
describe("test helpers", () => {
8+
it("should return a temp directory", async () => {
9+
const testName = "temp-dir"
10+
const pathToTempDir = await tmpdir(testName)
11+
expect(pathToTempDir).toContain(testName)
12+
expect(fs.access(pathToTempDir)).resolves.toStrictEqual(undefined)
13+
})
14+
})

test/unit/register.test.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { JSDOM } from "jsdom"
22
import { registerServiceWorker } from "../../src/browser/register"
3-
import { loggerModule } from "../utils/helpers"
3+
import { createLoggerMock } from "../utils/helpers"
44
import { LocationLike } from "./util.test"
55

66
describe("register", () => {
@@ -21,6 +21,7 @@ describe("register", () => {
2121
})
2222
})
2323

24+
const loggerModule = createLoggerMock()
2425
beforeEach(() => {
2526
jest.clearAllMocks()
2627
jest.mock("@coder/logger", () => loggerModule)
@@ -75,6 +76,7 @@ describe("register", () => {
7576
})
7677

7778
describe("when navigator and serviceWorker are NOT defined", () => {
79+
const loggerModule = createLoggerMock()
7880
beforeEach(() => {
7981
jest.clearAllMocks()
8082
jest.mock("@coder/logger", () => loggerModule)

test/unit/health.test.ts renamed to test/unit/routes/health.test.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import * as httpserver from "../utils/httpserver"
2-
import * as integration from "../utils/integration"
1+
import * as httpserver from "../../utils/httpserver"
2+
import * as integration from "../../utils/integration"
33

44
describe("health", () => {
55
let codeServer: httpserver.HttpServer | undefined

test/unit/routes/static.test.ts

+136
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { promises as fs } from "fs"
2+
import * as path from "path"
3+
import { tmpdir } from "../../utils/helpers"
4+
import * as httpserver from "../../utils/httpserver"
5+
import * as integration from "../../utils/integration"
6+
7+
describe("/static", () => {
8+
let _codeServer: httpserver.HttpServer | undefined
9+
function codeServer(): httpserver.HttpServer {
10+
if (!_codeServer) {
11+
throw new Error("tried to use code-server before setting it up")
12+
}
13+
return _codeServer
14+
}
15+
16+
let testFile: string | undefined
17+
let testFileContent: string | undefined
18+
let nonExistentTestFile: string | undefined
19+
20+
// The static endpoint expects a commit and then the full path of the file.
21+
// The commit is just for cache busting so we can use anything we want. `-`
22+
// and `development` are specially recognized in that they will cause the
23+
// static endpoint to avoid sending cache headers.
24+
const commit = "-"
25+
26+
beforeAll(async () => {
27+
const testDir = await tmpdir("static")
28+
testFile = path.join(testDir, "test")
29+
testFileContent = "static file contents"
30+
nonExistentTestFile = path.join(testDir, "i-am-not-here")
31+
await fs.writeFile(testFile, testFileContent)
32+
})
33+
34+
afterEach(async () => {
35+
if (_codeServer) {
36+
await _codeServer.close()
37+
_codeServer = undefined
38+
}
39+
})
40+
41+
function commonTests() {
42+
it("should return a 404 when a commit and file are not provided", async () => {
43+
const resp = await codeServer().fetch("/static")
44+
expect(resp.status).toBe(404)
45+
46+
const content = await resp.json()
47+
expect(content).toStrictEqual({ error: "Not Found" })
48+
})
49+
50+
it("should return a 404 when a file is not provided", async () => {
51+
const resp = await codeServer().fetch(`/static/${commit}`)
52+
expect(resp.status).toBe(404)
53+
54+
const content = await resp.json()
55+
expect(content).toStrictEqual({ error: "Not Found" })
56+
})
57+
}
58+
59+
describe("disabled authentication", () => {
60+
beforeEach(async () => {
61+
_codeServer = await integration.setup(["--auth=none"], "")
62+
})
63+
64+
commonTests()
65+
66+
it("should return a 404 for a nonexistent file", async () => {
67+
const resp = await codeServer().fetch(`/static/${commit}/${nonExistentTestFile}`)
68+
expect(resp.status).toBe(404)
69+
70+
const content = await resp.json()
71+
expect(content.error).toMatch("ENOENT")
72+
})
73+
74+
it("should return a 200 and file contents for an existent file", async () => {
75+
const resp = await codeServer().fetch(`/static/${commit}${testFile}`)
76+
expect(resp.status).toBe(200)
77+
78+
const content = await resp.text()
79+
expect(content).toStrictEqual(testFileContent)
80+
})
81+
})
82+
83+
describe("enabled authentication", () => {
84+
// Store whatever might be in here so we can restore it afterward.
85+
// TODO: We should probably pass this as an argument somehow instead of
86+
// manipulating the environment.
87+
const previousEnvPassword = process.env.PASSWORD
88+
89+
beforeEach(async () => {
90+
process.env.PASSWORD = "test"
91+
_codeServer = await integration.setup(["--auth=password"], "")
92+
})
93+
94+
afterEach(() => {
95+
process.env.PASSWORD = previousEnvPassword
96+
})
97+
98+
commonTests()
99+
100+
describe("inside code-server root", () => {
101+
it("should return a 404 for a nonexistent file", async () => {
102+
const resp = await codeServer().fetch(`/static/${commit}/${__filename}-does-not-exist`)
103+
expect(resp.status).toBe(404)
104+
105+
const content = await resp.json()
106+
expect(content.error).toMatch("ENOENT")
107+
})
108+
109+
it("should return a 200 and file contents for an existent file", async () => {
110+
const resp = await codeServer().fetch(`/static/${commit}${__filename}`)
111+
expect(resp.status).toBe(200)
112+
113+
const content = await resp.text()
114+
expect(content).toStrictEqual(await fs.readFile(__filename, "utf8"))
115+
})
116+
})
117+
118+
describe("outside code-server root", () => {
119+
it("should return a 401 for a nonexistent file", async () => {
120+
const resp = await codeServer().fetch(`/static/${commit}/${nonExistentTestFile}`)
121+
expect(resp.status).toBe(401)
122+
123+
const content = await resp.json()
124+
expect(content).toStrictEqual({ error: "Unauthorized" })
125+
})
126+
127+
it("should return a 401 for an existent file", async () => {
128+
const resp = await codeServer().fetch(`/static/${commit}${testFile}`)
129+
expect(resp.status).toBe(401)
130+
131+
const content = await resp.json()
132+
expect(content).toStrictEqual({ error: "Unauthorized" })
133+
})
134+
})
135+
})
136+
})

test/unit/util.test.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import {
1111
trimSlashes,
1212
normalize,
1313
} from "../../src/common/util"
14-
import { loggerModule } from "../utils/helpers"
14+
import { createLoggerMock } from "../utils/helpers"
1515

1616
const dom = new JSDOM()
1717
global.document = dom.window.document
@@ -229,6 +229,8 @@ describe("util", () => {
229229
jest.restoreAllMocks()
230230
})
231231

232+
const loggerModule = createLoggerMock()
233+
232234
it("should log an error with the message and stack trace", () => {
233235
const message = "You don't have access to that folder."
234236
const error = new Error(message)

test/utils/constants.ts

-11
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,3 @@
1-
import * as fs from "fs"
2-
import * as os from "os"
3-
import * as path from "path"
4-
51
export const CODE_SERVER_ADDRESS = process.env.CODE_SERVER_ADDRESS || "http://localhost:8080"
62
export const PASSWORD = process.env.PASSWORD || "e45432jklfdsab"
73
export const STORAGE = process.env.STORAGE || ""
8-
9-
export async function tmpdir(testName: string): Promise<string> {
10-
const dir = path.join(os.tmpdir(), "code-server")
11-
await fs.promises.mkdir(dir, { recursive: true })
12-
13-
return await fs.promises.mkdtemp(path.join(dir, `test-${testName}-`), { encoding: "utf8" })
14-
}

test/utils/helpers.ts

+31-10
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,32 @@
1-
export const loggerModule = {
2-
field: jest.fn(),
3-
level: 2,
4-
logger: {
5-
debug: jest.fn(),
6-
error: jest.fn(),
7-
info: jest.fn(),
8-
trace: jest.fn(),
9-
warn: jest.fn(),
10-
},
1+
import * as fs from "fs"
2+
import * as os from "os"
3+
import * as path from "path"
4+
5+
/**
6+
* Return a mock of @coder/logger.
7+
*/
8+
export function createLoggerMock() {
9+
return {
10+
field: jest.fn(),
11+
level: 2,
12+
logger: {
13+
debug: jest.fn(),
14+
error: jest.fn(),
15+
info: jest.fn(),
16+
trace: jest.fn(),
17+
warn: jest.fn(),
18+
},
19+
}
20+
}
21+
22+
/**
23+
* Create a uniquely named temporary directory.
24+
*
25+
* These directories are placed under a single temporary code-server directory.
26+
*/
27+
export async function tmpdir(testName: string): Promise<string> {
28+
const dir = path.join(os.tmpdir(), "code-server/tests")
29+
await fs.promises.mkdir(dir, { recursive: true })
30+
31+
return await fs.promises.mkdtemp(path.join(dir, `${testName}-`), { encoding: "utf8" })
1132
}

0 commit comments

Comments
 (0)