-
Notifications
You must be signed in to change notification settings - Fork 60
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
test: use native fetch with mock server (#702)
- Loading branch information
Showing
10 changed files
with
3,294 additions
and
1,884 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { getUserAgent } from "universal-user-agent"; | ||
import { VERSION } from "./version.js"; | ||
|
||
export default { | ||
headers: { | ||
"user-agent": `octokit-request.js/${VERSION} ${getUserAgent()}`, | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,5 @@ | ||
import { endpoint } from "@octokit/endpoint"; | ||
import { getUserAgent } from "universal-user-agent"; | ||
|
||
import { VERSION } from "./version.js"; | ||
import defaults from "./defaults.js"; | ||
import withDefaults from "./with-defaults.js"; | ||
|
||
export const request = withDefaults(endpoint, { | ||
headers: { | ||
"user-agent": `octokit-request.js/${VERSION} ${getUserAgent()}`, | ||
}, | ||
}); | ||
export const request = withDefaults(endpoint, defaults); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
import type { IncomingMessage } from "http"; | ||
|
||
export default function bodyParser(request: IncomingMessage) { | ||
return new Promise((resolve, reject) => { | ||
let body = ""; | ||
request.on("error", reject); | ||
request.on("data", (chunk: string) => (body += chunk)); | ||
request.on("end", () => resolve(body)); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,37 @@ | ||
import { createServer, RequestListener } from "node:http"; | ||
import { type AddressInfo } from "node:net"; | ||
import { once } from "node:stream"; | ||
|
||
import { endpoint } from "@octokit/endpoint"; | ||
import type { RequestInterface } from "@octokit/types"; | ||
|
||
import withDefaults from "../src/with-defaults.ts"; | ||
import defaults from "../src/defaults.ts"; | ||
|
||
export default async function mockRequestHttpServer( | ||
requestListener: RequestListener, | ||
): Promise< | ||
RequestInterface<object> & { | ||
closeMockServer: () => void; | ||
baseUrlMockServer: string; | ||
} | ||
> { | ||
const server = createServer(requestListener); | ||
server.listen(0); | ||
await once(server, "listening"); | ||
|
||
const baseUrl = `http://localhost:${(server.address() as AddressInfo).port}`; | ||
|
||
const request = withDefaults(endpoint, { | ||
...defaults, | ||
baseUrl, | ||
}) as RequestInterface<object> & { | ||
closeMockServer: () => void; | ||
baseUrlMockServer: string; | ||
}; | ||
|
||
request.baseUrlMockServer = baseUrl; | ||
request.closeMockServer = server.close.bind(server); | ||
|
||
return request; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
import { describe, it, expect } from "vitest"; | ||
import fetchMock from "fetch-mock"; | ||
|
||
import { request } from "../src/index.ts"; | ||
|
||
describe("request()", () => { | ||
it("is a function", () => { | ||
expect(request).toBeInstanceOf(Function); | ||
}); | ||
|
||
it("Request error", async () => { | ||
expect.assertions(1); | ||
|
||
// port: 8 // officially unassigned port. See https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers | ||
await expect(request("GET https://127.0.0.1:8/")).rejects.toHaveProperty( | ||
"status", | ||
500, | ||
); | ||
}); | ||
|
||
it("Resolves with url", async () => { | ||
expect.assertions(1); | ||
|
||
// this test cannot be mocked with `fetch-mock`. I don’t like to rely on | ||
// external websites to run tests, but in this case I’ll make an exception. | ||
// The alternative would be to start a local server we then send a request to, | ||
// this would only work in Node, so we would need to adapt the test setup, too. | ||
// We also can’t test the GitHub API, because on Travis unauthenticated | ||
// GitHub API requests are usually blocked due to IP rate limiting | ||
const response = await request( | ||
"https://www.githubstatus.com/api/v2/status.json", | ||
); | ||
expect(response.url).toEqual( | ||
"https://www.githubstatus.com/api/v2/status.json", | ||
); | ||
}); | ||
|
||
it("request should pass the `redirect` option to fetch", () => { | ||
expect.assertions(1); | ||
|
||
const customFetch = async (url: string, options: RequestInit) => { | ||
expect(options.redirect).toEqual("manual"); | ||
return await fetch(url, options); | ||
}; | ||
|
||
return request("/", { | ||
request: { | ||
redirect: "manual", | ||
fetch: customFetch, | ||
}, | ||
}); | ||
}); | ||
|
||
it("options.request.fetch", async () => { | ||
expect.assertions(1); | ||
|
||
const response = await request("/", { | ||
request: { | ||
fetch: () => | ||
Promise.resolve({ | ||
status: 200, | ||
headers: new Headers({ | ||
"Content-Type": "application/json; charset=utf-8", | ||
}), | ||
url: "http://api.github.com/", | ||
json() { | ||
return Promise.resolve("funk"); | ||
}, | ||
}), | ||
}, | ||
}); | ||
expect(response.data).toEqual("funk"); | ||
}); | ||
|
||
it("Request TypeError error with an Error cause", async () => { | ||
expect.assertions(2); | ||
|
||
try { | ||
// port: 8 // officially unassigned port. See https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers | ||
await request("GET https://127.0.0.1:8/", { | ||
request: { | ||
fetch: () => | ||
Promise.reject( | ||
Object.assign(new TypeError("fetch failed"), { | ||
cause: new Error("bad"), | ||
}), | ||
), | ||
}, | ||
}); | ||
throw new Error("should not resolve"); | ||
} catch (error) { | ||
expect(error.status).toEqual(500); | ||
expect(error.message).toEqual("bad"); | ||
} | ||
}); | ||
|
||
it("Request TypeError error with a string cause", async () => { | ||
expect.assertions(2); | ||
|
||
const mock = fetchMock.sandbox().get("https://127.0.0.1:8/", { | ||
throws: Object.assign(new TypeError("fetch failed"), { cause: "bad" }), | ||
}); | ||
|
||
try { | ||
// port: 8 // officially unassigned port. See https://en.wikipedia.org/wiki/List_of_TCP_and_UDP_port_numbers | ||
await request("GET https://127.0.0.1:8/", { | ||
request: { | ||
fetch: () => | ||
Promise.reject( | ||
Object.assign(new TypeError("fetch failed"), { | ||
cause: "bad", | ||
}), | ||
), | ||
}, | ||
}); | ||
throw new Error("should not resolve"); | ||
} catch (error) { | ||
expect(error.status).toEqual(500); | ||
expect(error.message).toEqual("bad"); | ||
} | ||
}); | ||
}); |
Oops, something went wrong.