Files
codetyper.cli/test/bash-matcher.test.ts
Carlos Gutierrez 187cc68304 Improve agent autonomy and diff view readability
Agent behavior improvements:
  - Add project context detection (tsconfig.json, pom.xml, etc.)
  - Enforce validation after changes (tsc --noEmit, mvn compile, etc.)
  - Run tests automatically - never ask "do you want me to run tests"
  - Complete full loop: create → type-check → test → confirm
  - Add command detection for direct execution (run tree, run ls)

  Diff view improvements:
  - Use darker backgrounds for added/removed lines
  - Add diffLineBgAdded, diffLineBgRemoved, diffLineText theme colors
  - Improve text visibility with white text on dark backgrounds
  - Update both React/Ink and SolidJS diff components

  Streaming fixes:
  - Fix tool call argument accumulation using OpenAI index field
  - Fix streaming content display after tool calls
  - Add consecutive error tracking to prevent token waste

  Other changes:
  - ESC to abort operations, Ctrl+C to exit
  - Fix model selection when provider changes in cascade mode
  - Add debug logging for troubleshooting
  - Move tests to root tests/ folder
  - Fix banner test GRADIENT_COLORS reference
2026-01-29 07:33:30 -05:00

153 lines
4.8 KiB
TypeScript

/**
* Unit tests for Bash Pattern Matcher
*/
import { describe, it, expect } from "bun:test";
import {
matchesBashPattern,
isBashAllowedByIndex,
findMatchingBashPatterns,
generateBashPattern,
extractCommandPrefix,
} from "@services/permissions/matchers/bash";
import { buildPatternIndex } from "@services/permissions/pattern-index";
import type { PermissionPattern } from "@/types/permissions";
describe("Bash Pattern Matcher", () => {
describe("matchesBashPattern", () => {
it("should match exact command with wildcard args", () => {
const pattern: PermissionPattern = {
tool: "Bash",
command: "git",
args: "*",
};
expect(matchesBashPattern("git", pattern)).toBe(true);
expect(matchesBashPattern("git status", pattern)).toBe(true);
expect(matchesBashPattern("git commit -m 'msg'", pattern)).toBe(true);
});
it("should not match different command", () => {
const pattern: PermissionPattern = {
tool: "Bash",
command: "git",
args: "*",
};
expect(matchesBashPattern("npm install", pattern)).toBe(false);
expect(matchesBashPattern("gitx status", pattern)).toBe(false);
});
it("should match command with specific args prefix", () => {
const pattern: PermissionPattern = {
tool: "Bash",
command: "git",
args: "status*",
};
expect(matchesBashPattern("git status", pattern)).toBe(true);
expect(matchesBashPattern("git status --short", pattern)).toBe(true);
expect(matchesBashPattern("git commit", pattern)).toBe(false);
});
it("should match exact args", () => {
const pattern: PermissionPattern = {
tool: "Bash",
command: "npm",
args: "install",
};
expect(matchesBashPattern("npm install", pattern)).toBe(true);
expect(matchesBashPattern("npm install lodash", pattern)).toBe(false);
});
it("should reject non-Bash patterns", () => {
const pattern: PermissionPattern = {
tool: "Read",
path: "*",
};
expect(matchesBashPattern("ls", pattern)).toBe(false);
});
});
describe("isBashAllowedByIndex", () => {
it("should check against index patterns", () => {
const index = buildPatternIndex(["Bash(git:*)", "Bash(npm install:*)"]);
expect(isBashAllowedByIndex("git status", index)).toBe(true);
expect(isBashAllowedByIndex("git commit", index)).toBe(true);
expect(isBashAllowedByIndex("npm install lodash", index)).toBe(true);
expect(isBashAllowedByIndex("npm run build", index)).toBe(false);
expect(isBashAllowedByIndex("rm -rf /", index)).toBe(false);
});
it("should return false for empty index", () => {
const index = buildPatternIndex([]);
expect(isBashAllowedByIndex("git status", index)).toBe(false);
});
});
describe("findMatchingBashPatterns", () => {
it("should find all matching patterns", () => {
const index = buildPatternIndex([
"Bash(git:*)",
"Bash(git status:*)",
"Bash(npm:*)",
]);
const matches = findMatchingBashPatterns("git status", index);
expect(matches.length).toBe(2);
expect(matches.map((m) => m.raw)).toContain("Bash(git:*)");
expect(matches.map((m) => m.raw)).toContain("Bash(git status:*)");
});
it("should return empty for no matches", () => {
const index = buildPatternIndex(["Bash(git:*)"]);
const matches = findMatchingBashPatterns("npm install", index);
expect(matches).toHaveLength(0);
});
});
describe("generateBashPattern", () => {
it("should generate pattern for multi-word commands", () => {
expect(generateBashPattern("git status")).toBe("Bash(git status:*)");
expect(generateBashPattern("npm install lodash")).toBe(
"Bash(npm install:*)",
);
expect(generateBashPattern("docker run nginx")).toBe(
"Bash(docker run:*)",
);
});
it("should generate pattern for single commands", () => {
expect(generateBashPattern("ls")).toBe("Bash(ls:*)");
expect(generateBashPattern("pwd")).toBe("Bash(pwd:*)");
});
it("should handle commands with many args", () => {
expect(generateBashPattern("git commit -m 'message'")).toBe(
"Bash(git commit:*)",
);
});
});
describe("extractCommandPrefix", () => {
it("should extract multi-word prefix", () => {
expect(extractCommandPrefix("git status")).toBe("git status");
expect(extractCommandPrefix("npm install lodash")).toBe("npm install");
expect(extractCommandPrefix("bun test --watch")).toBe("bun test");
});
it("should extract single word for non-recognized commands", () => {
expect(extractCommandPrefix("ls -la")).toBe("ls");
expect(extractCommandPrefix("cat file.txt")).toBe("cat");
});
});
});