memory-cli.test.mjs
170 lines 5.3 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 /**
2 * CLI memory subcommand integration tests.
3 */
4 import { describe, it, before, after, beforeEach } from 'node:test';
5 import assert from 'node:assert';
6 import fs from 'fs';
7 import path from 'path';
8 import os from 'os';
9 import { execSync } from 'child_process';
10 import { fileURLToPath } from 'url';
11
12 const __dirname = path.dirname(fileURLToPath(import.meta.url));
13 const cliPath = path.join(__dirname, '..', 'cli', 'index.mjs');
14 let tmpDir;
15 let vaultDir;
16 let dataDir;
17
18 function run(cmdArgs, opts = {}) {
19 const env = {
20 ...process.env,
21 KNOWTATION_VAULT_PATH: vaultDir,
22 KNOWTATION_DATA_DIR: dataDir,
23 KNOWTATION_MEMORY_ENABLED: 'true',
24 KNOWTATION_MEMORY_PROVIDER: 'file',
25 };
26 try {
27 const out = execSync(`node ${cliPath} ${cmdArgs}`, {
28 cwd: path.join(__dirname, '..'),
29 env,
30 timeout: 10000,
31 encoding: 'utf8',
32 stdio: ['pipe', 'pipe', 'pipe'],
33 input: opts.stdin,
34 });
35 return { stdout: out.trim(), exitCode: 0 };
36 } catch (e) {
37 return { stdout: (e.stdout || '').trim(), stderr: (e.stderr || '').trim(), exitCode: e.status };
38 }
39 }
40
41 before(() => {
42 tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'knowtation-cli-mem-'));
43 vaultDir = path.join(tmpDir, 'vault');
44 dataDir = path.join(tmpDir, 'data');
45 fs.mkdirSync(vaultDir, { recursive: true });
46 fs.mkdirSync(dataDir, { recursive: true });
47 fs.mkdirSync(path.join(tmpDir, 'config'), { recursive: true });
48 fs.writeFileSync(path.join(tmpDir, 'config', 'local.yaml'), `vault_path: ${vaultDir}\ndata_dir: ${dataDir}\nmemory:\n enabled: true\n provider: file\n`, 'utf8');
49 fs.writeFileSync(path.join(vaultDir, 'test.md'), '---\ntitle: test\n---\nHello', 'utf8');
50 });
51
52 after(() => {
53 fs.rmSync(tmpDir, { recursive: true, force: true });
54 });
55
56 describe('CLI memory subcommand', () => {
57 it('memory --help shows actions', () => {
58 const r = run('memory --help');
59 assert.strictEqual(r.exitCode, 0);
60 assert(r.stdout.includes('query'));
61 assert(r.stdout.includes('list'));
62 assert(r.stdout.includes('store'));
63 assert(r.stdout.includes('stats'));
64 });
65
66 it('memory without action shows error', () => {
67 const r = run('memory');
68 assert.notStrictEqual(r.exitCode, 0);
69 });
70
71 it('memory query with no events returns null', () => {
72 const r = run('memory query search --json');
73 assert.strictEqual(r.exitCode, 0);
74 const data = JSON.parse(r.stdout);
75 assert.strictEqual(data.value, null);
76 });
77
78 it('memory store + query round-trip', () => {
79 const storeR = run('memory store my_key \'{"note":"hello"}\'');
80 assert.strictEqual(storeR.exitCode, 0);
81 assert(storeR.stdout.includes('Stored:'));
82
83 const queryR = run('memory query user --json');
84 assert.strictEqual(queryR.exitCode, 0);
85 const data = JSON.parse(queryR.stdout);
86 assert.notStrictEqual(data.value, null);
87 });
88
89 it('memory store --json outputs id', () => {
90 const r = run('memory store test_key \'{"v":1}\' --json');
91 assert.strictEqual(r.exitCode, 0);
92 const data = JSON.parse(r.stdout);
93 assert.match(data.id, /^mem_/);
94 });
95
96 it('memory list returns events', () => {
97 run('memory store k1 \'{"x":1}\'');
98 const r = run('memory list --json');
99 assert.strictEqual(r.exitCode, 0);
100 const data = JSON.parse(r.stdout);
101 assert(Array.isArray(data.events));
102 assert(data.count > 0);
103 });
104
105 it('memory list --type filters', () => {
106 const r = run('memory list --type user --json');
107 assert.strictEqual(r.exitCode, 0);
108 const data = JSON.parse(r.stdout);
109 for (const e of data.events) {
110 assert.strictEqual(e.type, 'user');
111 }
112 });
113
114 it('memory stats returns totals', () => {
115 const r = run('memory stats --json');
116 assert.strictEqual(r.exitCode, 0);
117 const data = JSON.parse(r.stdout);
118 assert.strictEqual(typeof data.total, 'number');
119 assert(data.total > 0);
120 assert.strictEqual(typeof data.size_bytes, 'number');
121 });
122
123 it('memory stats human output', () => {
124 const r = run('memory stats');
125 assert.strictEqual(r.exitCode, 0);
126 assert(r.stdout.includes('Total events:'));
127 });
128
129 it('memory export --format jsonl outputs JSONL', () => {
130 const r = run('memory export --format jsonl');
131 assert.strictEqual(r.exitCode, 0);
132 const lines = r.stdout.split('\n').filter(Boolean);
133 assert(lines.length > 0);
134 for (const line of lines) {
135 JSON.parse(line);
136 }
137 });
138
139 it('memory export --format mif outputs MIF-like', () => {
140 const r = run('memory export --format mif');
141 assert.strictEqual(r.exitCode, 0);
142 assert(r.stdout.includes('---'));
143 assert(r.stdout.includes('type:'));
144 });
145
146 it('memory clear without --confirm fails', () => {
147 const r = run('memory clear');
148 assert.notStrictEqual(r.exitCode, 0);
149 });
150
151 it('memory clear --confirm clears events', () => {
152 run('memory store clearme \'{"v":1}\'');
153 const beforeR = run('memory stats --json');
154 const beforeTotal = JSON.parse(beforeR.stdout).total;
155 assert(beforeTotal > 0);
156
157 const r = run('memory clear --confirm --json');
158 assert.strictEqual(r.exitCode, 0);
159 const data = JSON.parse(r.stdout);
160 assert(data.cleared > 0);
161
162 const afterR = run('memory stats --json');
163 assert.strictEqual(JSON.parse(afterR.stdout).total, 0);
164 });
165
166 it('memory search fails gracefully with file provider', () => {
167 const r = run('memory search test query');
168 assert.notStrictEqual(r.exitCode, 0);
169 });
170 });
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 1 day ago