Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 40 additions & 12 deletions apps/server/src/pullRequest/GiteaPullRequestApi.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,12 +144,30 @@ it.effect("keeps a search hydration transport failure fatal", () =>
);

layer("GiteaPullRequestApi", (it) => {
it.effect("reconstructs auto-merge from the timeline when discovery is unavailable", () => Effect.gen(function* () {
mockedRequest.mockReturnValueOnce(Effect.fail(new GiteaApi.GiteaApiError({operation: "getFeatures", reason: "failed", detail: "temporarily unavailable"}))).mockReturnValueOnce(Effect.succeed(response([{id: 1, type: "pull_scheduled_merge"}])));
const api = yield* GiteaPullRequestApi.make;
expect(yield* api.getAutoMergeEnabled({host: "forge.example.test", repository: "acme/web", number: 7})).toBe(true);
expect(callAt(1).path).toContain("/timeline?");
}));
it.effect("reconstructs auto-merge from the timeline when discovery is unavailable", () =>
Effect.gen(function* () {
mockedRequest
.mockReturnValueOnce(
Effect.fail(
new GiteaApi.GiteaApiError({
operation: "getFeatures",
reason: "failed",
detail: "temporarily unavailable",
}),
),
)
.mockReturnValueOnce(Effect.succeed(response([{ id: 1, type: "pull_scheduled_merge" }])));
const api = yield* GiteaPullRequestApi.make;
expect(
yield* api.getAutoMergeEnabled({
host: "forge.example.test",
repository: "acme/web",
number: 7,
}),
).toBe(true);
expect(callAt(1).path).toContain("/timeline?");
}),
);
it.effect("opens a native revert PR only on an advertising Gitea server", () =>
Effect.gen(function* () {
mockedRequest
Expand Down Expand Up @@ -821,7 +839,7 @@ layer("GiteaPullRequestApi", (it) => {
}),
);

it.effect("does not turn omitted repository permissions into a denial", () =>
it.effect("does not advertise writes or merge methods from incomplete repository settings", () =>
Effect.gen(function* () {
mockedRequest.mockReturnValueOnce(Effect.succeed(response({})));
const api = yield* GiteaPullRequestApi.make;
Expand All @@ -831,13 +849,13 @@ layer("GiteaPullRequestApi", (it) => {
});

expect(access).toEqual({
canWrite: true,
canWrite: false,
mergeCapabilities: {
merge: true,
squash: true,
rebase: true,
merge: false,
squash: false,
rebase: false,
},
updateMethods: ["merge", "rebase"],
updateMethods: [],
});
}),
);
Expand Down Expand Up @@ -1042,6 +1060,16 @@ layer("GiteaPullRequestApi", (it) => {
}),
);

it.effect("does not request commit statuses without a head revision", () =>
Effect.gen(function* () {
const api = yield* GiteaPullRequestApi.make;
expect(
yield* api.listChecks({ host: "forge.example.test", repository: "acme/web", sha: "" }),
).toEqual([]);
expect(mockedRequest).not.toHaveBeenCalled();
}),
);

it.effect("reads every capped page of commit statuses and keeps the newest context", () =>
Effect.gen(function* () {
mockedRequest
Expand Down
18 changes: 7 additions & 11 deletions apps/server/src/pullRequest/GiteaPullRequestApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1082,20 +1082,15 @@ export const make = Effect.gen(function* () {
});
const repo = yield* decode(operation, RawRepository, response);
return {
// An omitted permission block is unknown rather than a denial. Gitea will still enforce
// the write, while hiding it here would leave an entitled viewer with no route to try.
canWrite:
repo.permissions == null ||
repo.permissions.push === true ||
repo.permissions.admin === true,
canWrite: repo.permissions?.push === true || repo.permissions?.admin === true,
mergeCapabilities: {
merge: repo.allow_merge_commits ?? true,
squash: repo.allow_squash_merge ?? true,
rebase: repo.allow_rebase ?? true,
merge: repo.allow_merge_commits === true,
squash: repo.allow_squash_merge === true,
rebase: repo.allow_rebase === true,
},
updateMethods: [
...(repo.allow_merge_update !== false ? (["merge"] as const) : []),
...(repo.allow_rebase_update !== false ? (["rebase"] as const) : []),
...(repo.allow_merge_update === true ? (["merge"] as const) : []),
...(repo.allow_rebase_update === true ? (["rebase"] as const) : []),
],
};
},
Expand Down Expand Up @@ -1337,6 +1332,7 @@ export const make = Effect.gen(function* () {
sha: string;
}) {
const operation = "listChecks";
if (input.sha.trim() === "") return [];
const statuses: Array<RawCommitStatus> = [];
let path = query(
`${basePath(input.repository)}/commits/${encodeURIComponent(input.sha)}/status`,
Expand Down