Skip to content

Commit edba73a

Browse files
committed
test: add comprehensive real-world test cases for command pattern parser
- Added extensive test coverage for shell-quote based command parsing - Included edge cases: quotes, escapes, special characters, redirections - Added real-world examples: git, npm, docker, curl, ssh commands - Validated robustness of parser against complex shell command patterns - Ensures reliable command execution across different shell syntaxes
1 parent 60815f4 commit edba73a

File tree

1 file changed

+237
-0
lines changed

1 file changed

+237
-0
lines changed

webview-ui/src/utils/__tests__/commandPatterns.spec.ts

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,243 @@ describe("extractCommandPatterns", () => {
117117
expect(patterns).toEqual(["git", "git add", "git status"])
118118
})
119119
})
120+
121+
describe("complex real-world examples", () => {
122+
it("should correctly parse unzip with pipe chain", () => {
123+
const command = "unzip -l builds/roo-cline-3.21.5-error-boundary-component.vsix|grep map"
124+
const patterns = extractCommandPatterns(command)
125+
126+
// Should extract individual commands from the chain
127+
expect(patterns).toContain("unzip")
128+
expect(patterns).toContain("grep")
129+
130+
// Should not include the full chain
131+
expect(patterns).not.toContain(command)
132+
133+
// Should not include flags or arguments
134+
expect(patterns).not.toContain("unzip -l")
135+
expect(patterns).not.toContain("grep map")
136+
})
137+
138+
it("should correctly parse git push with multiple arguments", () => {
139+
const command = "git push github refactor-use-files-for-history -f"
140+
const patterns = extractCommandPatterns(command)
141+
142+
// Should extract base command and subcommand
143+
expect(patterns).toContain("git")
144+
expect(patterns).toContain("git push")
145+
146+
// Should not include remote, branch, or flags
147+
expect(patterns).not.toContain("git push github")
148+
expect(patterns).not.toContain("github")
149+
expect(patterns).not.toContain("refactor-use-files-for-history")
150+
expect(patterns).not.toContain("-f")
151+
})
152+
153+
it("should correctly parse complex build and install chain", () => {
154+
const command =
155+
"../roo-main/build.sh && code --install-extension builds/roo-cline-3.21.5-error-boundary-component.vsix && code"
156+
const patterns = extractCommandPatterns(command)
157+
158+
// Should extract script file
159+
expect(patterns).toContain("../roo-main/build.sh")
160+
161+
// Should extract code command (appears twice, but set will dedupe)
162+
expect(patterns).toContain("code")
163+
164+
// Should not include the full chain
165+
expect(patterns).not.toContain(command)
166+
167+
// Should not include flags or arguments
168+
expect(patterns).not.toContain("code --install-extension")
169+
expect(patterns).not.toContain("--install-extension")
170+
expect(patterns).not.toContain("builds/roo-cline-3.21.5-error-boundary-component.vsix")
171+
})
172+
173+
it("should handle npm scripts with arguments", () => {
174+
const command = "npm run build -- --watch"
175+
const patterns = extractCommandPatterns(command)
176+
177+
expect(patterns).toContain("npm")
178+
expect(patterns).toContain("npm run")
179+
// Should stop at 'run' to allow any script
180+
expect(patterns).not.toContain("npm run build")
181+
})
182+
183+
it("should handle docker commands with subcommands", () => {
184+
const command = "docker compose up -d"
185+
const patterns = extractCommandPatterns(command)
186+
187+
expect(patterns).toContain("docker")
188+
expect(patterns).toContain("docker compose")
189+
expect(patterns).not.toContain("docker compose up")
190+
expect(patterns).not.toContain("-d")
191+
})
192+
193+
it("should handle kubectl commands", () => {
194+
const command = "kubectl get pods -n production"
195+
const patterns = extractCommandPatterns(command)
196+
197+
expect(patterns).toContain("kubectl")
198+
expect(patterns).toContain("kubectl get")
199+
expect(patterns).not.toContain("kubectl get pods")
200+
expect(patterns).not.toContain("-n")
201+
expect(patterns).not.toContain("production")
202+
})
203+
204+
it("should handle make targets", () => {
205+
const command = "make clean && make build"
206+
const patterns = extractCommandPatterns(command)
207+
208+
expect(patterns).toContain("make")
209+
expect(patterns).toContain("make clean")
210+
expect(patterns).toContain("make build")
211+
})
212+
213+
it("should handle python scripts with arguments", () => {
214+
const command = "python3 scripts/deploy.py --env production"
215+
const patterns = extractCommandPatterns(command)
216+
217+
expect(patterns).toContain("python3")
218+
// Should stop after interpreter
219+
expect(patterns).not.toContain("scripts/deploy.py")
220+
expect(patterns).not.toContain("--env")
221+
})
222+
223+
it("should handle environment variables with commands", () => {
224+
const command = "NODE_ENV=test npm test && NODE_ENV=production npm start"
225+
const patterns = extractCommandPatterns(command)
226+
227+
expect(patterns).toContain("npm")
228+
expect(patterns).toContain("npm test")
229+
expect(patterns).toContain("npm start")
230+
expect(patterns).not.toContain("NODE_ENV=test")
231+
expect(patterns).not.toContain("NODE_ENV=production")
232+
})
233+
234+
it("should handle complex pipe with multiple tools", () => {
235+
const command = "ps aux | grep node | awk '{print $2}' | xargs kill -9"
236+
const patterns = extractCommandPatterns(command)
237+
238+
expect(patterns).toContain("ps")
239+
expect(patterns).toContain("grep")
240+
expect(patterns).toContain("awk")
241+
expect(patterns).toContain("xargs")
242+
// Note: "xargs kill -9" is parsed as a single command where xargs is the command
243+
// and "kill -9" are arguments, so kill is not extracted as a separate pattern
244+
expect(patterns).not.toContain("kill")
245+
expect(patterns).not.toContain("ps aux")
246+
expect(patterns).not.toContain("grep node")
247+
expect(patterns).not.toContain("kill -9")
248+
})
249+
250+
it("should handle yarn workspaces commands", () => {
251+
const command = "yarn workspace @myapp/frontend build"
252+
const patterns = extractCommandPatterns(command)
253+
254+
expect(patterns).toContain("yarn")
255+
expect(patterns).toContain("yarn workspace")
256+
// Should not include the workspace name
257+
expect(patterns).not.toContain("@myapp/frontend")
258+
expect(patterns).not.toContain("build")
259+
})
260+
261+
it("should handle pnpm commands", () => {
262+
const command = "pnpm --filter ./packages/* test"
263+
const patterns = extractCommandPatterns(command)
264+
265+
expect(patterns).toContain("pnpm")
266+
// Should stop at flags
267+
expect(patterns).not.toContain("pnpm --filter")
268+
expect(patterns).not.toContain("test")
269+
})
270+
271+
it("should handle curl with complex arguments", () => {
272+
const command =
273+
"curl -X POST https://api.example.com/data -H 'Content-Type: application/json' -d '{\"key\": \"value\"}'"
274+
const patterns = extractCommandPatterns(command)
275+
276+
expect(patterns).toContain("curl")
277+
// Should stop at flags
278+
expect(patterns).not.toContain("curl -X")
279+
expect(patterns).not.toContain("POST")
280+
expect(patterns).not.toContain("https://api.example.com/data")
281+
})
282+
283+
it("should handle ssh commands", () => {
284+
const command = "ssh [email protected] 'cd /app && npm restart'"
285+
const patterns = extractCommandPatterns(command)
286+
287+
expect(patterns).toContain("ssh")
288+
// Should not include user@server or remote command
289+
expect(patterns).not.toContain("[email protected]")
290+
expect(patterns).not.toContain("cd")
291+
expect(patterns).not.toContain("npm")
292+
})
293+
294+
it("should handle rsync commands", () => {
295+
const command = "rsync -avz --delete ./dist/ user@server:/var/www/html/"
296+
const patterns = extractCommandPatterns(command)
297+
298+
expect(patterns).toContain("rsync")
299+
// Should stop at flags
300+
expect(patterns).not.toContain("rsync -avz")
301+
expect(patterns).not.toContain("--delete")
302+
})
303+
304+
it("should handle find with exec", () => {
305+
const command = "find . -name '*.log' -exec rm {} \\;"
306+
const patterns = extractCommandPatterns(command)
307+
308+
expect(patterns).toContain("find")
309+
// Should not include dangerous operations
310+
expect(patterns).not.toContain("rm")
311+
expect(patterns).not.toContain("-exec")
312+
})
313+
314+
it("should handle systemctl commands", () => {
315+
const command = "sudo systemctl restart nginx"
316+
const patterns = extractCommandPatterns(command)
317+
318+
expect(patterns).toContain("sudo")
319+
// Note: sudo is the command, systemctl is treated as an argument
320+
// The parser doesn't have special handling for sudo to extract the actual command
321+
expect(patterns).not.toContain("systemctl")
322+
expect(patterns).not.toContain("nginx")
323+
})
324+
325+
it("should handle aws cli commands", () => {
326+
const command = "aws s3 sync ./build s3://my-bucket --delete"
327+
const patterns = extractCommandPatterns(command)
328+
329+
expect(patterns).toContain("aws")
330+
// aws is not in special handling, so behavior may vary
331+
expect(patterns).not.toContain("--delete")
332+
})
333+
334+
it("should handle multiple redirects", () => {
335+
const command = "echo 'test' > file.txt && cat file.txt >> output.log"
336+
const patterns = extractCommandPatterns(command)
337+
338+
expect(patterns).toContain("echo")
339+
expect(patterns).toContain("cat")
340+
expect(patterns).not.toContain("echo 'test'")
341+
expect(patterns).not.toContain("file.txt")
342+
expect(patterns).not.toContain("output.log")
343+
})
344+
345+
it("should handle background processes", () => {
346+
const command = "npm start & npm run worker &"
347+
const patterns = extractCommandPatterns(command)
348+
349+
expect(patterns).toContain("npm")
350+
expect(patterns).toContain("npm start")
351+
// Note: The & operator is not in the chainOperators list (only &&, ||, ;, |)
352+
// So this is parsed as a single command "npm start & npm run worker &"
353+
// The parser stops at the first & character when parsing "npm start &..."
354+
expect(patterns).not.toContain("npm run")
355+
})
356+
})
120357
})
121358

122359
describe("getPatternDescription", () => {

0 commit comments

Comments
 (0)