mirror of
https://github.com/tdurieux/anonymous_github.git
synced 2026-10-19 14:17:20 +02:00
* fix: repair private repository access from the edit page * fix: respect App rollout and reload renamed sources after repair * fix: gate access repair by status and update response tests
105 lines
6.0 KiB
JavaScript
105 lines
6.0 KiB
JavaScript
const { expect } = require("chai");
|
|
require("ts-node/register/transpile-only");
|
|
const config = require("../src/config").default;
|
|
const app = require("../src/core/github-app");
|
|
const utils = require("../src/server/routes/route-utils");
|
|
const Repositories = require("../src/core/model/anonymizedRepositories/anonymizedRepositories.model").default;
|
|
const PullRequests = require("../src/core/model/anonymizedPullRequests/anonymizedPullRequests.model").default;
|
|
const { githubAppRouter } = require("../src/server/routes/github-app");
|
|
const migrate = githubAppRouter.stack.find(layer => layer.route?.path === "/connections/migrate").route.stack[0].handle;
|
|
|
|
for (const type of ["repository", "pull-request"]) {
|
|
describe(`${type} App reinstall recovery`, function () {
|
|
let restores, model, selected, writes, checks, owner;
|
|
function stub(object, key, value) {
|
|
const previous = object[key]; restores.push(() => { object[key] = previous; }); object[key] = value;
|
|
}
|
|
beforeEach(function () {
|
|
restores = []; writes = []; checks = []; owner = "owner";
|
|
model = { _id: "saved-resource", owner: "owner", status: "ready", statusDate: new Date(),
|
|
repoId: "published-url", pullRequestId: "published-url",
|
|
source: { repositoryName: "owner/private", repositoryFullName: "owner/private", commit: "saved-sha", pullRequestId: 7 },
|
|
githubAccess: { kind: "github-app", repositoryId: 42, installationId: 100, revision: "old" },
|
|
};
|
|
selected = { token: "fresh-token", binding: { kind: "github-app", repositoryId: 42, installationId: 200, revision: "new" } };
|
|
stub(config, "GITHUB_APP_ENABLED", true); stub(config, "GITHUB_APP_NEW_CONNECTIONS", true);
|
|
stub(utils, "getUser", async () => ({ id: owner }));
|
|
stub(utils, "handleError", error => { throw error; });
|
|
stub(app, "selectRepositoryAccess", async (id, name, connection, repositoryId) => {
|
|
expect(repositoryId).to.equal(42);
|
|
expect([id, name, connection]).to.deep.equal(["owner", "owner/private", "github-app"]);
|
|
return selected;
|
|
});
|
|
stub(app, "githubRequest", async (url, token) => { checks.push({ url, token }); return {}; });
|
|
const Model = type === "repository" ? Repositories : PullRequests;
|
|
stub(Model, "findOne", async filter => filter.owner === model.owner ? model : null);
|
|
stub(Model, "updateOne", async (filter, update) => { writes.push({ filter, update }); return { modifiedCount: 1 }; });
|
|
});
|
|
afterEach(function () { while (restores.length) restores.pop()(); });
|
|
async function run(preview = false) {
|
|
let response;
|
|
await migrate({ body: { type, id: "published-url", connection: "github-app", preview } }, { json: body => { response = body; } });
|
|
return response;
|
|
}
|
|
async function fails(message) {
|
|
let error; try { await run(); } catch (caught) { error = caught; }
|
|
expect(error?.message).to.equal(message); expect(writes).to.have.length(0);
|
|
}
|
|
it("previews fresh installation access without changing the saved binding", async function () {
|
|
expect(await run(true)).to.deep.equal({ eligible: true, connection: "github-app" });
|
|
expect(writes).to.have.length(0); expect(model.githubAccess.installationId).to.equal(100);
|
|
});
|
|
it("replaces only the binding while preserving the URL, source and snapshot", async function () {
|
|
expect(await run()).to.deep.equal({ connection: "github-app", fullName: "owner/private" });
|
|
expect(writes).to.have.length(1);
|
|
expect(writes[0].update).to.deep.equal({ $set: { githubAccess: selected.binding } });
|
|
expect(writes[0].filter.githubAccess).to.deep.equal(model.githubAccess);
|
|
expect(writes[0].filter.source).to.deep.equal(model.source);
|
|
expect(checks).to.deep.equal([{ url: type === "repository" ? "/repos/owner/private/commits/saved-sha" : "/repos/owner/private/pulls/7", token: "fresh-token" }]);
|
|
});
|
|
it("saves a resolved rename atomically with the binding after preview", async function () {
|
|
selected.fullName = "owner/renamed";
|
|
await run(true);
|
|
expect(writes).to.have.length(0);
|
|
await run();
|
|
expect(writes).to.have.length(1);
|
|
expect(writes[0].update).to.deep.equal({ $set: {
|
|
githubAccess: selected.binding,
|
|
[type === "repository" ? "source.repositoryName" : "source.repositoryFullName"]: "owner/renamed",
|
|
} });
|
|
expect(writes[0].filter.source).to.deep.equal(model.source);
|
|
expect(writes[0].filter.githubAccess).to.deep.equal(model.githubAccess);
|
|
expect(checks.every(check => check.url.startsWith("/repos/owner/renamed/"))).to.equal(true);
|
|
});
|
|
it("rejects a replacement repository at the same name", async function () {
|
|
selected.binding.repositoryId = 99; await fails("connection_changed"); expect(checks).to.have.length(0);
|
|
});
|
|
for (const status of ["preparing", "queue", "download", "removing", "expiring"]) {
|
|
it("rejects reconnect during " + status, async function () {
|
|
model.status = status;
|
|
await fails("repository_busy");
|
|
expect(checks).to.have.length(0);
|
|
});
|
|
}
|
|
it("requires access to the existing snapshot before saving", async function () {
|
|
stub(app, "githubRequest", async () => { throw new Error("github_app_access_required"); });
|
|
await fails("github_app_access_required");
|
|
});
|
|
if (type === "pull-request") {
|
|
it("allows stale downloads but guards their timestamp against a new refresh", async function () {
|
|
model.status = "download";
|
|
model.statusDate = new Date(Date.now() - 6 * 60 * 1000);
|
|
await run();
|
|
expect(writes[0].filter.statusDate).to.equal(model.statusDate);
|
|
});
|
|
}
|
|
it("does not let another user repair the binding", async function () {
|
|
owner = "other"; await fails("repo_not_found");
|
|
});
|
|
it("reports a concurrent change instead of overwriting it", async function () {
|
|
stub(type === "repository" ? Repositories : PullRequests, "updateOne", async () => ({ modifiedCount: 0 }));
|
|
await fails("connection_changed");
|
|
});
|
|
});
|
|
}
|