070959e133
landing-page-staging / Deploy landing page to staging (push) Has been skipped
landing-page-ci / Validate landing page (push) Failing after 4s
visual-baseline / Capture visual baselines (push) Has been cancelled
bake-plugin-previews / Bake plugin previews (push) Has been cancelled
112 lines
3.3 KiB
TypeScript
112 lines
3.3 KiB
TypeScript
import { promises as fsp } from 'node:fs';
|
|
import os from 'node:os';
|
|
import path from 'node:path';
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
|
|
|
import {
|
|
buildMemoryTree,
|
|
memoryDir,
|
|
readMemoryEntry,
|
|
updateMemoryTreeNode,
|
|
upsertMemoryEntry,
|
|
} from '../src/memory.js';
|
|
|
|
let dataDir = '';
|
|
|
|
beforeEach(async () => {
|
|
dataDir = await fsp.mkdtemp(path.join(os.tmpdir(), 'od-memory-tree-'));
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await fsp.rm(dataDir, { recursive: true, force: true });
|
|
});
|
|
|
|
describe('memory tree helpers', () => {
|
|
it('derives folder and entry nodes from the markdown memory store', async () => {
|
|
await upsertMemoryEntry(
|
|
dataDir,
|
|
{
|
|
name: 'Design agent goal',
|
|
description: 'Open Design should evolve from accepted work',
|
|
type: 'project',
|
|
body: '- Keep design-system extraction in the loop',
|
|
},
|
|
{ silent: true },
|
|
);
|
|
|
|
const tree = await buildMemoryTree(dataDir);
|
|
|
|
expect(tree).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
id: 'folder:project',
|
|
kind: 'folder',
|
|
path: '/project',
|
|
childrenCount: 1,
|
|
}),
|
|
expect.objectContaining({
|
|
id: 'project_design_agent_goal',
|
|
parentId: 'folder:project',
|
|
kind: 'entry',
|
|
path: '/project/project_design_agent_goal',
|
|
scope: 'project',
|
|
}),
|
|
]),
|
|
);
|
|
});
|
|
|
|
it('edits and moves an entry through the tree-aware patch helper', async () => {
|
|
const entry = await upsertMemoryEntry(
|
|
dataDir,
|
|
{
|
|
name: 'Reusable pattern',
|
|
description: 'Skill candidate',
|
|
type: 'reference',
|
|
body: '- Draft artifacts with provenance',
|
|
},
|
|
{ silent: true },
|
|
);
|
|
|
|
const updated = await updateMemoryTreeNode(dataDir, entry.id, {
|
|
name: 'Reusable pattern',
|
|
description: 'Skill candidate promoted from automation',
|
|
type: 'project',
|
|
body: '- Draft artifacts with provenance\n- Promote repeatable work into skills',
|
|
});
|
|
|
|
expect(updated.type).toBe('project');
|
|
expect(updated.description).toContain('promoted from automation');
|
|
expect(updated.body).toContain('Promote repeatable work into skills');
|
|
|
|
const stored = await readMemoryEntry(dataDir, entry.id);
|
|
expect(stored?.type).toBe('project');
|
|
|
|
const tree = await buildMemoryTree(dataDir);
|
|
expect(tree).toEqual(
|
|
expect.arrayContaining([
|
|
expect.objectContaining({
|
|
id: entry.id,
|
|
parentId: 'folder:project',
|
|
path: `/project/${entry.id}`,
|
|
}),
|
|
]),
|
|
);
|
|
});
|
|
|
|
it('rejects derived folder edits', async () => {
|
|
await expect(
|
|
updateMemoryTreeNode(dataDir, 'folder:project', { name: 'Project' }),
|
|
).rejects.toThrow('memory tree folders are derived');
|
|
});
|
|
|
|
it('returns an empty folder tree when the store is empty', async () => {
|
|
await fsp.rm(memoryDir(dataDir), { recursive: true, force: true });
|
|
const tree = await buildMemoryTree(dataDir);
|
|
|
|
// One folder per memory type: profile, user, feedback, project, reference,
|
|
// rule (the two new types — profile + rule — added two folders).
|
|
expect(tree.filter((node) => node.kind === 'folder')).toHaveLength(6);
|
|
expect(tree.filter((node) => node.kind === 'entry')).toHaveLength(0);
|
|
});
|
|
});
|