Skip to content

test: use native fetch with mock server #702

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 14 commits into from
Jul 10, 2024
2,870 changes: 1,412 additions & 1,458 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
"lint": "prettier --check '{src,test}/**/*' README.md package.json",
"lint:fix": "prettier --write '{src,test}/**/*' README.md package.json vite.config.js",
"pretest": "npm run -s lint",
"test": "vitest run --coverage"
"test": "vitest run --coverage",
"test:watch": "vitest --coverage"
},
"repository": "github:octokit/request.js",
"keywords": [
Expand Down
8 changes: 8 additions & 0 deletions src/defaults.ts
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()}`,
},
};
10 changes: 2 additions & 8 deletions src/index.ts
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);
10 changes: 10 additions & 0 deletions test/body-parser.ts
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));
});
}
37 changes: 37 additions & 0 deletions test/mock-request-http-server.ts
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;
}
122 changes: 122 additions & 0 deletions test/request-common.test.ts
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");
}
});
});
Loading