Skip to content
Closed
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
12 changes: 12 additions & 0 deletions src/handler-dispatcher.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,18 @@ describe("HandlerDispatcher", () => {
expect(coder.getCoderUserByGitHubId).toHaveBeenCalledWith(67890);
});

test("resolves coder user from sender (assigner), not issue author", async () => {
// senderId represents the person who assigned the bot, not the issue author
const ctx = {
...createTaskContext,
senderLogin: "org-maintainer",
senderId: 99999,
};
const result = makeDispatchedResult("create_task", ctx);
await dispatcher.dispatch(result);
expect(coder.getCoderUserByGitHubId).toHaveBeenCalledWith(99999);
});

test("creates a task and returns ActionOutputs", async () => {
coder.getTask.mockResolvedValue(null);
coder.createTask.mockResolvedValue(mockTask);
Expand Down
57 changes: 57 additions & 0 deletions src/handlers/create-task.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,63 @@ describe("CreateTaskHandler", () => {
expect(coder.createTask).not.toHaveBeenCalled();
});

// Permission check must use the sender (assigner), not the issue author.
// This covers the case where an external user without write access creates
// the issue and a maintainer with write access assigns the bot.
test("checks permission for senderLogin (the assigner)", async () => {
github.checkActorPermission.mockResolvedValue(true);
coder.getTask.mockResolvedValue(null);
coder.createTask.mockResolvedValue(mockTask);

const ctx = {
...issueContext,
senderLogin: "maintainer-who-assigned",
};
const handler = new CreateTaskHandler(
coder,
github as unknown as import("../github-client").GitHubClient,
baseInputs,
ctx,
logger,
);
await handler.run();

expect(github.checkActorPermission).toHaveBeenCalledWith(
ctx.owner,
ctx.repo,
"maintainer-who-assigned",
);
});

test("creates task when assigner has write access regardless of issue author", async () => {
github.checkActorPermission.mockResolvedValue(true);
coder.getTask.mockResolvedValue(null);
coder.createTask.mockResolvedValue(mockTask);

// senderLogin is the maintainer who assigned the bot, not the issue author
const ctx = {
...issueContext,
senderLogin: "org-maintainer",
};
const handler = new CreateTaskHandler(
coder,
github as unknown as import("../github-client").GitHubClient,
baseInputs,
ctx,
logger,
);
const result = await handler.run();

expect(result.skipped).toBe(false);
expect(coder.createTask).toHaveBeenCalledTimes(1);
// Verify permission was checked for the assigner, not anyone else
expect(github.checkActorPermission).toHaveBeenCalledWith(
"xmtp",
"libxmtp",
"org-maintainer",
);
});

// AC #4: Issue URL appended to prompt
test("appends issue URL to prompt", async () => {
github.checkActorPermission.mockResolvedValue(true);
Expand Down
23 changes: 23 additions & 0 deletions src/webhook-router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,29 @@ describe("WebhookRouter", () => {
expect(ctx.senderId).toBe(65710);
});

test("issues.assigned extracts senderLogin from sender, not issue author", async () => {
// Simulate: external user created the issue, maintainer assigned the bot
const payload = {
...issuesAssigned,
issue: {
...issuesAssigned.issue,
user: { login: "external-user", id: 11111 },
},
sender: { login: "org-maintainer", id: 22222 },
};
const result = await router.handleWebhook(
"issues",
"delivery-001b",
payload,
);

expect(result.dispatched).toBe(true);
if (!result.dispatched) throw new Error("expected dispatched");
const ctx = result.context as CreateTaskContext;
expect(ctx.senderLogin).toBe("org-maintainer");
expect(ctx.senderId).toBe(22222);
});

test("issues.assigned with non-matching assignee login → skipped", async () => {
const payload = {
...issuesAssigned,
Expand Down
Loading