Skip to content

feat: add tests for update.ts #4835

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 10 commits into from
Feb 14, 2022
109 changes: 105 additions & 4 deletions test/unit/node/update.test.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import * as http from "http"
import { logger } from "@coder/logger"
import { AddressInfo } from "net"
import * as path from "path"
import { SettingsProvider, UpdateSettings } from "../../../src/node/settings"
import { LatestResponse, UpdateProvider } from "../../../src/node/update"
import { clean, mockLogger, tmpdir } from "../../utils/helpers"
import { clean, isAddressInfo, mockLogger, tmpdir } from "../../utils/helpers"

describe("update", () => {
let version = "1.0.0"
Expand All @@ -23,6 +25,46 @@ describe("update", () => {
return response.end(JSON.stringify(latest))
}

if (request.url === "/reject-status-code") {
response.writeHead(500)
return response.end("rejected status code test")
}

if (request.url === "/no-location-header") {
response.writeHead(301, "testing", {
location: "",
})
return response.end("rejected status code test")
}

if (request.url === "/with-location-header") {
response.writeHead(301, "testing", {
location: "/latest",
})

return response.end()
}

// Checks if url matches /redirect/${number}
// with optional trailing slash
const match = request.url.match(/\/redirect\/([0-9]+)\/?$/)
if (match) {
if (request.url === "/redirect/0") {
response.writeHead(200)
return response.end("done")
}

// Subtract 1 from the current redirect number
// i.e. /redirect/10 -> /redirect/9 -> /redirect/8
const currentRedirectNumber = parseInt(match[1])
const newRedirectNumber = currentRedirectNumber - 1

response.writeHead(302, "testing", {
location: `/redirect/${String(newRedirectNumber)}`,
})
return response.end("")
}

// Anything else is a 404.
response.writeHead(404)
response.end("not found")
Expand All @@ -37,6 +79,7 @@ describe("update", () => {
}

let _provider: UpdateProvider | undefined
let _address: string | AddressInfo | null
const provider = (): UpdateProvider => {
if (!_provider) {
throw new Error("Update provider has not been created")
Expand All @@ -62,19 +105,20 @@ describe("update", () => {
})
})

const address = server.address()
if (!address || typeof address === "string" || !address.port) {
_address = server.address()
if (!isAddressInfo(_address)) {
throw new Error("unexpected address")
}

_provider = new UpdateProvider(`http://${address.address}:${address.port}/latest`, _settings)
_provider = new UpdateProvider(`http://${_address?.address}:${_address?.port}/latest`, _settings)
})

afterAll(() => {
server.close()
})

beforeEach(() => {
jest.clearAllMocks()
spy = []
})

Expand Down Expand Up @@ -170,4 +214,61 @@ describe("update", () => {
expect(update.checked < Date.now() && update.checked >= now).toEqual(true)
expect(update.version).toStrictEqual("unknown")
})

it("should reject if response has status code 500", async () => {
if (isAddressInfo(_address)) {
const mockURL = `http://${_address.address}:${_address.port}/reject-status-code`
let provider = new UpdateProvider(mockURL, settings())
let update = await provider.getUpdate(true)

expect(update.version).toBe("unknown")
expect(logger.error).toHaveBeenCalled()
expect(logger.error).toHaveBeenCalledWith("Failed to get latest version", {
identifier: "error",
value: `${mockURL}: 500`,
})
}
})

it("should reject if no location header provided", async () => {
if (isAddressInfo(_address)) {
const mockURL = `http://${_address.address}:${_address.port}/no-location-header`
let provider = new UpdateProvider(mockURL, settings())
let update = await provider.getUpdate(true)

expect(update.version).toBe("unknown")
expect(logger.error).toHaveBeenCalled()
expect(logger.error).toHaveBeenCalledWith("Failed to get latest version", {
identifier: "error",
value: `received redirect with no location header`,
})
}
})

it("should resolve the request with response.headers.location", async () => {
version = "4.1.1"
if (isAddressInfo(_address)) {
const mockURL = `http://${_address.address}:${_address.port}/with-location-header`
let provider = new UpdateProvider(mockURL, settings())
let update = await provider.getUpdate(true)

expect(logger.error).not.toHaveBeenCalled()
expect(update.version).toBe("4.1.1")
}
})

it("should reject if more than 10 redirects", async () => {
if (isAddressInfo(_address)) {
const mockURL = `http://${_address.address}:${_address.port}/redirect/11`
let provider = new UpdateProvider(mockURL, settings())
let update = await provider.getUpdate(true)

expect(update.version).toBe("unknown")
expect(logger.error).toHaveBeenCalled()
expect(logger.error).toHaveBeenCalledWith("Failed to get latest version", {
identifier: "error",
value: `reached max redirects`,
})
}
})
})
14 changes: 14 additions & 0 deletions test/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,3 +105,17 @@ export function idleTimer(message: string, reject: (error: Error) => void, delay
},
}
}

/**
* A helper function which returns a boolean indicating whether
* the given address is AddressInfo and has .address
* and a .port property.
*/
export function isAddressInfo(address: unknown): address is net.AddressInfo {
return (
address !== null &&
typeof address !== "string" &&
(address as net.AddressInfo).port !== undefined &&
(address as net.AddressInfo).address !== undefined
)
}