import { afterEach, beforeEach, describe, expect, it } from "vitest"; import { resetWarmBashParser, warmBashParser, } from "#src/access-intent/bash/parser"; import { parseBashCommandsSync } from "#src/access-intent/bash/sync-commands"; describe("collectCommands — control-flow descent", () => { beforeEach(async () => { resetWarmBashParser(); await warmBashParser(); }); afterEach(() => { resetWarmBashParser(); }); it("emits a for-loop's body commands in addition to the whole statement", () => { expect( parseBashCommandsSync('for d in a b; do find "$d" -name x; done'), ).toEqual([ { text: 'for d in a b; do find "$d" -name x; done' }, { text: 'find "$d" -name x' }, ]); }); it("emits a command substitution in the for value list as a nested context", () => { expect( parseBashCommandsSync('for f in $(find . -name x); do echo "$f"; done'), ).toEqual([ { text: 'for f in $(find . -name x); do echo "$f"; done' }, { text: "find . -name x", context: "command_substitution" }, { text: 'echo "$f"' }, ]); }); it("emits the condition and body commands of a redirected while loop", () => { expect( parseBashCommandsSync('while read l; do grep x "$l"; done < list'), ).toEqual([ { text: 'while read l; do grep x "$l"; done' }, { text: "read l" }, { text: 'grep x "$l"' }, ]); }); it("emits an if's condition, branch, and else commands", () => { expect( parseBashCommandsSync("if grep -q x f; then echo y; else rm z; fi"), ).toEqual([ { text: "if grep -q x f; then echo y; else rm z; fi" }, { text: "grep -q x f" }, { text: "echo y" }, { text: "rm z" }, ]); }); it("emits a case item's commands", () => { expect(parseBashCommandsSync("case $x in a) find . ;; esac")).toEqual([ { text: "case $x in a) find . ;; esac" }, { text: "find ." }, ]); }); it("emits a command substitution in the case subject as a nested context", () => { expect( parseBashCommandsSync("case $(rm x) in a) find . ;; esac"), ).toEqual([ { text: "case $(rm x) in a) find . ;; esac" }, { text: "rm x", context: "command_substitution" }, { text: "find ." }, ]); }); it("descends a brace group", () => { expect(parseBashCommandsSync("{ grep x f; }")).toEqual([ { text: "{ grep x f; }" }, { text: "grep x f" }, ]); }); it("descends a function definition's body", () => { expect(parseBashCommandsSync("f() { grep x f; }")).toEqual([ { text: "f() { grep x f; }" }, { text: "{ grep x f; }" }, { text: "grep x f" }, ]); }); it("descends nested loops", () => { expect( parseBashCommandsSync("for a in 1; do for b in 2; do find .; done; done"), ).toEqual([ { text: "for a in 1; do for b in 2; do find .; done; done" }, { text: "for b in 2; do find .; done" }, { text: "find ." }, ]); }); it("descends a c-style for loop", () => { expect( parseBashCommandsSync("for ((i=0;i<3;i++)); do echo i; done"), ).toEqual([ { text: "for ((i=0;i<3;i++)); do echo i; done" }, { text: "echo i" }, ]); }); it("emits no container units for a clean loop body", () => { expect(parseBashCommandsSync('for d in a b; do echo "$d"; done')).toEqual([ { text: 'for d in a b; do echo "$d"; done' }, { text: 'echo "$d"' }, ]); }); it("does not emit a [[ ]] test as a separate command unit", () => { expect(parseBashCommandsSync("if [[ -f x ]]; then echo y; fi")).toEqual([ { text: "if [[ -f x ]]; then echo y; fi" }, { text: "echo y" }, ]); }); it("catches a substitution hosted inside a [[ ]] test", () => { expect( parseBashCommandsSync('if [[ "$(rm x)" = y ]]; then echo y; fi'), ).toEqual([ { text: 'if [[ "$(rm x)" = y ]]; then echo y; fi' }, { text: "rm x", context: "command_substitution" }, { text: "echo y" }, ]); }); }); describe("collectCommands — assignment, declaration, and negation", () => { beforeEach(async () => { resetWarmBashParser(); await warmBashParser(); }); afterEach(() => { resetWarmBashParser(); }); it("emits a substitution in a plain assignment value as a nested context", () => { expect(parseBashCommandsSync("x=$(grep foo f)")).toEqual([ { text: "x=$(grep foo f)" }, { text: "grep foo f", context: "command_substitution" }, ]); }); it("emits a substitution in an export declaration value", () => { expect(parseBashCommandsSync("export X=$(find .)")).toEqual([ { text: "export X=$(find .)" }, { text: "find .", context: "command_substitution" }, ]); }); it("emits a substitution in a local declaration inside a function body", () => { expect(parseBashCommandsSync("f() { local y=$(rg z); }")).toEqual([ { text: "f() { local y=$(rg z); }" }, { text: "{ local y=$(rg z); }" }, { text: "rg z", context: "command_substitution" }, ]); }); it("emits a negated command's inner command as a normal unit", () => { expect(parseBashCommandsSync("! grep -q x f")).toEqual([ { text: "! grep -q x f" }, { text: "grep -q x f" }, ]); }); it("emits a negated condition's inner command inside an if", () => { expect(parseBashCommandsSync("if ! grep -q x f; then :; fi")).toEqual([ { text: "if ! grep -q x f; then :; fi" }, { text: "! grep -q x f" }, { text: "grep -q x f" }, { text: ":" }, ]); }); });