- Same as #143 - Resolves https://github.com/stats-organization/github-stats-extended/pull/143#discussion_r3105155487 - Closes #141
278 lines
6.3 KiB
JavaScript
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"],
|
|
]);
|
|
});
|
|
});
|