Files
anonymous_github/test/runtime-regressions.test.js
T

236 lines
7.9 KiB
JavaScript

require("ts-node/register/transpile-only");
const path = require("path");
const { expect } = require("chai");
const Repository = require("../src/core/Repository").default;
const PullRequest = require("../src/core/PullRequest").default;
const PullRequestModel = require("../src/core/model/anonymizedPullRequests/anonymizedPullRequests.model").default;
const github = require("../src/core/GitHubUtils");
const githubApp = require("../src/core/github-app");
const routeUtils = require("../src/server/routes/route-utils");
const GitHubStream = require("../src/core/source/GitHubStream").default;
const {
RepositoryStatus,
} = require("../src/core/types");
const { joinUrl } = require("../src/core/url-utils");
const pullRequestRouter = require("../src/server/routes/pullRequest-private").default;
const {
resolveWorkerProcessorPath,
} = require("../src/queue/worker-path");
describe("Runtime regression fixes", function () {
const restores = [];
function stub(object, key, value) {
const original = object[key];
restores.push(() => { object[key] = original; });
object[key] = value;
}
afterEach(() => { while (restores.length) restores.pop()(); });
describe("joinUrl", function () {
it("preserves the double slash in https URLs", function () {
expect(joinUrl("https://streamer.example.com", "api/download")).to.equal(
"https://streamer.example.com/api/download"
);
});
it("handles base URLs that already end with a slash", function () {
expect(joinUrl("http://localhost:3000/", "/api")).to.equal(
"http://localhost:3000/api"
);
});
});
describe("PullRequest.download()", function () {
it("uses the authenticated GitHub API diff endpoint", async function () {
const pullRequest = new PullRequest(new PullRequestModel({
source: { repositoryFullName: "owner/repo", pullRequestId: 42 },
}));
pullRequest.getToken = async () => "secret-token";
const requests = [];
const tokens = [];
stub(github, "octokit", token => {
tokens.push(token);
return {
rest: { pulls: { get: async options => {
requests.push(options);
return { data: options.mediaType?.format === "diff" ? "private diff" : {
title: "Private PR", base: { repo: { full_name: "owner/repo" } }, head: {},
} };
} } },
paginate: async () => [],
};
});
await pullRequest.download();
expect(tokens).to.deep.equal(["secret-token"]);
expect(requests).to.deep.equal([
{ owner: "owner", repo: "repo", pull_number: 42 },
{ owner: "owner", repo: "repo", pull_number: 42, mediaType: { format: "diff" } },
]);
expect(pullRequest.model.pullRequest.diff).to.equal("private diff");
});
});
describe("POST / pull request creation", function () {
it("stores the conference on the persisted model", async function () {
const update = {
pullRequestId: "anon-pr-1",
conference: "conf-2026",
source: {
repositoryFullName: "owner/repo",
pullRequestId: 123,
},
options: {
expirationMode: "never",
},
terms: [],
};
const user = {
id: "507f191e810c19729de860ea",
};
const saved = [];
let response;
let anonymized = false;
stub(routeUtils, "getUser", async () => user);
stub(routeUtils, "handleError", error => { throw error; });
stub(githubApp, "selectRepositoryAccess", async () => ({ binding: { kind: "oauth" } }));
stub(PullRequestModel.prototype, "save", async function () {
saved.push(this.toObject());
return this;
});
stub(PullRequest.prototype, "anonymize", async function () {
expect(saved).to.have.length(1);
anonymized = true;
});
const route = pullRequestRouter.stack.find(layer =>
layer.route?.path === "/" && layer.route.methods.post).route;
await route.stack[0].handle({ body: update }, { send: value => { response = value; } });
expect(saved).to.have.length(1);
expect(saved[0].conference).to.equal("conf-2026");
expect(saved[0].source.repositoryFullName).to.equal("owner/repo");
expect(saved[0].source.pullRequestId).to.equal(123);
expect(response.conference).to.equal("conf-2026");
expect(anonymized).to.equal(true);
});
});
describe("Repository.check()", function () {
it("records logical expiration before throwing repository_expired", async function () {
const repo = new Repository({
owner: "507f191e810c19729de860ea",
options: {
expirationMode: "date",
expirationDate: new Date(Date.now() - 1000),
},
status: RepositoryStatus.READY,
statusDate: new Date(),
source: {
repositoryName: "owner/repo",
type: "GitHubStream",
},
});
let expired = false;
repo.markExpired = async function () {
await Promise.resolve();
expired = true;
this.model.status = RepositoryStatus.EXPIRED;
};
try {
await repo.check();
throw new Error("expected Repository.check() to throw");
} catch (error) {
expect(error.message).to.equal("repository_expired");
}
expect(expired).to.equal(true);
});
});
describe("GitHubStream.iterateFiles()", function () {
it("recursively refetches truncated subtrees", async function () {
const source = new GitHubStream({
repoId: "repo-1",
organization: "owner",
repoName: "repo",
commit: "head",
getToken: async () => "token",
});
const calls = [];
source.getGHTree = async function (_oct, _token, sha, _count, opt) {
calls.push({ sha, recursive: opt.recursive === true });
if (sha === "head" && opt.recursive) return { tree: [], truncated: true };
if (sha === "head") {
return {
tree: [{ type: "tree", path: "src", sha: "src-sha" }],
truncated: false,
};
}
if (sha === "src-sha" && opt.recursive === true) {
return {
tree: [],
truncated: true,
};
}
if (sha === "src-sha" && opt.recursive === false) {
return {
tree: [{ type: "tree", path: "deep", sha: "deep-sha" }],
truncated: false,
};
}
if (sha === "deep-sha" && opt.recursive === true) {
return {
tree: [{ type: "blob", path: "file.ts", sha: "file-sha" }],
truncated: false,
};
}
throw new Error(`unexpected request: ${sha}:${opt.recursive}`);
};
const files = [];
for await (const file of source.iterateFiles()) {
files.push(path.posix.join(file.path, file.name));
}
expect(files).to.deep.equal(["src", "src/deep", "src/deep/file.ts"]);
expect(calls).to.deep.equal([
{ sha: "head", recursive: true },
{ sha: "head", recursive: false },
{ sha: "src-sha", recursive: true },
{ sha: "src-sha", recursive: false },
{ sha: "deep-sha", recursive: true },
]);
});
});
describe("resolveWorkerProcessorPath", function () {
it("falls back to the source-mode worker wrapper when build output is missing", function () {
const resolved = resolveWorkerProcessorPath(
"downloadRepository",
() => false
);
expect(resolved).to.equal(
path.resolve("src/queue/processes/downloadRepository.worker.js")
);
});
it("prefers the built worker script when it exists", function () {
const resolved = resolveWorkerProcessorPath("removeCache", (candidate) =>
candidate.endsWith(path.join("build", "queue", "processes", "removeCache.js"))
);
expect(resolved).to.equal(
path.resolve("build/queue/processes/removeCache.js")
);
});
});
});