@synth-coder/memhub 0.2.3 → 0.2.5
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/.github/workflows/ci.yml +48 -12
- package/.github/workflows/release.yml +70 -0
- package/AGENTS.md +71 -73
- package/README.md +284 -195
- package/README.zh-CN.md +90 -30
- package/dist/src/cli/agents/claude-code.d.ts +5 -0
- package/dist/src/cli/agents/claude-code.d.ts.map +1 -0
- package/dist/src/cli/agents/claude-code.js +14 -0
- package/dist/src/cli/agents/claude-code.js.map +1 -0
- package/dist/src/cli/agents/cline.d.ts +5 -0
- package/dist/src/cli/agents/cline.d.ts.map +1 -0
- package/dist/src/cli/agents/cline.js +14 -0
- package/dist/src/cli/agents/cline.js.map +1 -0
- package/dist/src/cli/agents/cursor.d.ts +5 -0
- package/dist/src/cli/agents/cursor.d.ts.map +1 -0
- package/dist/src/cli/agents/cursor.js +14 -0
- package/dist/src/cli/agents/cursor.js.map +1 -0
- package/dist/src/cli/agents/factory-droid.d.ts +5 -0
- package/dist/src/cli/agents/factory-droid.d.ts.map +1 -0
- package/dist/src/cli/agents/factory-droid.js +14 -0
- package/dist/src/cli/agents/factory-droid.js.map +1 -0
- package/dist/src/cli/agents/gemini-cli.d.ts +5 -0
- package/dist/src/cli/agents/gemini-cli.d.ts.map +1 -0
- package/dist/src/cli/agents/gemini-cli.js +14 -0
- package/dist/src/cli/agents/gemini-cli.js.map +1 -0
- package/dist/src/cli/agents/index.d.ts +13 -0
- package/dist/src/cli/agents/index.d.ts.map +1 -0
- package/dist/src/cli/agents/index.js +27 -0
- package/dist/src/cli/agents/index.js.map +1 -0
- package/dist/src/cli/agents/windsurf.d.ts +5 -0
- package/dist/src/cli/agents/windsurf.d.ts.map +1 -0
- package/dist/src/cli/agents/windsurf.js +14 -0
- package/dist/src/cli/agents/windsurf.js.map +1 -0
- package/dist/src/cli/index.d.ts +8 -0
- package/dist/src/cli/index.d.ts.map +1 -0
- package/dist/src/cli/index.js +168 -0
- package/dist/src/cli/index.js.map +1 -0
- package/dist/src/cli/init.d.ts +34 -0
- package/dist/src/cli/init.d.ts.map +1 -0
- package/dist/src/cli/init.js +140 -0
- package/dist/src/cli/init.js.map +1 -0
- package/dist/src/cli/instructions.d.ts +29 -0
- package/dist/src/cli/instructions.d.ts.map +1 -0
- package/dist/src/cli/instructions.js +141 -0
- package/dist/src/cli/instructions.js.map +1 -0
- package/dist/src/cli/types.d.ts +22 -0
- package/dist/src/cli/types.d.ts.map +1 -0
- package/dist/src/cli/types.js +75 -0
- package/dist/src/cli/types.js.map +1 -0
- package/dist/src/contracts/mcp.js +34 -34
- package/dist/src/contracts/schemas.js.map +1 -1
- package/dist/src/server/mcp-server.d.ts.map +1 -1
- package/dist/src/server/mcp-server.js +7 -14
- package/dist/src/server/mcp-server.js.map +1 -1
- package/dist/src/services/embedding-service.d.ts.map +1 -1
- package/dist/src/services/embedding-service.js +1 -1
- package/dist/src/services/embedding-service.js.map +1 -1
- package/dist/src/services/memory-service.d.ts.map +1 -1
- package/dist/src/services/memory-service.js.map +1 -1
- package/dist/src/storage/markdown-storage.d.ts.map +1 -1
- package/dist/src/storage/markdown-storage.js +1 -1
- package/dist/src/storage/markdown-storage.js.map +1 -1
- package/dist/src/storage/vector-index.d.ts.map +1 -1
- package/dist/src/storage/vector-index.js +4 -5
- package/dist/src/storage/vector-index.js.map +1 -1
- package/docs/README.md +21 -0
- package/docs/mcp-tools.md +136 -0
- package/docs/user-guide.md +184 -0
- package/package.json +61 -59
- package/src/cli/agents/claude-code.ts +14 -0
- package/src/cli/agents/cline.ts +14 -0
- package/src/cli/agents/codex.ts +14 -0
- package/src/cli/agents/cursor.ts +14 -0
- package/src/cli/agents/factory-droid.ts +14 -0
- package/src/cli/agents/gemini-cli.ts +14 -0
- package/src/cli/agents/index.ts +36 -0
- package/src/cli/agents/windsurf.ts +14 -0
- package/src/cli/index.ts +192 -0
- package/src/cli/init.ts +218 -0
- package/src/cli/instructions.ts +156 -0
- package/src/cli/types.ts +112 -0
- package/src/contracts/index.ts +12 -12
- package/src/contracts/mcp.ts +223 -223
- package/src/contracts/schemas.ts +307 -307
- package/src/contracts/types.ts +410 -410
- package/src/index.ts +8 -8
- package/src/server/index.ts +5 -5
- package/src/server/mcp-server.ts +169 -186
- package/src/services/embedding-service.ts +114 -114
- package/src/services/index.ts +5 -5
- package/src/services/memory-service.ts +656 -663
- package/src/storage/frontmatter-parser.ts +243 -243
- package/src/storage/index.ts +6 -6
- package/src/storage/markdown-storage.ts +228 -236
- package/src/storage/vector-index.ts +159 -160
- package/src/utils/index.ts +5 -5
- package/src/utils/slugify.ts +63 -63
- package/test/cli/init.test.ts +402 -0
- package/test/contracts/schemas.test.ts +313 -313
- package/test/contracts/types.test.ts +21 -21
- package/test/frontmatter-parser-more.test.ts +94 -94
- package/test/server/mcp-server.test.ts +211 -210
- package/test/services/memory-service-edge.test.ts +248 -248
- package/test/services/memory-service.test.ts +291 -279
- package/test/storage/frontmatter-parser.test.ts +223 -223
- package/test/storage/markdown-storage.test.ts +226 -217
- package/test/storage/storage-edge.test.ts +238 -238
- package/test/storage/vector-index.test.ts +149 -153
- package/test/utils/slugify-edge.test.ts +94 -94
- package/test/utils/slugify.test.ts +72 -68
- package/docs/architecture-diagrams.md +0 -368
- package/docs/architecture.md +0 -381
- package/docs/contracts.md +0 -190
- package/docs/prompt-template.md +0 -33
- package/docs/proposals/mcp-typescript-sdk-refactor.md +0 -568
- package/docs/proposals/proposal-close-gates.md +0 -58
- package/docs/tool-calling-policy.md +0 -101
- package/docs/vector-search.md +0 -306
|
@@ -1,217 +1,226 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Markdown Storage Tests
|
|
3
|
-
* Tests for the MarkdownStorage class
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
7
|
-
import { mkdtempSync, rmSync, existsSync, readFileSync } from 'fs';
|
|
8
|
-
import { tmpdir } from 'os';
|
|
9
|
-
import { join } from 'path';
|
|
10
|
-
import { MarkdownStorage, StorageError } from '../../src/storage/markdown-storage.js';
|
|
11
|
-
import type { Memory } from '../../src/contracts/types.js';
|
|
12
|
-
|
|
13
|
-
describe('MarkdownStorage', () => {
|
|
14
|
-
let tempDir: string;
|
|
15
|
-
let storage: MarkdownStorage;
|
|
16
|
-
|
|
17
|
-
beforeEach(() => {
|
|
18
|
-
tempDir = mkdtempSync(join(tmpdir(), 'memhub-storage-test-'));
|
|
19
|
-
storage = new MarkdownStorage({ storagePath: tempDir });
|
|
20
|
-
});
|
|
21
|
-
|
|
22
|
-
afterEach(() => {
|
|
23
|
-
rmSync(tempDir, { recursive: true, force: true });
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
describe('write', () => {
|
|
27
|
-
const sampleMemory: Memory = {
|
|
28
|
-
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
29
|
-
createdAt: '2024-03-15T10:30:00Z',
|
|
30
|
-
updatedAt: '2024-03-15T10:30:00Z',
|
|
31
|
-
sessionId: '550e8400-e29b-41d4-a716-446655440999',
|
|
32
|
-
tags: ['test', 'memory'],
|
|
33
|
-
category: 'testing',
|
|
34
|
-
importance: 3,
|
|
35
|
-
title: 'Test Memory',
|
|
36
|
-
content: 'This is the content of the test memory.',
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
it('should write memory to markdown file', async () => {
|
|
40
|
-
const result = await storage.write(sampleMemory);
|
|
41
|
-
expect(existsSync(result)).toBe(true);
|
|
42
|
-
expect(result).toContain(`${sampleMemory.createdAt.split('T')[0]}`);
|
|
43
|
-
expect(result).toContain(sampleMemory.sessionId as string);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
it('should write valid YAML front matter', async () => {
|
|
47
|
-
const filePath = await storage.write(sampleMemory);
|
|
48
|
-
const content = readFileSync(filePath, 'utf-8');
|
|
49
|
-
expect(content).toMatch(/^---\n/);
|
|
50
|
-
expect(content).toContain('id: "550e8400-e29b-41d4-a716-446655440000"');
|
|
51
|
-
expect(content).toContain('created_at: "2024-03-15T10:30:00Z"');
|
|
52
|
-
expect(content).toContain('session_id: "550e8400-e29b-41d4-a716-446655440999"');
|
|
53
|
-
});
|
|
54
|
-
|
|
55
|
-
it('should write markdown body with title', async () => {
|
|
56
|
-
const filePath = await storage.write(sampleMemory);
|
|
57
|
-
const content = readFileSync(filePath, 'utf-8');
|
|
58
|
-
expect(content).toContain('# Test Memory');
|
|
59
|
-
expect(content).toContain('This is the content');
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
it('should write tags as YAML array', async () => {
|
|
63
|
-
const filePath = await storage.write(sampleMemory);
|
|
64
|
-
const content = readFileSync(filePath, 'utf-8');
|
|
65
|
-
expect(content).toContain('tags:');
|
|
66
|
-
expect(content).toContain('test');
|
|
67
|
-
expect(content).toContain('memory');
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
it('should return nested file path', async () => {
|
|
71
|
-
const result = await storage.write(sampleMemory);
|
|
72
|
-
expect(result).toContain('test-memory.md');
|
|
73
|
-
expect(result).toContain('2024-03-15');
|
|
74
|
-
expect(result).toContain('550e8400-e29b-41d4-a716-446655440999');
|
|
75
|
-
});
|
|
76
|
-
});
|
|
77
|
-
|
|
78
|
-
describe('read', () => {
|
|
79
|
-
it('should read memory from markdown file', async () => {
|
|
80
|
-
// Create a test file first
|
|
81
|
-
const testContent = `---
|
|
82
|
-
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
83
|
-
created_at: "2024-03-15T10:30:00Z"
|
|
84
|
-
updated_at: "2024-03-15T10:30:00Z"
|
|
85
|
-
tags:
|
|
86
|
-
- test
|
|
87
|
-
category: "testing"
|
|
88
|
-
importance: 3
|
|
89
|
-
---
|
|
90
|
-
|
|
91
|
-
# Test Memory
|
|
92
|
-
|
|
93
|
-
This is the content.
|
|
94
|
-
`;
|
|
95
|
-
const filePath = join(tempDir, 'test.md');
|
|
96
|
-
// Use sync write for test setup
|
|
97
|
-
const { writeFileSync } = await import('fs');
|
|
98
|
-
writeFileSync(filePath, testContent);
|
|
99
|
-
|
|
100
|
-
const memory = await storage.read('550e8400-e29b-41d4-a716-446655440000');
|
|
101
|
-
expect(memory.id).toBe('550e8400-e29b-41d4-a716-446655440000');
|
|
102
|
-
expect(memory.title).toBe('Test Memory');
|
|
103
|
-
});
|
|
104
|
-
|
|
105
|
-
it('should throw error when file not found', async () => {
|
|
106
|
-
await expect(
|
|
107
|
-
|
|
108
|
-
)
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
it('should parse front matter correctly', async () => {
|
|
112
|
-
const testContent = `---
|
|
113
|
-
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
114
|
-
created_at: "2024-03-15T10:30:00Z"
|
|
115
|
-
updated_at: "2024-03-15T14:20:00Z"
|
|
116
|
-
tags:
|
|
117
|
-
- tag1
|
|
118
|
-
- tag2
|
|
119
|
-
category: "work"
|
|
120
|
-
importance: 4
|
|
121
|
-
---
|
|
122
|
-
|
|
123
|
-
# Test Title
|
|
124
|
-
|
|
125
|
-
Test content.
|
|
126
|
-
`;
|
|
127
|
-
const { writeFileSync } = await import('fs');
|
|
128
|
-
writeFileSync(join(tempDir, 'test.md'), testContent);
|
|
129
|
-
|
|
130
|
-
const memory = await storage.read('550e8400-e29b-41d4-a716-446655440000');
|
|
131
|
-
expect(memory.tags).toEqual(['tag1', 'tag2']);
|
|
132
|
-
expect(memory.category).toBe('work');
|
|
133
|
-
expect(memory.importance).toBe(4);
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
|
|
137
|
-
describe('delete', () => {
|
|
138
|
-
it('should delete memory file', async () => {
|
|
139
|
-
// Create a test file first
|
|
140
|
-
const testContent = `---
|
|
141
|
-
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
142
|
-
created_at: "2024-03-15T10:30:00Z"
|
|
143
|
-
updated_at: "2024-03-15T10:30:00Z"
|
|
144
|
-
tags: []
|
|
145
|
-
category: "general"
|
|
146
|
-
importance: 3
|
|
147
|
-
---
|
|
148
|
-
|
|
149
|
-
# Test
|
|
150
|
-
`;
|
|
151
|
-
const filePath = join(tempDir, 'test.md');
|
|
152
|
-
const { writeFileSync } = await import('fs');
|
|
153
|
-
writeFileSync(filePath, testContent);
|
|
154
|
-
|
|
155
|
-
await storage.delete('550e8400-e29b-41d4-a716-446655440000');
|
|
156
|
-
expect(existsSync(filePath)).toBe(false);
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
it('should throw error when file not found', async () => {
|
|
160
|
-
await expect(
|
|
161
|
-
|
|
162
|
-
)
|
|
163
|
-
});
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
describe('list', () => {
|
|
167
|
-
it('should list all memory files', async () => {
|
|
168
|
-
const { writeFileSync } = await import('fs');
|
|
169
|
-
writeFileSync(
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Markdown Storage Tests
|
|
3
|
+
* Tests for the MarkdownStorage class
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
|
|
7
|
+
import { mkdtempSync, rmSync, existsSync, readFileSync } from 'fs';
|
|
8
|
+
import { tmpdir } from 'os';
|
|
9
|
+
import { join } from 'path';
|
|
10
|
+
import { MarkdownStorage, StorageError } from '../../src/storage/markdown-storage.js';
|
|
11
|
+
import type { Memory } from '../../src/contracts/types.js';
|
|
12
|
+
|
|
13
|
+
describe('MarkdownStorage', () => {
|
|
14
|
+
let tempDir: string;
|
|
15
|
+
let storage: MarkdownStorage;
|
|
16
|
+
|
|
17
|
+
beforeEach(() => {
|
|
18
|
+
tempDir = mkdtempSync(join(tmpdir(), 'memhub-storage-test-'));
|
|
19
|
+
storage = new MarkdownStorage({ storagePath: tempDir });
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
afterEach(() => {
|
|
23
|
+
rmSync(tempDir, { recursive: true, force: true });
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
describe('write', () => {
|
|
27
|
+
const sampleMemory: Memory = {
|
|
28
|
+
id: '550e8400-e29b-41d4-a716-446655440000',
|
|
29
|
+
createdAt: '2024-03-15T10:30:00Z',
|
|
30
|
+
updatedAt: '2024-03-15T10:30:00Z',
|
|
31
|
+
sessionId: '550e8400-e29b-41d4-a716-446655440999',
|
|
32
|
+
tags: ['test', 'memory'],
|
|
33
|
+
category: 'testing',
|
|
34
|
+
importance: 3,
|
|
35
|
+
title: 'Test Memory',
|
|
36
|
+
content: 'This is the content of the test memory.',
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
it('should write memory to markdown file', async () => {
|
|
40
|
+
const result = await storage.write(sampleMemory);
|
|
41
|
+
expect(existsSync(result)).toBe(true);
|
|
42
|
+
expect(result).toContain(`${sampleMemory.createdAt.split('T')[0]}`);
|
|
43
|
+
expect(result).toContain(sampleMemory.sessionId as string);
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('should write valid YAML front matter', async () => {
|
|
47
|
+
const filePath = await storage.write(sampleMemory);
|
|
48
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
49
|
+
expect(content).toMatch(/^---\n/);
|
|
50
|
+
expect(content).toContain('id: "550e8400-e29b-41d4-a716-446655440000"');
|
|
51
|
+
expect(content).toContain('created_at: "2024-03-15T10:30:00Z"');
|
|
52
|
+
expect(content).toContain('session_id: "550e8400-e29b-41d4-a716-446655440999"');
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('should write markdown body with title', async () => {
|
|
56
|
+
const filePath = await storage.write(sampleMemory);
|
|
57
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
58
|
+
expect(content).toContain('# Test Memory');
|
|
59
|
+
expect(content).toContain('This is the content');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('should write tags as YAML array', async () => {
|
|
63
|
+
const filePath = await storage.write(sampleMemory);
|
|
64
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
65
|
+
expect(content).toContain('tags:');
|
|
66
|
+
expect(content).toContain('test');
|
|
67
|
+
expect(content).toContain('memory');
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('should return nested file path', async () => {
|
|
71
|
+
const result = await storage.write(sampleMemory);
|
|
72
|
+
expect(result).toContain('test-memory.md');
|
|
73
|
+
expect(result).toContain('2024-03-15');
|
|
74
|
+
expect(result).toContain('550e8400-e29b-41d4-a716-446655440999');
|
|
75
|
+
});
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
describe('read', () => {
|
|
79
|
+
it('should read memory from markdown file', async () => {
|
|
80
|
+
// Create a test file first
|
|
81
|
+
const testContent = `---
|
|
82
|
+
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
83
|
+
created_at: "2024-03-15T10:30:00Z"
|
|
84
|
+
updated_at: "2024-03-15T10:30:00Z"
|
|
85
|
+
tags:
|
|
86
|
+
- test
|
|
87
|
+
category: "testing"
|
|
88
|
+
importance: 3
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
# Test Memory
|
|
92
|
+
|
|
93
|
+
This is the content.
|
|
94
|
+
`;
|
|
95
|
+
const filePath = join(tempDir, 'test.md');
|
|
96
|
+
// Use sync write for test setup
|
|
97
|
+
const { writeFileSync } = await import('fs');
|
|
98
|
+
writeFileSync(filePath, testContent);
|
|
99
|
+
|
|
100
|
+
const memory = await storage.read('550e8400-e29b-41d4-a716-446655440000');
|
|
101
|
+
expect(memory.id).toBe('550e8400-e29b-41d4-a716-446655440000');
|
|
102
|
+
expect(memory.title).toBe('Test Memory');
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('should throw error when file not found', async () => {
|
|
106
|
+
await expect(storage.read('550e8400-e29b-41d4-a716-446655440000')).rejects.toThrow(
|
|
107
|
+
StorageError
|
|
108
|
+
);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it('should parse front matter correctly', async () => {
|
|
112
|
+
const testContent = `---
|
|
113
|
+
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
114
|
+
created_at: "2024-03-15T10:30:00Z"
|
|
115
|
+
updated_at: "2024-03-15T14:20:00Z"
|
|
116
|
+
tags:
|
|
117
|
+
- tag1
|
|
118
|
+
- tag2
|
|
119
|
+
category: "work"
|
|
120
|
+
importance: 4
|
|
121
|
+
---
|
|
122
|
+
|
|
123
|
+
# Test Title
|
|
124
|
+
|
|
125
|
+
Test content.
|
|
126
|
+
`;
|
|
127
|
+
const { writeFileSync } = await import('fs');
|
|
128
|
+
writeFileSync(join(tempDir, 'test.md'), testContent);
|
|
129
|
+
|
|
130
|
+
const memory = await storage.read('550e8400-e29b-41d4-a716-446655440000');
|
|
131
|
+
expect(memory.tags).toEqual(['tag1', 'tag2']);
|
|
132
|
+
expect(memory.category).toBe('work');
|
|
133
|
+
expect(memory.importance).toBe(4);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('delete', () => {
|
|
138
|
+
it('should delete memory file', async () => {
|
|
139
|
+
// Create a test file first
|
|
140
|
+
const testContent = `---
|
|
141
|
+
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
142
|
+
created_at: "2024-03-15T10:30:00Z"
|
|
143
|
+
updated_at: "2024-03-15T10:30:00Z"
|
|
144
|
+
tags: []
|
|
145
|
+
category: "general"
|
|
146
|
+
importance: 3
|
|
147
|
+
---
|
|
148
|
+
|
|
149
|
+
# Test
|
|
150
|
+
`;
|
|
151
|
+
const filePath = join(tempDir, 'test.md');
|
|
152
|
+
const { writeFileSync } = await import('fs');
|
|
153
|
+
writeFileSync(filePath, testContent);
|
|
154
|
+
|
|
155
|
+
await storage.delete('550e8400-e29b-41d4-a716-446655440000');
|
|
156
|
+
expect(existsSync(filePath)).toBe(false);
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
it('should throw error when file not found', async () => {
|
|
160
|
+
await expect(storage.delete('550e8400-e29b-41d4-a716-446655440000')).rejects.toThrow(
|
|
161
|
+
StorageError
|
|
162
|
+
);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('list', () => {
|
|
167
|
+
it('should list all memory files', async () => {
|
|
168
|
+
const { writeFileSync } = await import('fs');
|
|
169
|
+
writeFileSync(
|
|
170
|
+
join(tempDir, '2024-03-15-a.md'),
|
|
171
|
+
'---\nid: "a"\ncreated_at: "2024-03-15T10:30:00Z"\nupdated_at: "2024-03-15T10:30:00Z"\ntags: []\ncategory: "general"\nimportance: 3\n---\n\n# A'
|
|
172
|
+
);
|
|
173
|
+
writeFileSync(
|
|
174
|
+
join(tempDir, '2024-03-16-b.md'),
|
|
175
|
+
'---\nid: "b"\ncreated_at: "2024-03-16T10:30:00Z"\nupdated_at: "2024-03-16T10:30:00Z"\ntags: []\ncategory: "general"\nimportance: 3\n---\n\n# B'
|
|
176
|
+
);
|
|
177
|
+
|
|
178
|
+
const files = await storage.list();
|
|
179
|
+
expect(files).toHaveLength(2);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('should return empty array when no files exist', async () => {
|
|
183
|
+
const files = await storage.list();
|
|
184
|
+
expect(files).toEqual([]);
|
|
185
|
+
});
|
|
186
|
+
|
|
187
|
+
it('should only include .md files', async () => {
|
|
188
|
+
const { writeFileSync } = await import('fs');
|
|
189
|
+
writeFileSync(
|
|
190
|
+
join(tempDir, 'test.md'),
|
|
191
|
+
'---\nid: "test"\ncreated_at: "2024-03-15T10:30:00Z"\nupdated_at: "2024-03-15T10:30:00Z"\ntags: []\ncategory: "general"\nimportance: 3\n---\n\n# Test'
|
|
192
|
+
);
|
|
193
|
+
writeFileSync(join(tempDir, 'test.txt'), 'not markdown');
|
|
194
|
+
|
|
195
|
+
const files = await storage.list();
|
|
196
|
+
expect(files).toHaveLength(1);
|
|
197
|
+
expect(files[0].filename).toBe('test.md');
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe('findById', () => {
|
|
202
|
+
it('should find file by memory ID', async () => {
|
|
203
|
+
const testContent = `---
|
|
204
|
+
id: "550e8400-e29b-41d4-a716-446655440000"
|
|
205
|
+
created_at: "2024-03-15T10:30:00Z"
|
|
206
|
+
updated_at: "2024-03-15T10:30:00Z"
|
|
207
|
+
tags: []
|
|
208
|
+
category: "general"
|
|
209
|
+
importance: 3
|
|
210
|
+
---
|
|
211
|
+
|
|
212
|
+
# Test
|
|
213
|
+
`;
|
|
214
|
+
const { writeFileSync } = await import('fs');
|
|
215
|
+
writeFileSync(join(tempDir, 'test.md'), testContent);
|
|
216
|
+
|
|
217
|
+
const filePath = await storage.findById('550e8400-e29b-41d4-a716-446655440000');
|
|
218
|
+
expect(filePath).toContain('test.md');
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('should return null when ID not found', async () => {
|
|
222
|
+
const filePath = await storage.findById('550e8400-e29b-41d4-a716-446655440000');
|
|
223
|
+
expect(filePath).toBeNull();
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
});
|