|
1 | 1 | import fs from "node:fs"; |
2 | 2 | import path from "node:path"; |
3 | | -import {afterEach, beforeEach, expect, it} from "vitest"; |
| 3 | +import {afterEach, beforeEach, expect, it, vi} from "vitest"; |
4 | 4 | import {ApprovalOptionId} from "../../../ApprovalOptionId"; |
5 | 5 | import { |
| 6 | + createAuthenticatedFixture, |
6 | 7 | createPermissionResponse, |
7 | 8 | createPermissionResponder, |
8 | 9 | createReadOnlyFixture, |
@@ -69,3 +70,47 @@ describeE2E("E2E shell approval tests", () => { |
69 | 70 | expect(fixture.readPermissionRequests(sessionId, "execute").length).toBe(2); |
70 | 71 | }); |
71 | 72 | }); |
| 73 | + |
| 74 | +describeE2E("E2E shell cancellation tests", () => { |
| 75 | + let fixture: SpawnedAgentFixture | null = null; |
| 76 | + |
| 77 | + afterEach(async () => { |
| 78 | + await fixture?.dispose(); |
| 79 | + fixture = null; |
| 80 | + }); |
| 81 | + |
| 82 | + function isProcessRunning(pid: number): boolean { |
| 83 | + try { |
| 84 | + process.kill(pid, 0); |
| 85 | + return true; |
| 86 | + } catch { |
| 87 | + return false; |
| 88 | + } |
| 89 | + } |
| 90 | + |
| 91 | + it("cancels a running shell command", async () => { |
| 92 | + fixture = await createAuthenticatedFixture(); |
| 93 | + const sessionId = (await fixture.createSession()).sessionId; |
| 94 | + const pidFilePath = path.join(fixture.workspaceDir, "cancel-command.pid"); |
| 95 | + const command = `/bin/sh -c 'echo $$ > "${pidFilePath}"; exec sleep 100'`; |
| 96 | + |
| 97 | + const promptResponse = fixture.connection.prompt({ |
| 98 | + sessionId, |
| 99 | + prompt: [{type: "text", text: `Use your shell tool to run exactly \`${command}\`.`}], |
| 100 | + }); |
| 101 | + |
| 102 | + const pid = await vi.waitFor(() => { |
| 103 | + const content = fs.existsSync(pidFilePath) ? fs.readFileSync(pidFilePath, "utf8").trim() : ""; |
| 104 | + const parsed = Number.parseInt(content, 10); |
| 105 | + expect(parsed).toBeGreaterThan(0); |
| 106 | + return parsed; |
| 107 | + }, {timeout: 10_000}); |
| 108 | + expect(isProcessRunning(pid)).toBe(true); |
| 109 | + await fixture.connection.cancel({sessionId}); |
| 110 | + |
| 111 | + expect((await promptResponse).stopReason).toBe("cancelled"); |
| 112 | + await vi.waitFor(() => { |
| 113 | + expect(isProcessRunning(pid)).toBe(false); |
| 114 | + }, {timeout: 5_000}); |
| 115 | + }); |
| 116 | +}); |
0 commit comments