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
153 lines
4.8 KiB
TypeScript
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");
|
|
});
|
|
});
|
|
});
|