summaryrefslogtreecommitdiffhomepage
path: root/packages/core/tests
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-05-20 15:04:26 +0900
committerAdam Malczewski <[email protected]>2026-05-20 15:04:26 +0900
commitadc8bd185b54935e7a31aae04da3175b7989927a (patch)
tree031fa53009a4708ce0cc15c0e3dcb2f2a73cf2d9 /packages/core/tests
parent52af4ca33b1f83b8f863a6267d447944f55e729d (diff)
downloaddispatch-adc8bd185b54935e7a31aae04da3175b7989927a.tar.gz
dispatch-adc8bd185b54935e7a31aae04da3175b7989927a.zip
feat: phase 3 — config, skills, model groups, task list, and sidebar UI
- Config system: TOML-based dispatch.toml with hot-reload via chokidar - Model/key resolution: tag-based model selection, key fallback chains - Skills system: directory loader with TOML frontmatter, agent mappings - Task list tool: add/update/list/get operations with WebSocket events - API routes: GET /config, /skills, /skills/:name, /models, /models/resolve - Frontend: sidebar with model status, task list, config viewer, skills browser, permission log - Sliding sidebar animation using CSS transitions (not Svelte transitions)
Diffstat (limited to 'packages/core/tests')
-rw-r--r--packages/core/tests/config/loader.test.ts48
1 files changed, 25 insertions, 23 deletions
diff --git a/packages/core/tests/config/loader.test.ts b/packages/core/tests/config/loader.test.ts
index 9a358fe..5c02326 100644
--- a/packages/core/tests/config/loader.test.ts
+++ b/packages/core/tests/config/loader.test.ts
@@ -14,25 +14,25 @@ afterEach(() => {
rmSync(TMP, { recursive: true, force: true });
});
-function writeYaml(content: string): void {
- writeFileSync(join(TMP, "dispatch.yaml"), content, "utf-8");
+function writeToml(content: string): void {
+ writeFileSync(join(TMP, "dispatch.toml"), content, "utf-8");
}
describe("loadConfig", () => {
- it("returns empty permissions when dispatch.yaml is missing", () => {
+ it("returns empty permissions when dispatch.toml is missing", () => {
const config = loadConfig(TMP);
expect(config.permissions).toEqual({});
});
it("parses simple string permissions", () => {
- writeYaml(`permissions:\n read: allow\n edit: deny\n`);
+ writeToml(`[permissions]\nread = "allow"\nedit = "deny"\n`);
const config = loadConfig(TMP);
expect(config.permissions["read"]).toBe("allow");
expect(config.permissions["edit"]).toBe("deny");
});
it("parses nested pattern permissions", () => {
- writeYaml(`permissions:\n bash:\n "npm test": allow\n "*": ask\n`);
+ writeToml(`[permissions.bash]\n"npm test" = "allow"\n"*" = "ask"\n`);
const config = loadConfig(TMP);
const bash = config.permissions["bash"] as Record<string, string>;
expect(bash["npm test"]).toBe("allow");
@@ -40,29 +40,27 @@ describe("loadConfig", () => {
});
it("ignores comment lines", () => {
- writeYaml(`# this is a comment\npermissions:\n # another comment\n read: allow\n`);
+ writeToml(`# this is a comment\n[permissions]\n# another comment\nread = "allow"\n`);
const config = loadConfig(TMP);
expect(config.permissions["read"]).toBe("allow");
});
it("handles ~ expansion in nested keys", () => {
- const home = homedir();
- writeYaml(`permissions:\n read:\n "~/projects/*": allow\n`);
+ writeToml(`[permissions.read]\n"~/projects/*" = "allow"\n`);
const config = loadConfig(TMP);
const read = config.permissions["read"] as Record<string, string>;
- expect(read[`${home}/projects/*`]).toBe("allow");
+ expect(read["~/projects/*"]).toBe("allow");
});
it("handles $HOME expansion in nested keys", () => {
- const home = homedir();
- writeYaml(`permissions:\n read:\n "$HOME/docs/*": allow\n`);
+ writeToml(`[permissions.read]\n"$HOME/docs/*" = "allow"\n`);
const config = loadConfig(TMP);
const read = config.permissions["read"] as Record<string, string>;
- expect(read[`${home}/docs/*`]).toBe("allow");
+ expect(read["$HOME/docs/*"]).toBe("allow");
});
it("parses quoted keys", () => {
- writeYaml(`permissions:\n bash:\n "git commit *": allow\n "rm *": deny\n`);
+ writeToml(`[permissions.bash]\n"git commit *" = "allow"\n"rm *" = "deny"\n`);
const config = loadConfig(TMP);
const bash = config.permissions["bash"] as Record<string, string>;
expect(bash["git commit *"]).toBe("allow");
@@ -70,8 +68,8 @@ describe("loadConfig", () => {
});
it("handles multiple permission groups", () => {
- writeYaml(
- `permissions:\n read: allow\n edit:\n "*": ask\n "src/**": allow\n bash:\n "npm test": allow\n "*": ask\n`,
+ writeToml(
+ `[permissions]\nread = "allow"\n\n[permissions.edit]\n"*" = "ask"\n"src/**" = "allow"\n\n[permissions.bash]\n"npm test" = "allow"\n"*" = "ask"\n`,
);
const config = loadConfig(TMP);
expect(config.permissions["read"]).toBe("allow");
@@ -83,28 +81,32 @@ describe("loadConfig", () => {
expect(bash["*"]).toBe("ask");
});
- it("preserves # inside quoted string values", () => {
- writeYaml(`permissions:\n bash:\n '"file#1"': allow\n`);
+ it("preserves # inside quoted string keys", () => {
+ writeToml(`[permissions.bash]\n"file#1" = "allow"\n`);
const config = loadConfig(TMP);
const bash = config.permissions["bash"] as Record<string, string>;
- expect(bash['"file#1"']).toBe("allow");
+ expect(bash["file#1"]).toBe("allow");
});
- it("strips inline comments on nested map keys (empty value after comment)", () => {
- writeYaml(`permissions:\n bash: # scripts\n "*": allow\n`);
+ it("strips inline comments on table headers", () => {
+ writeToml(`[permissions.bash] # scripts\n"*" = "allow"\n`);
const config = loadConfig(TMP);
const bash = config.permissions["bash"] as Record<string, string>;
expect(bash["*"]).toBe("allow");
});
it("expands ~ with platform path separator", () => {
- const home = homedir();
// Simulate a path using the OS separator
const pattern = `~${sep}projects${sep}*`;
- writeYaml(`permissions:\n read:\n "${pattern}": allow\n`);
+ writeToml(`[permissions.read]\n"${pattern}" = "allow"\n`);
const config = loadConfig(TMP);
const read = config.permissions["read"] as Record<string, string>;
- expect(read[`${home}${sep}projects${sep}*`]).toBe("allow");
+ expect(read[pattern]).toBe("allow");
+ });
+
+ it("throws on TOML parse errors", () => {
+ writeToml("this is not valid TOML [[[");
+ expect(() => loadConfig(TMP)).toThrow();
});
});