Skip to content

feat: add more tests for proxy.ts #3858

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jul 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion test/unit/helpers.test.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { promises as fs } from "fs"
import { tmpdir, useEnv } from "../../test/utils/helpers"
import { getAvailablePort, tmpdir, useEnv } from "../../test/utils/helpers"

/**
* This file is for testing test helpers (not core code).
Expand Down Expand Up @@ -39,3 +39,16 @@ describe("useEnv", () => {
expect(process.env[envKey]).toEqual("test environment variable")
})
})

describe("getAvailablePort", () => {
it("should return a valid port", async () => {
const port = await getAvailablePort()
expect(port).toBeGreaterThan(0)
expect(port).toBeLessThanOrEqual(65535)
})
it("should return different ports for different calls", async () => {
const portOne = await getAvailablePort()
const portTwo = await getAvailablePort()
expect(portOne).not.toEqual(portTwo)
})
})
110 changes: 110 additions & 0 deletions test/unit/node/proxy.test.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import bodyParser from "body-parser"
import * as express from "express"
import * as nodeFetch from "node-fetch"
import * as http from "http"
import { HttpCode } from "../../../src/common/http"
import { proxy } from "../../../src/node/proxy"
import * as httpserver from "../../utils/httpserver"
import * as integration from "../../utils/integration"
import { getAvailablePort } from "../../utils/helpers"

describe("proxy", () => {
const nhooyrDevServer = new httpserver.HttpServer()
Expand Down Expand Up @@ -102,4 +107,109 @@ describe("proxy", () => {
expect(resp.status).toBe(200)
expect(await resp.json()).toBe("coder is the best")
})

it("should handle bad requests", async () => {
e.use(bodyParser.json({ strict: false }))
e.post("/wsup", (req, res) => {
res.json(req.body)
})
codeServer = await integration.setup(["--auth=none"], "")
const resp = await codeServer.fetch(proxyPath, {
method: "post",
body: "coder is the best",
headers: {
"Content-Type": "application/json",
},
})
expect(resp.status).toBe(400)
expect(resp.statusText).toMatch("Bad Request")
})

it("should handle invalid routes", async () => {
e.post("/wsup", (req, res) => {
res.json(req.body)
})
codeServer = await integration.setup(["--auth=none"], "")
const resp = await codeServer.fetch(`${proxyPath}/hello`)
expect(resp.status).toBe(404)
expect(resp.statusText).toMatch("Not Found")
})

it("should handle errors", async () => {
e.use(bodyParser.json({ strict: false }))
e.post("/wsup", (req, res) => {
throw new Error("BROKEN")
})
codeServer = await integration.setup(["--auth=none"], "")
const resp = await codeServer.fetch(proxyPath, {
method: "post",
body: JSON.stringify("coder is the best"),
headers: {
"Content-Type": "application/json",
},
})
expect(resp.status).toBe(500)
expect(resp.statusText).toMatch("Internal Server Error")
})
})

// NOTE@jsjoeio
// Both this test suite and the one above it are very similar
// The main difference is this one uses http and node-fetch
// and specifically tests the proxy in isolation vs. using
// the httpserver abstraction we've built.
//
// Leaving this as a separate test suite for now because
// we may consider refactoring the httpserver abstraction
// in the future.
//
// If you're writing a test specifically for code in
// src/node/proxy.ts, you should probably add it to
// this test suite.
describe("proxy (standalone)", () => {
let URL = ""
let PROXY_URL = ""
let testServer: http.Server
let proxyTarget: http.Server

beforeEach(async () => {
const PORT = await getAvailablePort()
const PROXY_PORT = await getAvailablePort()
URL = `http://localhost:${PORT}`
PROXY_URL = `http://localhost:${PROXY_PORT}`
// Define server and a proxy server
testServer = http.createServer((req, res) => {
proxy.web(req, res, {
target: PROXY_URL,
})
})

proxyTarget = http.createServer((req, res) => {
res.writeHead(200, { "Content-Type": "text/plain" })
res.end()
})

// Start both servers
await proxyTarget.listen(PROXY_PORT)
await testServer.listen(PORT)
})

afterEach(async () => {
await testServer.close()
await proxyTarget.close()
})

it("should return a 500 when proxy target errors ", async () => {
// Close the proxy target so that proxy errors
await proxyTarget.close()
const errorResp = await nodeFetch.default(`${URL}/error`)
expect(errorResp.status).toBe(HttpCode.ServerError)
expect(errorResp.statusText).toBe("Internal Server Error")
})

it("should proxy correctly", async () => {
const resp = await nodeFetch.default(`${URL}/route`)
expect(resp.status).toBe(200)
expect(resp.statusText).toBe("OK")
})
})
21 changes: 21 additions & 0 deletions test/utils/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { promises as fs } from "fs"
import * as os from "os"
import * as path from "path"
import * as net from "net"

/**
* Return a mock of @coder/logger.
Expand Down Expand Up @@ -61,3 +62,23 @@ export function useEnv(key: string): [(nextValue: string | undefined) => string

return [setValue, resetValue]
}

/**
* Helper function to get a random port.
*
* Source: https://github.com/sindresorhus/get-port/blob/main/index.js#L23-L33
*/
export const getAvailablePort = (options?: net.ListenOptions): Promise<number> =>
new Promise((resolve, reject) => {
const server = net.createServer()
server.unref()
server.on("error", reject)
server.listen(options, () => {
// NOTE@jsjoeio: not a huge fan of the type assertion
// but it works for now.
const { port } = server.address() as net.AddressInfo
server.close(() => {
resolve(port)
})
})
})