Skip to content

Commit 571d1a0

Browse files
committed
no-mistakes: apply CI fixes
1 parent ea1f06a commit 571d1a0

5 files changed

Lines changed: 465 additions & 20 deletions

File tree

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import { AUTONOMOUS_EXIT_CODES, AutonomousRunError, type AutonomousTerminalState } from "../autonomous-run.js"
2+
3+
describe("AutonomousRunError", () => {
4+
it("should create error with correct state and message", () => {
5+
const error = new AutonomousRunError("needs_input", "User input required")
6+
expect(error.state).toBe("needs_input")
7+
expect(error.message).toBe("User input required")
8+
expect(error.name).toBe("AutonomousRunError")
9+
})
10+
11+
it("should support all terminal states except completed", () => {
12+
const states: Array<Exclude<AutonomousTerminalState, "completed">> = [
13+
"needs_input",
14+
"provider_failed",
15+
"tool_failed",
16+
"cancelled",
17+
"timed_out",
18+
"configuration_error",
19+
"crashed",
20+
]
21+
22+
states.forEach((state) => {
23+
const error = new AutonomousRunError(state, `Test ${state}`)
24+
expect(error.state).toBe(state)
25+
expect(error.message).toBe(`Test ${state}`)
26+
})
27+
})
28+
29+
it("should be instanceof Error", () => {
30+
const error = new AutonomousRunError("crashed", "Something went wrong")
31+
expect(error).toBeInstanceOf(Error)
32+
expect(error).toBeInstanceOf(AutonomousRunError)
33+
})
34+
})
35+
36+
describe("AUTONOMOUS_EXIT_CODES", () => {
37+
it("should map completed to exit code 0", () => {
38+
expect(AUTONOMOUS_EXIT_CODES.completed).toBe(0)
39+
})
40+
41+
it("should map needs_input to exit code 2", () => {
42+
expect(AUTONOMOUS_EXIT_CODES.needs_input).toBe(2)
43+
})
44+
45+
it("should map provider_failed to exit code 4", () => {
46+
expect(AUTONOMOUS_EXIT_CODES.provider_failed).toBe(4)
47+
})
48+
49+
it("should map tool_failed to exit code 5", () => {
50+
expect(AUTONOMOUS_EXIT_CODES.tool_failed).toBe(5)
51+
})
52+
53+
it("should map cancelled to exit code 6", () => {
54+
expect(AUTONOMOUS_EXIT_CODES.cancelled).toBe(6)
55+
})
56+
57+
it("should map timed_out to exit code 124", () => {
58+
expect(AUTONOMOUS_EXIT_CODES.timed_out).toBe(124)
59+
})
60+
61+
it("should map configuration_error to exit code 78", () => {
62+
expect(AUTONOMOUS_EXIT_CODES.configuration_error).toBe(78)
63+
})
64+
65+
it("should map crashed to exit code 70", () => {
66+
expect(AUTONOMOUS_EXIT_CODES.crashed).toBe(70)
67+
})
68+
69+
it("should have mappings for all terminal states", () => {
70+
const expectedStates: AutonomousTerminalState[] = [
71+
"completed",
72+
"needs_input",
73+
"provider_failed",
74+
"tool_failed",
75+
"cancelled",
76+
"timed_out",
77+
"configuration_error",
78+
"crashed",
79+
]
80+
81+
expectedStates.forEach((state) => {
82+
expect(AUTONOMOUS_EXIT_CODES[state]).toBeDefined()
83+
expect(typeof AUTONOMOUS_EXIT_CODES[state]).toBe("number")
84+
})
85+
})
86+
87+
it("should use conventional exit codes", () => {
88+
// Exit code 0 = success
89+
expect(AUTONOMOUS_EXIT_CODES.completed).toBe(0)
90+
// Exit code 124 is conventional for timeout (from GNU timeout command)
91+
expect(AUTONOMOUS_EXIT_CODES.timed_out).toBe(124)
92+
// Exit code 78 is EX_CONFIG from sysexits.h
93+
expect(AUTONOMOUS_EXIT_CODES.configuration_error).toBe(78)
94+
// Exit code 70 is EX_SOFTWARE from sysexits.h
95+
expect(AUTONOMOUS_EXIT_CODES.crashed).toBe(70)
96+
})
97+
})
Lines changed: 227 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,227 @@
1+
import { validateAutonomousFlags, validateProviderBaseUrl } from "../autonomous-validation.js"
2+
3+
describe("validateAutonomousFlags", () => {
4+
it("returns no errors for valid autonomous configuration", () => {
5+
const errors = validateAutonomousFlags({
6+
autonomous: true,
7+
print: true,
8+
workspace: "/path/to/workspace",
9+
timeout: 300,
10+
provider: "openrouter",
11+
})
12+
expect(errors).toEqual([])
13+
})
14+
15+
it("returns no errors when autonomous is false regardless of other flags", () => {
16+
const errors = validateAutonomousFlags({
17+
autonomous: false,
18+
mode: "code",
19+
requireApproval: true,
20+
print: false,
21+
})
22+
expect(errors).toEqual([])
23+
})
24+
25+
it("returns error when mode is specified with autonomous", () => {
26+
const errors = validateAutonomousFlags({
27+
autonomous: true,
28+
mode: "code",
29+
print: true,
30+
workspace: "/path",
31+
timeout: 300,
32+
})
33+
expect(errors).toHaveLength(1)
34+
expect(errors[0].message).toContain("--mode cannot be used with --autonomous")
35+
})
36+
37+
it("returns error when requireApproval is true with autonomous", () => {
38+
const errors = validateAutonomousFlags({
39+
autonomous: true,
40+
requireApproval: true,
41+
print: true,
42+
workspace: "/path",
43+
timeout: 300,
44+
})
45+
expect(errors).toHaveLength(1)
46+
expect(errors[0].message).toContain("--require-approval cannot be used with --autonomous")
47+
})
48+
49+
it("returns error when print is false with autonomous", () => {
50+
const errors = validateAutonomousFlags({
51+
autonomous: true,
52+
print: false,
53+
workspace: "/path",
54+
timeout: 300,
55+
})
56+
expect(errors).toHaveLength(1)
57+
expect(errors[0].message).toContain("--autonomous requires --print")
58+
})
59+
60+
it("returns error when stdinPromptStream is true with autonomous", () => {
61+
const errors = validateAutonomousFlags({
62+
autonomous: true,
63+
stdinPromptStream: true,
64+
print: true,
65+
workspace: "/path",
66+
timeout: 300,
67+
})
68+
expect(errors).toHaveLength(1)
69+
expect(errors[0].message).toContain("cannot use --stdin-prompt-stream")
70+
})
71+
72+
it("returns error when workspace is not specified with autonomous", () => {
73+
const errors = validateAutonomousFlags({
74+
autonomous: true,
75+
print: true,
76+
timeout: 300,
77+
})
78+
expect(errors).toHaveLength(1)
79+
expect(errors[0].message).toContain("--autonomous requires an explicit --workspace")
80+
})
81+
82+
it("returns error when timeout is not specified with autonomous", () => {
83+
const errors = validateAutonomousFlags({
84+
autonomous: true,
85+
print: true,
86+
workspace: "/path",
87+
})
88+
expect(errors).toHaveLength(1)
89+
expect(errors[0].message).toContain("--autonomous requires --timeout")
90+
})
91+
92+
it("returns error when timeout is zero with autonomous", () => {
93+
const errors = validateAutonomousFlags({
94+
autonomous: true,
95+
print: true,
96+
workspace: "/path",
97+
timeout: 0,
98+
})
99+
expect(errors).toHaveLength(1)
100+
expect(errors[0].message).toContain("--autonomous requires --timeout")
101+
})
102+
103+
it("returns error when timeout is negative with autonomous", () => {
104+
const errors = validateAutonomousFlags({
105+
autonomous: true,
106+
print: true,
107+
workspace: "/path",
108+
timeout: -10,
109+
})
110+
expect(errors).toHaveLength(1)
111+
expect(errors[0].message).toContain("--autonomous requires --timeout")
112+
})
113+
114+
it("returns error when timeout is NaN with autonomous", () => {
115+
const errors = validateAutonomousFlags({
116+
autonomous: true,
117+
print: true,
118+
workspace: "/path",
119+
timeout: NaN,
120+
})
121+
expect(errors).toHaveLength(1)
122+
expect(errors[0].message).toContain("--autonomous requires --timeout")
123+
})
124+
125+
it("returns error when timeout is Infinity with autonomous", () => {
126+
const errors = validateAutonomousFlags({
127+
autonomous: true,
128+
print: true,
129+
workspace: "/path",
130+
timeout: Infinity,
131+
})
132+
expect(errors).toHaveLength(1)
133+
expect(errors[0].message).toContain("--autonomous requires --timeout")
134+
})
135+
136+
it("accepts valid positive timeout values", () => {
137+
const validTimeouts = [1, 60, 300, 3600, 0.5, 1.5]
138+
validTimeouts.forEach((timeout) => {
139+
const errors = validateAutonomousFlags({
140+
autonomous: true,
141+
print: true,
142+
workspace: "/path",
143+
timeout,
144+
})
145+
expect(errors).toEqual([])
146+
})
147+
})
148+
149+
it("returns multiple errors when multiple validations fail", () => {
150+
const errors = validateAutonomousFlags({
151+
autonomous: true,
152+
mode: "code",
153+
requireApproval: true,
154+
print: false,
155+
stdinPromptStream: true,
156+
// missing workspace and timeout
157+
})
158+
expect(errors.length).toBeGreaterThan(1)
159+
expect(errors.some((e) => e.message.includes("--mode"))).toBe(true)
160+
expect(errors.some((e) => e.message.includes("--require-approval"))).toBe(true)
161+
expect(errors.some((e) => e.message.includes("--print"))).toBe(true)
162+
expect(errors.some((e) => e.message.includes("--stdin-prompt-stream"))).toBe(true)
163+
expect(errors.some((e) => e.message.includes("--workspace"))).toBe(true)
164+
expect(errors.some((e) => e.message.includes("--timeout"))).toBe(true)
165+
})
166+
167+
it("returns error when providerBaseUrl is used with non-openrouter provider in autonomous mode", () => {
168+
const errors = validateAutonomousFlags({
169+
autonomous: true,
170+
print: true,
171+
workspace: "/path",
172+
timeout: 300,
173+
providerBaseUrl: "https://custom.api",
174+
provider: "anthropic",
175+
})
176+
expect(errors).toHaveLength(1)
177+
expect(errors[0].message).toContain("--provider-base-url is currently supported only with --provider openrouter")
178+
})
179+
180+
it("allows providerBaseUrl with openrouter provider in autonomous mode", () => {
181+
const errors = validateAutonomousFlags({
182+
autonomous: true,
183+
print: true,
184+
workspace: "/path",
185+
timeout: 300,
186+
providerBaseUrl: "https://custom.api",
187+
provider: "openrouter",
188+
})
189+
expect(errors).toEqual([])
190+
})
191+
})
192+
193+
describe("validateProviderBaseUrl", () => {
194+
it("returns null when providerBaseUrl is not specified", () => {
195+
expect(validateProviderBaseUrl(undefined, "anthropic")).toBeNull()
196+
expect(validateProviderBaseUrl(undefined, "openrouter")).toBeNull()
197+
expect(validateProviderBaseUrl(undefined, undefined)).toBeNull()
198+
})
199+
200+
it("returns null when providerBaseUrl is used with openrouter", () => {
201+
expect(validateProviderBaseUrl("https://custom.api", "openrouter")).toBeNull()
202+
})
203+
204+
it("returns error when providerBaseUrl is used with anthropic", () => {
205+
const error = validateProviderBaseUrl("https://custom.api", "anthropic")
206+
expect(error).not.toBeNull()
207+
expect(error?.message).toContain("--provider-base-url is currently supported only with --provider openrouter")
208+
})
209+
210+
it("returns error when providerBaseUrl is used with openai-native", () => {
211+
const error = validateProviderBaseUrl("https://custom.api", "openai-native")
212+
expect(error).not.toBeNull()
213+
expect(error?.message).toContain("--provider-base-url is currently supported only with --provider openrouter")
214+
})
215+
216+
it("returns error when providerBaseUrl is used with gemini", () => {
217+
const error = validateProviderBaseUrl("https://custom.api", "gemini")
218+
expect(error).not.toBeNull()
219+
expect(error?.message).toContain("--provider-base-url is currently supported only with --provider openrouter")
220+
})
221+
222+
it("returns error when providerBaseUrl is used without a provider", () => {
223+
const error = validateProviderBaseUrl("https://custom.api", undefined)
224+
expect(error).not.toBeNull()
225+
expect(error?.message).toContain("--provider-base-url is currently supported only with --provider openrouter")
226+
})
227+
})

0 commit comments

Comments
 (0)