@mclawnet/task 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +100 -0
- package/dist/__tests__/compute-lead-briefing.test.d.ts +2 -0
- package/dist/__tests__/compute-lead-briefing.test.d.ts.map +1 -0
- package/dist/__tests__/compute-lead-briefing.test.js +81 -0
- package/dist/__tests__/compute-lead-briefing.test.js.map +1 -0
- package/dist/__tests__/compute-member-briefing.test.d.ts +2 -0
- package/dist/__tests__/compute-member-briefing.test.d.ts.map +1 -0
- package/dist/__tests__/compute-member-briefing.test.js +36 -0
- package/dist/__tests__/compute-member-briefing.test.js.map +1 -0
- package/dist/__tests__/compute-task-briefing.test.d.ts +2 -0
- package/dist/__tests__/compute-task-briefing.test.d.ts.map +1 -0
- package/dist/__tests__/compute-task-briefing.test.js +72 -0
- package/dist/__tests__/compute-task-briefing.test.js.map +1 -0
- package/dist/__tests__/dag.test.d.ts +2 -0
- package/dist/__tests__/dag.test.d.ts.map +1 -0
- package/dist/__tests__/dag.test.js +81 -0
- package/dist/__tests__/dag.test.js.map +1 -0
- package/dist/__tests__/format-lead-briefing.test.d.ts +2 -0
- package/dist/__tests__/format-lead-briefing.test.d.ts.map +1 -0
- package/dist/__tests__/format-lead-briefing.test.js +70 -0
- package/dist/__tests__/format-lead-briefing.test.js.map +1 -0
- package/dist/__tests__/format-member-briefing.test.d.ts +2 -0
- package/dist/__tests__/format-member-briefing.test.d.ts.map +1 -0
- package/dist/__tests__/format-member-briefing.test.js +33 -0
- package/dist/__tests__/format-member-briefing.test.js.map +1 -0
- package/dist/__tests__/format-task-briefing.test.d.ts +2 -0
- package/dist/__tests__/format-task-briefing.test.d.ts.map +1 -0
- package/dist/__tests__/format-task-briefing.test.js +74 -0
- package/dist/__tests__/format-task-briefing.test.js.map +1 -0
- package/dist/__tests__/locking.test.d.ts +2 -0
- package/dist/__tests__/locking.test.d.ts.map +1 -0
- package/dist/__tests__/locking.test.js +38 -0
- package/dist/__tests__/locking.test.js.map +1 -0
- package/dist/__tests__/meta.test.d.ts +2 -0
- package/dist/__tests__/meta.test.d.ts.map +1 -0
- package/dist/__tests__/meta.test.js +48 -0
- package/dist/__tests__/meta.test.js.map +1 -0
- package/dist/__tests__/path.test.d.ts +2 -0
- package/dist/__tests__/path.test.d.ts.map +1 -0
- package/dist/__tests__/path.test.js +45 -0
- package/dist/__tests__/path.test.js.map +1 -0
- package/dist/__tests__/schema.test.d.ts +2 -0
- package/dist/__tests__/schema.test.d.ts.map +1 -0
- package/dist/__tests__/schema.test.js +49 -0
- package/dist/__tests__/schema.test.js.map +1 -0
- package/dist/__tests__/store-create-deps.test.d.ts +2 -0
- package/dist/__tests__/store-create-deps.test.d.ts.map +1 -0
- package/dist/__tests__/store-create-deps.test.js +23 -0
- package/dist/__tests__/store-create-deps.test.js.map +1 -0
- package/dist/__tests__/store-link.test.d.ts +2 -0
- package/dist/__tests__/store-link.test.d.ts.map +1 -0
- package/dist/__tests__/store-link.test.js +64 -0
- package/dist/__tests__/store-link.test.js.map +1 -0
- package/dist/__tests__/store-rebuild.test.d.ts +2 -0
- package/dist/__tests__/store-rebuild.test.d.ts.map +1 -0
- package/dist/__tests__/store-rebuild.test.js +47 -0
- package/dist/__tests__/store-rebuild.test.js.map +1 -0
- package/dist/__tests__/store.test.d.ts +2 -0
- package/dist/__tests__/store.test.d.ts.map +1 -0
- package/dist/__tests__/store.test.js +70 -0
- package/dist/__tests__/store.test.js.map +1 -0
- package/dist/briefings/compute-lead.d.ts +17 -0
- package/dist/briefings/compute-lead.d.ts.map +1 -0
- package/dist/briefings/compute-lead.js +52 -0
- package/dist/briefings/compute-lead.js.map +1 -0
- package/dist/briefings/compute-member.d.ts +12 -0
- package/dist/briefings/compute-member.d.ts.map +1 -0
- package/dist/briefings/compute-member.js +30 -0
- package/dist/briefings/compute-member.js.map +1 -0
- package/dist/briefings/compute-task.d.ts +17 -0
- package/dist/briefings/compute-task.d.ts.map +1 -0
- package/dist/briefings/compute-task.js +60 -0
- package/dist/briefings/compute-task.js.map +1 -0
- package/dist/briefings/format-lead.d.ts +19 -0
- package/dist/briefings/format-lead.d.ts.map +1 -0
- package/dist/briefings/format-lead.js +47 -0
- package/dist/briefings/format-lead.js.map +1 -0
- package/dist/briefings/format-member.d.ts +18 -0
- package/dist/briefings/format-member.d.ts.map +1 -0
- package/dist/briefings/format-member.js +24 -0
- package/dist/briefings/format-member.js.map +1 -0
- package/dist/briefings/format-task.d.ts +19 -0
- package/dist/briefings/format-task.d.ts.map +1 -0
- package/dist/briefings/format-task.js +48 -0
- package/dist/briefings/format-task.js.map +1 -0
- package/dist/dag.d.ts +10 -0
- package/dist/dag.d.ts.map +1 -0
- package/dist/dag.js +33 -0
- package/dist/dag.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/locking.d.ts +8 -0
- package/dist/locking.d.ts.map +1 -0
- package/dist/locking.js +19 -0
- package/dist/locking.js.map +1 -0
- package/dist/meta.d.ts +21 -0
- package/dist/meta.d.ts.map +1 -0
- package/dist/meta.js +37 -0
- package/dist/meta.js.map +1 -0
- package/dist/path.d.ts +7 -0
- package/dist/path.d.ts.map +1 -0
- package/dist/path.js +34 -0
- package/dist/path.js.map +1 -0
- package/dist/schema.d.ts +42 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +41 -0
- package/dist/schema.js.map +1 -0
- package/dist/store.d.ts +47 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +248 -0
- package/dist/store.js.map +1 -0
- package/package.json +33 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { describe, expect, it } from "vitest";
|
|
2
|
+
import { formatTaskBriefing } from "../briefings/format-task.js";
|
|
3
|
+
import { createEmptyTask } from "../schema.js";
|
|
4
|
+
function mkTask(id, subject, status = "pending") {
|
|
5
|
+
const t = createEmptyTask({ id, teamName: "team-a", subject });
|
|
6
|
+
t.status = status;
|
|
7
|
+
return t;
|
|
8
|
+
}
|
|
9
|
+
function emptyBody() {
|
|
10
|
+
return {
|
|
11
|
+
actionable: [],
|
|
12
|
+
awareness: [],
|
|
13
|
+
counters: { actionable: 0, awareness: 0, blocked: 0, waitingOnUser: 0 },
|
|
14
|
+
warnings: [],
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
describe("formatTaskBriefing", () => {
|
|
18
|
+
it("renders the empty placeholder when body is fully empty", () => {
|
|
19
|
+
expect(formatTaskBriefing(emptyBody())).toBe("### 你的任务简报: 暂无任务");
|
|
20
|
+
});
|
|
21
|
+
it("renders one actionable task with instanceId in header", () => {
|
|
22
|
+
const body = {
|
|
23
|
+
...emptyBody(),
|
|
24
|
+
actionable: [mkTask("T-123", "do the thing", "in_progress")],
|
|
25
|
+
counters: { actionable: 1, awareness: 0, blocked: 0, waitingOnUser: 0 },
|
|
26
|
+
};
|
|
27
|
+
const out = formatTaskBriefing(body, { instanceId: "worker-0" });
|
|
28
|
+
expect(out).toContain("### 你的任务简报 (worker-0)");
|
|
29
|
+
expect(out).toContain("**可执行 (1)**");
|
|
30
|
+
expect(out).toContain("T-123");
|
|
31
|
+
expect(out).toContain("[in_progress]");
|
|
32
|
+
expect(out).toContain("do the thing");
|
|
33
|
+
expect(out).toContain("**计数**: actionable=1 awareness=0 blocked=0 waitingOnUser=0");
|
|
34
|
+
});
|
|
35
|
+
it("truncates when actionable exceeds maxItems and preserves original count", () => {
|
|
36
|
+
const tasks = [];
|
|
37
|
+
for (let i = 0; i < 12; i++)
|
|
38
|
+
tasks.push(mkTask(`T-${i}`, `s${i}`));
|
|
39
|
+
const body = {
|
|
40
|
+
...emptyBody(),
|
|
41
|
+
actionable: tasks,
|
|
42
|
+
counters: { actionable: 12, awareness: 0, blocked: 0, waitingOnUser: 0 },
|
|
43
|
+
};
|
|
44
|
+
const out = formatTaskBriefing(body, { maxItems: 10 });
|
|
45
|
+
const bulletLines = out.split("\n").filter((l) => /^- T-\d+ \[/.test(l));
|
|
46
|
+
expect(bulletLines).toHaveLength(10);
|
|
47
|
+
expect(out).toContain("- (还有 2 项被截断)");
|
|
48
|
+
expect(out).toContain("**计数**: actionable=12");
|
|
49
|
+
});
|
|
50
|
+
it("renders warnings section when warnings present", () => {
|
|
51
|
+
const body = {
|
|
52
|
+
...emptyBody(),
|
|
53
|
+
actionable: [mkTask("T-1", "subj")],
|
|
54
|
+
counters: { actionable: 1, awareness: 0, blocked: 0, waitingOnUser: 0 },
|
|
55
|
+
warnings: ["broken-deps: task T-300 has cancelled/missing dependencies [T-299]"],
|
|
56
|
+
};
|
|
57
|
+
const out = formatTaskBriefing(body);
|
|
58
|
+
expect(out).toContain("**警告**:");
|
|
59
|
+
expect(out).toContain("- broken-deps: task T-300 has cancelled/missing dependencies [T-299]");
|
|
60
|
+
});
|
|
61
|
+
it("renders awareness bucket independently when actionable is empty", () => {
|
|
62
|
+
const body = {
|
|
63
|
+
...emptyBody(),
|
|
64
|
+
awareness: [mkTask("T-200", "wait subject")],
|
|
65
|
+
counters: { actionable: 0, awareness: 1, blocked: 0, waitingOnUser: 0 },
|
|
66
|
+
};
|
|
67
|
+
const out = formatTaskBriefing(body);
|
|
68
|
+
expect(out).not.toContain("**可执行");
|
|
69
|
+
expect(out).toContain("**需关注 (1)**");
|
|
70
|
+
expect(out).toContain("- T-200 wait subject");
|
|
71
|
+
expect(out).toContain("**计数**: actionable=0 awareness=1 blocked=0 waitingOnUser=0");
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
//# sourceMappingURL=format-task-briefing.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"format-task-briefing.test.js","sourceRoot":"","sources":["../../src/__tests__/format-task-briefing.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locking.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/locking.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { withLock } from "../locking.js";
|
|
6
|
+
describe("withLock", () => {
|
|
7
|
+
it("serializes concurrent operations on same key", async () => {
|
|
8
|
+
const dir = mkdtempSync(join(tmpdir(), "task-lock-"));
|
|
9
|
+
mkdirSync(dir, { recursive: true });
|
|
10
|
+
writeFileSync(join(dir, "target"), "");
|
|
11
|
+
const order = [];
|
|
12
|
+
const op = (label, ms) => withLock(join(dir, "target"), async () => {
|
|
13
|
+
order.push(`${label}-start`);
|
|
14
|
+
await new Promise(r => setTimeout(r, ms));
|
|
15
|
+
order.push(`${label}-end`);
|
|
16
|
+
});
|
|
17
|
+
await Promise.all([op("A", 30), op("B", 10)]);
|
|
18
|
+
// 不假设谁先拿锁(proper-lockfile 不保证 FIFO),只断言互斥:
|
|
19
|
+
// 谁的 *-start 先出现,谁的 *-end 必须紧随其后
|
|
20
|
+
expect(order.length).toBe(4);
|
|
21
|
+
const winner = order[0][0]; // 'A' 或 'B'
|
|
22
|
+
const loser = winner === "A" ? "B" : "A";
|
|
23
|
+
expect(order).toEqual([
|
|
24
|
+
`${winner}-start`, `${winner}-end`,
|
|
25
|
+
`${loser}-start`, `${loser}-end`,
|
|
26
|
+
]);
|
|
27
|
+
});
|
|
28
|
+
it("propagates errors and releases lock", async () => {
|
|
29
|
+
const dir = mkdtempSync(join(tmpdir(), "task-lock-"));
|
|
30
|
+
writeFileSync(join(dir, "target"), "");
|
|
31
|
+
await expect(withLock(join(dir, "target"), async () => { throw new Error("boom"); })).rejects.toThrow("boom");
|
|
32
|
+
// 后续仍可拿锁
|
|
33
|
+
let ok = false;
|
|
34
|
+
await withLock(join(dir, "target"), async () => { ok = true; });
|
|
35
|
+
expect(ok).toBe(true);
|
|
36
|
+
});
|
|
37
|
+
});
|
|
38
|
+
//# sourceMappingURL=locking.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"locking.test.js","sourceRoot":"","sources":["../../src/__tests__/locking.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/meta.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { mkdtempSync, readFileSync, existsSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { TaskStore } from "../store.js";
|
|
6
|
+
import { metaFile, projectRoot } from "../path.js";
|
|
7
|
+
describe("project meta.json bootstrap", () => {
|
|
8
|
+
const workDir = "/Users/test/proj";
|
|
9
|
+
it("TaskStore constructor writes meta.json on first use", () => {
|
|
10
|
+
const home = mkdtempSync(join(tmpdir(), "task-meta-"));
|
|
11
|
+
expect(existsSync(metaFile(workDir, home))).toBe(false);
|
|
12
|
+
new TaskStore({ workDir, home });
|
|
13
|
+
const path = metaFile(workDir, home);
|
|
14
|
+
expect(existsSync(path)).toBe(true);
|
|
15
|
+
const meta = JSON.parse(readFileSync(path, "utf-8"));
|
|
16
|
+
expect(meta).toMatchObject({ version: 1, workDir });
|
|
17
|
+
expect(typeof meta.createdAt).toBe("string");
|
|
18
|
+
// ISO-8601 sanity
|
|
19
|
+
expect(Number.isFinite(Date.parse(meta.createdAt))).toBe(true);
|
|
20
|
+
});
|
|
21
|
+
it("constructor is idempotent — does not overwrite existing createdAt", () => {
|
|
22
|
+
const home = mkdtempSync(join(tmpdir(), "task-meta-"));
|
|
23
|
+
new TaskStore({ workDir, home });
|
|
24
|
+
const first = JSON.parse(readFileSync(metaFile(workDir, home), "utf-8"));
|
|
25
|
+
// recreate
|
|
26
|
+
new TaskStore({ workDir, home });
|
|
27
|
+
const second = JSON.parse(readFileSync(metaFile(workDir, home), "utf-8"));
|
|
28
|
+
expect(second.createdAt).toBe(first.createdAt);
|
|
29
|
+
});
|
|
30
|
+
it("preserves unrelated fields written by other code", () => {
|
|
31
|
+
const home = mkdtempSync(join(tmpdir(), "task-meta-"));
|
|
32
|
+
// Pre-existing file from a different writer (e.g. demo fixture)
|
|
33
|
+
const root = projectRoot(workDir, home);
|
|
34
|
+
const { mkdirSync } = require("node:fs");
|
|
35
|
+
mkdirSync(root, { recursive: true });
|
|
36
|
+
writeFileSync(metaFile(workDir, home), JSON.stringify({
|
|
37
|
+
version: 1,
|
|
38
|
+
workDir,
|
|
39
|
+
createdAt: "2020-01-01T00:00:00.000Z",
|
|
40
|
+
swarms: { "old-swarm": { teamName: "x" } },
|
|
41
|
+
}));
|
|
42
|
+
new TaskStore({ workDir, home });
|
|
43
|
+
const meta = JSON.parse(readFileSync(metaFile(workDir, home), "utf-8"));
|
|
44
|
+
expect(meta.createdAt).toBe("2020-01-01T00:00:00.000Z");
|
|
45
|
+
expect(meta.swarms).toEqual({ "old-swarm": { teamName: "x" } });
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
//# sourceMappingURL=meta.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"meta.test.js","sourceRoot":"","sources":["../../src/__tests__/meta.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/path.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { encodeCwd, projectRoot, tasksDir, taskFile, indexFile } from "../path.js";
|
|
3
|
+
describe("encodeCwd", () => {
|
|
4
|
+
it("replaces / with - and strips leading dash", () => {
|
|
5
|
+
expect(encodeCwd("/Users/lcf/code/foo")).toBe("-Users-lcf-code-foo");
|
|
6
|
+
});
|
|
7
|
+
it("handles trailing slash", () => {
|
|
8
|
+
expect(encodeCwd("/Users/lcf/code/foo/")).toBe("-Users-lcf-code-foo");
|
|
9
|
+
});
|
|
10
|
+
it("rejects non-absolute paths", () => {
|
|
11
|
+
expect(() => encodeCwd("relative/path")).toThrow(/absolute/);
|
|
12
|
+
});
|
|
13
|
+
it("encodes Windows drive-letter paths", () => {
|
|
14
|
+
expect(encodeCwd("C:\\Users\\lcf\\code\\foo")).toBe("C--Users-lcf-code-foo");
|
|
15
|
+
});
|
|
16
|
+
it("encodes Windows paths with forward slashes", () => {
|
|
17
|
+
expect(encodeCwd("C:/Users/lcf/code/foo")).toBe("C--Users-lcf-code-foo");
|
|
18
|
+
});
|
|
19
|
+
it("encodes UNC paths", () => {
|
|
20
|
+
expect(encodeCwd("\\\\server\\share\\path")).toBe("--server-share-path");
|
|
21
|
+
});
|
|
22
|
+
it("handles trailing backslash on Windows paths", () => {
|
|
23
|
+
expect(encodeCwd("C:\\Users\\lcf\\")).toBe("C--Users-lcf");
|
|
24
|
+
});
|
|
25
|
+
it("still rejects truly relative Windows paths", () => {
|
|
26
|
+
expect(() => encodeCwd("Users\\lcf")).toThrow(/absolute/);
|
|
27
|
+
});
|
|
28
|
+
});
|
|
29
|
+
describe("path helpers", () => {
|
|
30
|
+
const cwd = "/Users/lcf/code/foo";
|
|
31
|
+
it("projectRoot composes ~/.clawnet/projects/<encoded>", () => {
|
|
32
|
+
const root = projectRoot(cwd, "/home/user");
|
|
33
|
+
expect(root).toBe("/home/user/.clawnet/projects/-Users-lcf-code-foo");
|
|
34
|
+
});
|
|
35
|
+
it("tasksDir = projectRoot/tasks", () => {
|
|
36
|
+
expect(tasksDir(cwd, "/home/user")).toMatch(/projects\/-Users-lcf-code-foo\/tasks$/);
|
|
37
|
+
});
|
|
38
|
+
it("taskFile uses <id>.json", () => {
|
|
39
|
+
expect(taskFile(cwd, "abc-123", "/home/user")).toMatch(/tasks\/abc-123\.json$/);
|
|
40
|
+
});
|
|
41
|
+
it("indexFile is tasks/index.json", () => {
|
|
42
|
+
expect(indexFile(cwd, "/home/user")).toMatch(/tasks\/index\.json$/);
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
//# sourceMappingURL=path.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"path.test.js","sourceRoot":"","sources":["../../src/__tests__/path.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAEnF,QAAQ,CAAC,WAAW,EAAE,GAAG,EAAE;IACzB,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,CAAC,SAAS,CAAC,qBAAqB,CAAC,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,wBAAwB,EAAE,GAAG,EAAE;QAChC,MAAM,CAAC,SAAS,CAAC,sBAAsB,CAAC,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,4BAA4B,EAAE,GAAG,EAAE;QACpC,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,eAAe,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC/D,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC5C,MAAM,CAAC,SAAS,CAAC,2BAA2B,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAC/E,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,SAAS,CAAC,uBAAuB,CAAC,CAAC,CAAC,IAAI,CAAC,uBAAuB,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,mBAAmB,EAAE,GAAG,EAAE;QAC3B,MAAM,CAAC,SAAS,CAAC,yBAAyB,CAAC,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAC3E,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACrD,MAAM,CAAC,SAAS,CAAC,kBAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,MAAM,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,MAAM,GAAG,GAAG,qBAAqB,CAAC;IAClC,EAAE,CAAC,oDAAoD,EAAE,GAAG,EAAE;QAC5D,MAAM,IAAI,GAAG,WAAW,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;QAC5C,MAAM,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,kDAAkD,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,uCAAuC,CAAC,CAAC;IACvF,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACjC,MAAM,CAAC,QAAQ,CAAC,GAAG,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,uBAAuB,CAAC,CAAC;IAClF,CAAC,CAAC,CAAC;IACH,EAAE,CAAC,+BAA+B,EAAE,GAAG,EAAE;QACvC,MAAM,CAAC,SAAS,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACtE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/schema.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { TaskStatusSchema, validateTask, createEmptyTask } from "../schema.js";
|
|
3
|
+
describe("TaskStatusSchema", () => {
|
|
4
|
+
it("accepts valid statuses", () => {
|
|
5
|
+
for (const s of ["pending", "in_progress", "completed", "cancelled"]) {
|
|
6
|
+
expect(TaskStatusSchema.has(s)).toBe(true);
|
|
7
|
+
}
|
|
8
|
+
});
|
|
9
|
+
it("rejects invalid", () => {
|
|
10
|
+
expect(TaskStatusSchema.has("blocked")).toBe(false);
|
|
11
|
+
});
|
|
12
|
+
});
|
|
13
|
+
describe("createEmptyTask", () => {
|
|
14
|
+
it("returns full 13-field MVP task", () => {
|
|
15
|
+
const t = createEmptyTask({
|
|
16
|
+
id: "abcdefgh-1111-2222-3333-444455556666",
|
|
17
|
+
teamName: "dev-team",
|
|
18
|
+
subject: "do thing",
|
|
19
|
+
});
|
|
20
|
+
expect(t.id).toBe("abcdefgh-1111-2222-3333-444455556666");
|
|
21
|
+
expect(t.displayId).toBe("abcdefgh");
|
|
22
|
+
expect(t.status).toBe("pending");
|
|
23
|
+
expect(t.teamName).toBe("dev-team");
|
|
24
|
+
expect(t.swarmId).toBeNull();
|
|
25
|
+
expect(t.owner).toBeNull();
|
|
26
|
+
expect(t.subject).toBe("do thing");
|
|
27
|
+
expect(t.description).toBe("");
|
|
28
|
+
expect(t.blockedBy).toEqual([]);
|
|
29
|
+
expect(t.blocks).toEqual([]);
|
|
30
|
+
expect(t.historyEvents).toEqual([]);
|
|
31
|
+
expect(t.comments).toEqual([]);
|
|
32
|
+
expect(typeof t.createdAt).toBe("string");
|
|
33
|
+
expect(typeof t.updatedAt).toBe("string");
|
|
34
|
+
});
|
|
35
|
+
});
|
|
36
|
+
describe("validateTask", () => {
|
|
37
|
+
it("passes valid task", () => {
|
|
38
|
+
const t = createEmptyTask({ id: "x".repeat(36), teamName: "t", subject: "s" });
|
|
39
|
+
expect(() => validateTask(t)).not.toThrow();
|
|
40
|
+
});
|
|
41
|
+
it("rejects missing id", () => {
|
|
42
|
+
expect(() => validateTask({ ...createEmptyTask({ id: "x".repeat(36), teamName: "t", subject: "s" }), id: "" })).toThrow(/id/);
|
|
43
|
+
});
|
|
44
|
+
it("rejects bad status", () => {
|
|
45
|
+
const t = createEmptyTask({ id: "x".repeat(36), teamName: "t", subject: "s" });
|
|
46
|
+
expect(() => validateTask({ ...t, status: "blocked" })).toThrow(/status/);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
//# sourceMappingURL=schema.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema.test.js","sourceRoot":"","sources":["../../src/__tests__/schema.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-create-deps.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/store-create-deps.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { mkdtempSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { TaskStore } from "../store.js";
|
|
6
|
+
function makeStore() {
|
|
7
|
+
const home = mkdtempSync(join(tmpdir(), "task-create-deps-"));
|
|
8
|
+
return new TaskStore({ workDir: "/Users/test/proj", home });
|
|
9
|
+
}
|
|
10
|
+
describe("TaskStore.create with blockedBy", () => {
|
|
11
|
+
let store;
|
|
12
|
+
beforeEach(() => { store = makeStore(); });
|
|
13
|
+
it("creates task and writes reverse edges in deps", async () => {
|
|
14
|
+
const a = await store.create({ teamName: "t", subject: "a" });
|
|
15
|
+
const b = await store.create({ teamName: "t", subject: "b", blockedBy: [a.id] });
|
|
16
|
+
expect(b.blockedBy).toEqual([a.id]);
|
|
17
|
+
expect(store.get(a.id).blocks).toEqual([b.id]);
|
|
18
|
+
});
|
|
19
|
+
it("rejects creation referencing missing dep", async () => {
|
|
20
|
+
await expect(store.create({ teamName: "t", subject: "x", blockedBy: ["missing"] })).rejects.toThrow(/dependency.*not found/i);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
//# sourceMappingURL=store-create-deps.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-create-deps.test.js","sourceRoot":"","sources":["../../src/__tests__/store-create-deps.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAExC,SAAS,SAAS;IAChB,MAAM,IAAI,GAAG,WAAW,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,mBAAmB,CAAC,CAAC,CAAC;IAC9D,OAAO,IAAI,SAAS,CAAC,EAAE,OAAO,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAC;AAC9D,CAAC;AAED,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC/C,IAAI,KAAgB,CAAC;IACrB,UAAU,CAAC,GAAG,EAAE,GAAG,KAAK,GAAG,SAAS,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC;IAE3C,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;QAC7D,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,CAAC,CAAC;QAC9D,MAAM,CAAC,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACjF,MAAM,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;QACpC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IACjD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QACxD,MAAM,MAAM,CACV,KAAK,CAAC,MAAM,CAAC,EAAE,QAAQ,EAAE,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,CAAC,SAAS,CAAC,EAAE,CAAC,CACtE,CAAC,OAAO,CAAC,OAAO,CAAC,wBAAwB,CAAC,CAAC;IAC9C,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-link.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/store-link.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { mkdtempSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { TaskStore } from "../store.js";
|
|
6
|
+
import { taskFile } from "../path.js";
|
|
7
|
+
function makeStore() {
|
|
8
|
+
const home = mkdtempSync(join(tmpdir(), "task-link-"));
|
|
9
|
+
return { store: new TaskStore({ workDir: "/Users/test/proj", home }), home };
|
|
10
|
+
}
|
|
11
|
+
describe("TaskStore link/unlink", () => {
|
|
12
|
+
let store;
|
|
13
|
+
let home;
|
|
14
|
+
beforeEach(() => { ({ store, home } = makeStore()); });
|
|
15
|
+
it("link writes both edges atomically", async () => {
|
|
16
|
+
const a = await store.create({ teamName: "t", subject: "a" });
|
|
17
|
+
const b = await store.create({ teamName: "t", subject: "b" });
|
|
18
|
+
await store.link(b.id, a.id);
|
|
19
|
+
expect(store.get(b.id).blockedBy).toEqual([a.id]);
|
|
20
|
+
expect(store.get(a.id).blocks).toEqual([b.id]);
|
|
21
|
+
});
|
|
22
|
+
it("link rejects cycle", async () => {
|
|
23
|
+
const a = await store.create({ teamName: "t", subject: "a" });
|
|
24
|
+
const b = await store.create({ teamName: "t", subject: "b" });
|
|
25
|
+
await store.link(b.id, a.id);
|
|
26
|
+
await expect(store.link(a.id, b.id)).rejects.toThrow(/cycle/i);
|
|
27
|
+
});
|
|
28
|
+
it("link is idempotent", async () => {
|
|
29
|
+
const a = await store.create({ teamName: "t", subject: "a" });
|
|
30
|
+
const b = await store.create({ teamName: "t", subject: "b" });
|
|
31
|
+
await store.link(b.id, a.id);
|
|
32
|
+
await store.link(b.id, a.id);
|
|
33
|
+
expect(store.get(b.id).blockedBy).toEqual([a.id]);
|
|
34
|
+
expect(store.get(a.id).blocks).toEqual([b.id]);
|
|
35
|
+
});
|
|
36
|
+
it("unlink removes both edges", async () => {
|
|
37
|
+
const a = await store.create({ teamName: "t", subject: "a" });
|
|
38
|
+
const b = await store.create({ teamName: "t", subject: "b" });
|
|
39
|
+
await store.link(b.id, a.id);
|
|
40
|
+
await store.unlink(b.id, a.id);
|
|
41
|
+
expect(store.get(b.id).blockedBy).toEqual([]);
|
|
42
|
+
expect(store.get(a.id).blocks).toEqual([]);
|
|
43
|
+
});
|
|
44
|
+
it("unlink no-op when not linked", async () => {
|
|
45
|
+
const a = await store.create({ teamName: "t", subject: "a" });
|
|
46
|
+
const b = await store.create({ teamName: "t", subject: "b" });
|
|
47
|
+
await store.unlink(b.id, a.id);
|
|
48
|
+
expect(store.get(b.id).blockedBy).toEqual([]);
|
|
49
|
+
});
|
|
50
|
+
it("link repairs half-edge from previous crash", async () => {
|
|
51
|
+
const a = await store.create({ teamName: "t", subject: "a" });
|
|
52
|
+
const b = await store.create({ teamName: "t", subject: "b" });
|
|
53
|
+
// 模拟"上次只写了 from.blockedBy 没写 to.blocks"的脏状态——直接改 task 文件绕过 guard
|
|
54
|
+
const bPath = taskFile("/Users/test/proj", b.id, home);
|
|
55
|
+
const bTask = JSON.parse(readFileSync(bPath, "utf-8"));
|
|
56
|
+
bTask.blockedBy = [a.id];
|
|
57
|
+
writeFileSync(bPath, JSON.stringify(bTask, null, 2));
|
|
58
|
+
expect(store.get(a.id).blocks).toEqual([]); // 半边
|
|
59
|
+
await store.link(b.id, a.id);
|
|
60
|
+
expect(store.get(b.id).blockedBy).toEqual([a.id]); // 不重复
|
|
61
|
+
expect(store.get(a.id).blocks).toEqual([b.id]); // 修复
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
//# sourceMappingURL=store-link.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-link.test.js","sourceRoot":"","sources":["../../src/__tests__/store-link.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-rebuild.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/store-rebuild.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { describe, it, expect } from "vitest";
|
|
2
|
+
import { mkdtempSync, writeFileSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { TaskStore } from "../store.js";
|
|
6
|
+
import { tasksDir, indexFile } from "../path.js";
|
|
7
|
+
function makeStore() {
|
|
8
|
+
const home = mkdtempSync(join(tmpdir(), "task-rebuild-"));
|
|
9
|
+
return { store: new TaskStore({ workDir: "/Users/test/proj", home }), home };
|
|
10
|
+
}
|
|
11
|
+
describe("TaskStore.rebuildIndex", () => {
|
|
12
|
+
it("recovers index from task files when index is missing", async () => {
|
|
13
|
+
const { store, home } = makeStore();
|
|
14
|
+
const t = await store.create({ teamName: "t", subject: "a" });
|
|
15
|
+
rmSync(indexFile("/Users/test/proj", home));
|
|
16
|
+
await store.rebuildIndex();
|
|
17
|
+
const entry = store.list().find(e => e.id === t.id);
|
|
18
|
+
expect(entry.subject).toBe("a");
|
|
19
|
+
expect(entry.status).toBe("pending");
|
|
20
|
+
});
|
|
21
|
+
it("recovers index when index is stale (missing entry)", async () => {
|
|
22
|
+
const { store, home } = makeStore();
|
|
23
|
+
const a = await store.create({ teamName: "t", subject: "a" });
|
|
24
|
+
const dir = tasksDir("/Users/test/proj", home);
|
|
25
|
+
writeFileSync(join(dir, "ghost.json"), JSON.stringify({
|
|
26
|
+
...a,
|
|
27
|
+
id: "ghost",
|
|
28
|
+
displayId: "ghost",
|
|
29
|
+
subject: "ghost-task",
|
|
30
|
+
}, null, 2));
|
|
31
|
+
await store.rebuildIndex();
|
|
32
|
+
const entries = store.list();
|
|
33
|
+
expect(entries.find(e => e.id === "ghost")).toBeTruthy();
|
|
34
|
+
expect(entries.find(e => e.id === a.id)).toBeTruthy();
|
|
35
|
+
});
|
|
36
|
+
it("removes stale entries pointing at deleted task files", async () => {
|
|
37
|
+
const { store, home } = makeStore();
|
|
38
|
+
const a = await store.create({ teamName: "t", subject: "a" });
|
|
39
|
+
const b = await store.create({ teamName: "t", subject: "b" });
|
|
40
|
+
rmSync(join(tasksDir("/Users/test/proj", home), `${b.id}.json`));
|
|
41
|
+
await store.rebuildIndex();
|
|
42
|
+
const entries = store.list();
|
|
43
|
+
expect(entries.find(e => e.id === a.id)).toBeTruthy();
|
|
44
|
+
expect(entries.find(e => e.id === b.id)).toBeUndefined();
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=store-rebuild.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store-rebuild.test.js","sourceRoot":"","sources":["../../src/__tests__/store-rebuild.test.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/store.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach } from "vitest";
|
|
2
|
+
import { mkdtempSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { TaskStore } from "../store.js";
|
|
6
|
+
function makeStore() {
|
|
7
|
+
const home = mkdtempSync(join(tmpdir(), "task-store-"));
|
|
8
|
+
return new TaskStore({ workDir: "/Users/test/proj", home });
|
|
9
|
+
}
|
|
10
|
+
describe("TaskStore CRUD", () => {
|
|
11
|
+
let store;
|
|
12
|
+
beforeEach(() => { store = makeStore(); });
|
|
13
|
+
it("create persists and is readable", async () => {
|
|
14
|
+
const t = await store.create({ teamName: "dev", subject: "hello" });
|
|
15
|
+
expect(t.id).toMatch(/^[0-9a-f-]{36}$/);
|
|
16
|
+
const fetched = store.get(t.id);
|
|
17
|
+
expect(fetched.subject).toBe("hello");
|
|
18
|
+
});
|
|
19
|
+
it("tryGet returns null for missing", () => {
|
|
20
|
+
expect(store.tryGet("missing-id")).toBeNull();
|
|
21
|
+
});
|
|
22
|
+
it("get throws for missing", () => {
|
|
23
|
+
expect(() => store.get("missing-id")).toThrow(/not found/);
|
|
24
|
+
});
|
|
25
|
+
it("update merges fields and bumps updatedAt", async () => {
|
|
26
|
+
const t = await store.create({ teamName: "dev", subject: "a" });
|
|
27
|
+
const before = t.updatedAt;
|
|
28
|
+
await new Promise(r => setTimeout(r, 5));
|
|
29
|
+
const updated = await store.update(t.id, { subject: "b" });
|
|
30
|
+
expect(updated.subject).toBe("b");
|
|
31
|
+
expect(updated.updatedAt).not.toBe(before);
|
|
32
|
+
});
|
|
33
|
+
it("addComment is idempotent on stable id", async () => {
|
|
34
|
+
const t = await store.create({ teamName: "dev", subject: "a" });
|
|
35
|
+
await store.addComment(t.id, { id: "c1", author: "system", type: "note", body: "x" });
|
|
36
|
+
await store.addComment(t.id, { id: "c1", author: "system", type: "note", body: "x" });
|
|
37
|
+
const fetched = store.get(t.id);
|
|
38
|
+
expect(fetched.comments.length).toBe(1);
|
|
39
|
+
});
|
|
40
|
+
it("list returns all created tasks via index", async () => {
|
|
41
|
+
await store.create({ teamName: "dev", subject: "a" });
|
|
42
|
+
await store.create({ teamName: "dev", subject: "b" });
|
|
43
|
+
const all = store.list();
|
|
44
|
+
expect(all.length).toBe(2);
|
|
45
|
+
});
|
|
46
|
+
it("index entry mirrors task header fields", async () => {
|
|
47
|
+
const t = await store.create({ teamName: "dev", subject: "hello" });
|
|
48
|
+
const entry = store.list().find(e => e.id === t.id);
|
|
49
|
+
expect(entry.subject).toBe("hello");
|
|
50
|
+
expect(entry.status).toBe("pending");
|
|
51
|
+
expect(entry.displayId).toBe(t.displayId);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
describe("TaskStore.update reverse-edge guard", () => {
|
|
55
|
+
it("rejects patch with blockedBy", async () => {
|
|
56
|
+
const home = mkdtempSync(join(tmpdir(), "task-update-guard-"));
|
|
57
|
+
const store = new TaskStore({ workDir: "/Users/test/proj", home });
|
|
58
|
+
const t = await store.create({ teamName: "t", subject: "x" });
|
|
59
|
+
await expect(store.update(t.id, { blockedBy: ["y"] }))
|
|
60
|
+
.rejects.toThrow(/use link\/unlink/i);
|
|
61
|
+
});
|
|
62
|
+
it("rejects patch with blocks", async () => {
|
|
63
|
+
const home = mkdtempSync(join(tmpdir(), "task-update-guard-"));
|
|
64
|
+
const store = new TaskStore({ workDir: "/Users/test/proj", home });
|
|
65
|
+
const t = await store.create({ teamName: "t", subject: "x" });
|
|
66
|
+
await expect(store.update(t.id, { blocks: ["y"] }))
|
|
67
|
+
.rejects.toThrow(/use link\/unlink/i);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
//# sourceMappingURL=store.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.test.js","sourceRoot":"","sources":["../../src/__tests__/store.test.ts"],"names":[],"mappings":"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"}
|