Files

278 lines
6.3 KiB
JavaScript

/**
* @file Tests for the status/pat-info cloud function.
*/
import axios from "axios";
import MockAdapter from "axios-mock-adapter";
import {
afterAll,
afterEach,
beforeAll,
describe,
expect,
it,
vi,
} from "vitest";
const mock = new MockAdapter(axios);
const successData = {
data: {
rateLimit: {
remaining: 4986,
},
},
};
const faker = (query) => {
const req = {
query: { ...query },
};
const res = {
setHeader: vi.fn(),
send: vi.fn(),
};
return { req, res };
};
const rate_limit_error = {
errors: [
{
type: "RATE_LIMITED",
message: "API rate limit exceeded for user ID.",
},
],
data: {
rateLimit: {
resetAt: Date.now(),
},
},
};
const other_error = {
errors: [
{
type: "SOME_ERROR",
message: "This is a error",
},
],
};
const bad_credentials_error = {
message: "Bad credentials",
};
let RATE_LIMIT_SECONDS, patInfo;
beforeAll(async () => {
vi.stubEnv("PAT_1", "testPAT1");
vi.stubEnv("PAT_2", "testPAT2");
vi.stubEnv("PAT_3", "testPAT3");
vi.stubEnv("PAT_4", "testPAT4");
const { logger } =
await import("@stats-organization/github-readme-stats-core");
vi.spyOn(logger, "log").mockImplementation(() => {});
vi.spyOn(logger, "error").mockImplementation(() => {});
({ RATE_LIMIT_SECONDS, default: patInfo } =
await import("../api-renamed/status/pat-info.js"));
});
afterEach(() => {
mock.reset();
vi.unstubAllEnvs();
// modules may cache environment variables, so we need to reset them
vi.resetModules();
});
afterAll(() => {
vi.restoreAllMocks();
});
describe("Test /api/status/pat-info", () => {
it("should return only 'validPATs' if all PATs are valid", async () => {
mock
.onPost("https://api.github.com/graphql")
.replyOnce(200, rate_limit_error)
.onPost("https://api.github.com/graphql")
.reply(200, successData);
const { req, res } = faker({}, {});
await patInfo(req, res);
expect(res.setHeader).toHaveBeenCalledWith(
"Content-Type",
"application/json",
);
expect(res.send).toHaveBeenCalledWith(
JSON.stringify(
{
validPATs: ["PAT_2", "PAT_3", "PAT_4"],
expiredPATs: [],
exhaustedPATs: ["PAT_1"],
suspendedPATs: [],
errorPATs: [],
details: {
PAT_1: {
status: "exhausted",
remaining: 0,
resetIn: "0 minutes",
},
PAT_2: {
status: "valid",
remaining: 4986,
},
PAT_3: {
status: "valid",
remaining: 4986,
},
PAT_4: {
status: "valid",
remaining: 4986,
},
},
},
null,
2,
),
);
});
it("should return `errorPATs` if a PAT causes an error to be thrown", async () => {
mock
.onPost("https://api.github.com/graphql")
.replyOnce(200, other_error)
.onPost("https://api.github.com/graphql")
.reply(200, successData);
const { req, res } = faker({}, {});
await patInfo(req, res);
expect(res.setHeader).toHaveBeenCalledWith(
"Content-Type",
"application/json",
);
expect(res.send).toHaveBeenCalledWith(
JSON.stringify(
{
validPATs: ["PAT_2", "PAT_3", "PAT_4"],
expiredPATs: [],
exhaustedPATs: [],
suspendedPATs: [],
errorPATs: ["PAT_1"],
details: {
PAT_1: {
status: "error",
error: {
type: "SOME_ERROR",
message: "This is a error",
},
},
PAT_2: {
status: "valid",
remaining: 4986,
},
PAT_3: {
status: "valid",
remaining: 4986,
},
PAT_4: {
status: "valid",
remaining: 4986,
},
},
},
null,
2,
),
);
});
it("should return `expiredPaths` if a PAT returns a 'Bad credentials' error", async () => {
mock
.onPost("https://api.github.com/graphql")
.replyOnce(404, bad_credentials_error)
.onPost("https://api.github.com/graphql")
.reply(200, successData);
const { req, res } = faker({}, {});
await patInfo(req, res);
expect(res.setHeader).toHaveBeenCalledWith(
"Content-Type",
"application/json",
);
expect(res.send).toHaveBeenCalledWith(
JSON.stringify(
{
validPATs: ["PAT_2", "PAT_3", "PAT_4"],
expiredPATs: ["PAT_1"],
exhaustedPATs: [],
suspendedPATs: [],
errorPATs: [],
details: {
PAT_1: {
status: "expired",
},
PAT_2: {
status: "valid",
remaining: 4986,
},
PAT_3: {
status: "valid",
remaining: 4986,
},
PAT_4: {
status: "valid",
remaining: 4986,
},
},
},
null,
2,
),
);
});
it("should throw an error if something goes wrong", async () => {
mock.onPost("https://api.github.com/graphql").networkError();
const { req, res } = faker({}, {});
await patInfo(req, res);
expect(res.setHeader).toHaveBeenCalledWith(
"Content-Type",
"application/json",
);
expect(res.send).toHaveBeenCalledWith(
"Something went wrong: Network Error",
);
});
it("should have proper cache when no error is thrown", async () => {
mock.onPost("https://api.github.com/graphql").reply(200, successData);
const { req, res } = faker({}, {});
await patInfo(req, res);
expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "application/json"],
["Cache-Control", `max-age=0, s-maxage=${RATE_LIMIT_SECONDS}`],
]);
});
it("should have proper cache when error is thrown", async () => {
mock.onPost("https://api.github.com/graphql").networkError();
const { req, res } = faker({}, {});
await patInfo(req, res);
expect(res.setHeader.mock.calls).toEqual([
["Content-Type", "application/json"],
["Cache-Control", "no-store"],
]);
});
});