397777089e
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
73 lines
2.2 KiB
JavaScript
73 lines
2.2 KiB
JavaScript
import { describe, it, expect } from 'vitest';
|
|
import { tokenizeBash, isMutatingSegment } from './bash-tokenizer.mjs';
|
|
|
|
describe('tokenizeBash — basics', () => {
|
|
it('tokenizes a simple command', () => {
|
|
const r = tokenizeBash('ls -la /tmp');
|
|
expect(r.ok).toBe(true);
|
|
expect(r.segments).toHaveLength(1);
|
|
expect(r.segments[0].tokens).toEqual(['ls', '-la', '/tmp']);
|
|
expect(r.hasSubshell).toBe(false);
|
|
});
|
|
|
|
it('returns ok:false on empty input', () => {
|
|
expect(tokenizeBash('').ok).toBe(false);
|
|
expect(tokenizeBash(' ').ok).toBe(false);
|
|
expect(tokenizeBash(null).ok).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('tokenizeBash — segments & operators', () => {
|
|
it('splits on && and records the operator', () => {
|
|
const r = tokenizeBash('ls && git commit');
|
|
expect(r.segments.map((s) => s.tokens[0])).toEqual(['ls', 'git']);
|
|
expect(r.segments[0].op).toBe('&&');
|
|
expect(r.segments[1].op).toBe(null);
|
|
});
|
|
|
|
it('splits on pipe', () => {
|
|
const r = tokenizeBash('cat a | grep x');
|
|
expect(r.segments).toHaveLength(2);
|
|
expect(r.segments[0].op).toBe('|');
|
|
});
|
|
});
|
|
|
|
describe('tokenizeBash — sub-shell detection', () => {
|
|
it.each([
|
|
['echo `ls`', 'backtick'],
|
|
['echo $(ls)', 'cmd-subst'],
|
|
['diff <(ls a) <(ls b)', 'process-subst-in'],
|
|
['cat <<EOF\nx\nEOF', 'heredoc'],
|
|
])('flags %s', (cmd, kind) => {
|
|
const r = tokenizeBash(cmd);
|
|
expect(r.ok).toBe(true);
|
|
expect(r.hasSubshell).toBe(true);
|
|
expect(r.subshellKinds).toContain(kind);
|
|
});
|
|
|
|
it('does not flag plain command', () => {
|
|
expect(tokenizeBash('ls -la').hasSubshell).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('tokenizeBash — parse errors', () => {
|
|
it('returns ok:false on unbalanced quotes', () => {
|
|
expect(tokenizeBash('echo "unterminated').ok).toBe(false);
|
|
expect(tokenizeBash("echo 'open").ok).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('isMutatingSegment', () => {
|
|
it.each([
|
|
[['rm', '-rf', 'x'], true],
|
|
[['git', 'commit', '-m', 'x'], true],
|
|
[['git', 'status'], false],
|
|
[['composer', 'install'], true],
|
|
[['composer', 'show'], false],
|
|
[['cat', 'x', '>', 'y'], true],
|
|
[['grep', 'x', 'file'], false],
|
|
])('%j → %s', (tokens, expected) => {
|
|
expect(isMutatingSegment(tokens)).toBe(expected);
|
|
});
|
|
});
|