deepbase-sqlite 3.4.12 → 3.5.1

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/test/test.js CHANGED
@@ -8,840 +8,617 @@ import { SqliteDriver } from '../src/SqliteDriver.js';
8
8
  const __dirname = path.dirname(fileURLToPath(import.meta.url));
9
9
  const testDataPath = path.join(__dirname, 'test-data');
10
10
 
11
- describe('SqliteDriver', function() {
12
- let db;
13
- let testCounter = 0;
14
-
15
- before(function() {
16
- // Clean up test data before all tests
17
- if (fs.existsSync(testDataPath)) {
18
- fs.rmSync(testDataPath, { recursive: true, force: true });
19
- }
20
- });
11
+ const PRAGMA_MODES = ['none', 'safe', 'balanced', 'fast'];
21
12
 
22
- beforeEach(async function() {
23
- // Use unique name for each test to avoid singleton conflicts
24
- testCounter++;
25
- db = new DeepBase(new SqliteDriver({
26
- name: `test-${testCounter}`,
27
- path: testDataPath
28
- }));
29
- await db.connect();
30
- });
13
+ for (const pragma of PRAGMA_MODES) {
14
+ describe(`SqliteDriver [pragma=${pragma}]`, function () {
15
+ let db;
16
+ let testCounter = 0;
31
17
 
32
- afterEach(async function() {
33
- await db.disconnect();
34
- if (fs.existsSync(testDataPath)) {
35
- fs.rmSync(testDataPath, { recursive: true, force: true });
36
- }
37
- });
38
-
39
- describe('Basic Operations', function() {
40
- it('should set and get a simple value', async function() {
41
- await db.set('key', 'value');
42
- const result = await db.get('key');
43
- assert.strictEqual(result, 'value');
18
+ before(function () {
19
+ if (fs.existsSync(testDataPath)) {
20
+ fs.rmSync(testDataPath, { recursive: true, force: true });
21
+ }
44
22
  });
45
23
 
46
- it('should set and get nested values', async function() {
47
- await db.set('user', 'name', 'Alice');
48
- await db.set('user', 'age', 30);
49
-
50
- const name = await db.get('user', 'name');
51
- const age = await db.get('user', 'age');
52
-
53
- assert.strictEqual(name, 'Alice');
54
- assert.strictEqual(age, 30);
24
+ beforeEach(async function () {
25
+ testCounter++;
26
+ db = new DeepBase(new SqliteDriver({
27
+ name: `test-${pragma}-${testCounter}`,
28
+ path: testDataPath,
29
+ pragma,
30
+ }));
31
+ await db.connect();
55
32
  });
56
33
 
57
- it('should get entire nested object', async function() {
58
- await db.set('user', 'name', 'Bob');
59
- await db.set('user', 'age', 25);
60
-
61
- const user = await db.get('user');
62
- assert.deepStrictEqual(user, { name: 'Bob', age: 25 });
34
+ afterEach(async function () {
35
+ await db.disconnect();
36
+ if (fs.existsSync(testDataPath)) {
37
+ fs.rmSync(testDataPath, { recursive: true, force: true });
38
+ }
63
39
  });
64
40
 
65
- it('should return null for non-existent keys', async function() {
66
- const result = await db.get('nonexistent');
67
- assert.strictEqual(result, null);
68
- });
41
+ describe('Basic Operations', function () {
42
+ it('should set and get a simple value', async function () {
43
+ await db.set('key', 'value');
44
+ assert.strictEqual(await db.get('key'), 'value');
45
+ });
69
46
 
70
- it('should handle numbers', async function() {
71
- await db.set('number', 42);
72
- const result = await db.get('number');
73
- assert.strictEqual(result, 42);
74
- });
47
+ it('should set and get nested values', async function () {
48
+ await db.set('user', 'name', 'Alice');
49
+ await db.set('user', 'age', 30);
50
+ assert.strictEqual(await db.get('user', 'name'), 'Alice');
51
+ assert.strictEqual(await db.get('user', 'age'), 30);
52
+ });
75
53
 
76
- it('should handle booleans', async function() {
77
- await db.set('flag', true);
78
- const result = await db.get('flag');
79
- assert.strictEqual(result, true);
80
- });
54
+ it('should get entire nested object', async function () {
55
+ await db.set('user', 'name', 'Bob');
56
+ await db.set('user', 'age', 25);
57
+ assert.deepStrictEqual(await db.get('user'), { name: 'Bob', age: 25 });
58
+ });
81
59
 
82
- it('should handle arrays', async function() {
83
- await db.set('list', [1, 2, 3]);
84
- const result = await db.get('list');
85
- assert.deepStrictEqual(result, [1, 2, 3]);
86
- });
60
+ it('should return null for non-existent keys', async function () {
61
+ assert.strictEqual(await db.get('nonexistent'), null);
62
+ });
87
63
 
88
- it('should handle complex objects', async function() {
89
- const complexObj = {
90
- name: 'Test',
91
- items: [1, 2, 3],
92
- meta: { created: '2024-01-01', active: true }
93
- };
94
- await db.set('complex', complexObj);
95
- const result = await db.get('complex');
96
- assert.deepStrictEqual(result, complexObj);
97
- });
98
- });
64
+ it('should handle numbers', async function () {
65
+ await db.set('number', 42);
66
+ assert.strictEqual(await db.get('number'), 42);
67
+ });
99
68
 
100
- describe('Keys with Dots', function() {
101
- it('should handle keys containing dots', async function() {
102
- const setResult = await db.set('value', 'martin.clasen@gmail.com', 1);
103
-
104
- assert.strictEqual(setResult.length, 2);
105
- assert.strictEqual(setResult[0], 'value');
106
- assert.strictEqual(setResult[1], 'martin.clasen@gmail.com');
107
-
108
- const getValue = await db.get('value');
109
- assert.deepStrictEqual(getValue, { 'martin.clasen@gmail.com': 1 });
110
- });
69
+ it('should handle booleans', async function () {
70
+ await db.set('flag', true);
71
+ assert.strictEqual(await db.get('flag'), true);
72
+ });
111
73
 
112
- it('should handle keys with multiple dots', async function() {
113
- await db.set('config', 'api.prod.endpoint', 'https://api.example.com');
114
- const endpoint = await db.get('config', 'api.prod.endpoint');
115
- assert.strictEqual(endpoint, 'https://api.example.com');
116
- });
74
+ it('should handle arrays', async function () {
75
+ await db.set('list', [1, 2, 3]);
76
+ assert.deepStrictEqual(await db.get('list'), [1, 2, 3]);
77
+ });
117
78
 
118
- it('should handle nested paths with dots in keys', async function() {
119
- await db.set('users', 'john.doe@example.com', 'name', 'John Doe');
120
- await db.set('users', 'john.doe@example.com', 'age', 30);
121
-
122
- const user = await db.get('users', 'john.doe@example.com');
123
- assert.deepStrictEqual(user, { name: 'John Doe', age: 30 });
79
+ it('should handle complex objects', async function () {
80
+ const complexObj = {
81
+ name: 'Test',
82
+ items: [1, 2, 3],
83
+ meta: { created: '2024-01-01', active: true },
84
+ };
85
+ await db.set('complex', complexObj);
86
+ assert.deepStrictEqual(await db.get('complex'), complexObj);
87
+ });
124
88
  });
125
89
 
126
- it('should handle dots in first level keys', async function() {
127
- await db.set('config.prod', 'value', 100);
128
- const configProd = await db.get('config.prod');
129
- assert.deepStrictEqual(configProd, { value: 100 });
130
- });
90
+ describe('Keys with Dots', function () {
91
+ it('should handle keys containing dots', async function () {
92
+ const setResult = await db.set('value', 'martin.clasen@gmail.com', 1);
93
+ assert.strictEqual(setResult.length, 2);
94
+ assert.strictEqual(setResult[0], 'value');
95
+ assert.strictEqual(setResult[1], 'martin.clasen@gmail.com');
96
+ assert.deepStrictEqual(await db.get('value'), { 'martin.clasen@gmail.com': 1 });
97
+ });
131
98
 
132
- it('should distinguish between dots in keys and path separators', async function() {
133
- // Set a regular nested path
134
- await db.set('users', 'alice', 'email', 'alice@example.com');
135
-
136
- // Set a key with dots at the same level
137
- await db.set('users', 'john.doe@company.com', 'email', 'john@work.com');
138
-
139
- const users = await db.get('users');
140
- assert.strictEqual(users.alice.email, 'alice@example.com');
141
- assert.strictEqual(users['john.doe@company.com'].email, 'john@work.com');
142
- assert.strictEqual(Object.keys(users).length, 2);
143
- });
99
+ it('should handle keys with multiple dots', async function () {
100
+ await db.set('config', 'api.prod.endpoint', 'https://api.example.com');
101
+ assert.strictEqual(await db.get('config', 'api.prod.endpoint'), 'https://api.example.com');
102
+ });
144
103
 
145
- it('should delete keys containing dots', async function() {
146
- await db.set('emails', 'user.name@domain.com', 'verified', true);
147
- await db.set('emails', 'other@email.com', 'verified', false);
148
-
149
- await db.del('emails', 'user.name@domain.com');
150
-
151
- const email1 = await db.get('emails', 'user.name@domain.com');
152
- const email2 = await db.get('emails', 'other@email.com');
153
-
154
- assert.strictEqual(email1, null);
155
- assert.deepStrictEqual(email2, { verified: false });
156
- });
157
- });
104
+ it('should handle nested paths with dots in keys', async function () {
105
+ await db.set('users', 'john.doe@example.com', 'name', 'John Doe');
106
+ await db.set('users', 'john.doe@example.com', 'age', 30);
107
+ assert.deepStrictEqual(await db.get('users', 'john.doe@example.com'), { name: 'John Doe', age: 30 });
108
+ });
158
109
 
159
- describe('Delete Operations', function() {
160
- it('should delete a key', async function() {
161
- await db.set('temp', 'value');
162
- await db.del('temp');
163
-
164
- const result = await db.get('temp');
165
- assert.strictEqual(result, null);
166
- });
110
+ it('should handle dots in first level keys', async function () {
111
+ await db.set('config.prod', 'value', 100);
112
+ assert.deepStrictEqual(await db.get('config.prod'), { value: 100 });
113
+ });
167
114
 
168
- it('should delete nested key', async function() {
169
- await db.set('user', 'name', 'Alice');
170
- await db.set('user', 'age', 30);
171
- await db.del('user', 'age');
172
-
173
- const user = await db.get('user');
174
- assert.deepStrictEqual(user, { name: 'Alice' });
175
- });
115
+ it('should distinguish between dots in keys and path separators', async function () {
116
+ await db.set('users', 'alice', 'email', 'alice@example.com');
117
+ await db.set('users', 'john.doe@company.com', 'email', 'john@work.com');
118
+ const users = await db.get('users');
119
+ assert.strictEqual(users.alice.email, 'alice@example.com');
120
+ assert.strictEqual(users['john.doe@company.com'].email, 'john@work.com');
121
+ assert.strictEqual(Object.keys(users).length, 2);
122
+ });
176
123
 
177
- it('should delete parent and all children', async function() {
178
- await db.set('parent', 'child1', 'value1');
179
- await db.set('parent', 'child2', 'value2');
180
- await db.set('parent', 'nested', 'deep', 'value3');
181
-
182
- await db.del('parent');
183
-
184
- const result = await db.get('parent');
185
- assert.strictEqual(result, null);
186
-
187
- const child1 = await db.get('parent', 'child1');
188
- assert.strictEqual(child1, null);
189
-
190
- const nested = await db.get('parent', 'nested', 'deep');
191
- assert.strictEqual(nested, null);
124
+ it('should delete keys containing dots', async function () {
125
+ await db.set('emails', 'user.name@domain.com', 'verified', true);
126
+ await db.set('emails', 'other@email.com', 'verified', false);
127
+ await db.del('emails', 'user.name@domain.com');
128
+ assert.strictEqual(await db.get('emails', 'user.name@domain.com'), null);
129
+ assert.deepStrictEqual(await db.get('emails', 'other@email.com'), { verified: false });
130
+ });
192
131
  });
193
132
 
194
- it('should clear all data', async function() {
195
- await db.set('key1', 'value1');
196
- await db.set('key2', 'value2');
197
- await db.del();
198
-
199
- const all = await db.get();
200
- assert.deepStrictEqual(all, {});
201
- });
202
- });
133
+ describe('Delete Operations', function () {
134
+ it('should delete a key', async function () {
135
+ await db.set('temp', 'value');
136
+ await db.del('temp');
137
+ assert.strictEqual(await db.get('temp'), null);
138
+ });
203
139
 
204
- describe('Add Operation', function() {
205
- it('should add item with auto-generated ID', async function() {
206
- const path = await db.add('users', { name: 'Charlie' });
207
-
208
- assert.strictEqual(path.length, 2);
209
- assert.strictEqual(path[0], 'users');
210
- assert.strictEqual(typeof path[1], 'string');
211
- assert.strictEqual(path[1].length, 10);
212
-
213
- const user = await db.get(...path);
214
- assert.deepStrictEqual(user, { name: 'Charlie' });
215
- });
140
+ it('should delete nested key', async function () {
141
+ await db.set('user', 'name', 'Alice');
142
+ await db.set('user', 'age', 30);
143
+ await db.del('user', 'age');
144
+ assert.deepStrictEqual(await db.get('user'), { name: 'Alice' });
145
+ });
216
146
 
217
- it('should add multiple items with unique IDs', async function() {
218
- const path1 = await db.add('items', { value: 1 });
219
- const path2 = await db.add('items', { value: 2 });
220
-
221
- assert.notStrictEqual(path1[1], path2[1]);
222
-
223
- const item1 = await db.get(...path1);
224
- const item2 = await db.get(...path2);
225
-
226
- assert.deepStrictEqual(item1, { value: 1 });
227
- assert.deepStrictEqual(item2, { value: 2 });
228
- });
147
+ it('should delete parent and all children', async function () {
148
+ await db.set('parent', 'child1', 'value1');
149
+ await db.set('parent', 'child2', 'value2');
150
+ await db.set('parent', 'nested', 'deep', 'value3');
151
+ await db.del('parent');
152
+ assert.strictEqual(await db.get('parent'), null);
153
+ assert.strictEqual(await db.get('parent', 'child1'), null);
154
+ assert.strictEqual(await db.get('parent', 'nested', 'deep'), null);
155
+ });
229
156
 
230
- it('should add items at nested paths', async function() {
231
- const path = await db.add('categories', 'electronics', 'items', { name: 'Laptop' });
232
-
233
- assert.strictEqual(path.length, 4);
234
- assert.strictEqual(path[0], 'categories');
235
- assert.strictEqual(path[1], 'electronics');
236
- assert.strictEqual(path[2], 'items');
237
-
238
- const item = await db.get(...path);
239
- assert.deepStrictEqual(item, { name: 'Laptop' });
157
+ it('should clear all data', async function () {
158
+ await db.set('key1', 'value1');
159
+ await db.set('key2', 'value2');
160
+ await db.del();
161
+ assert.deepStrictEqual(await db.get(), {});
162
+ });
240
163
  });
241
- });
242
164
 
243
- describe('Increment/Decrement', function() {
244
- it('should increment a value', async function() {
245
- await db.set('counter', 10);
246
- await db.inc('counter', 5);
247
-
248
- const result = await db.get('counter');
249
- assert.strictEqual(result, 15);
250
- });
165
+ describe('Add Operation', function () {
166
+ it('should add item with auto-generated ID', async function () {
167
+ const p = await db.add('users', { name: 'Charlie' });
168
+ assert.strictEqual(p.length, 2);
169
+ assert.strictEqual(p[0], 'users');
170
+ assert.strictEqual(typeof p[1], 'string');
171
+ assert.strictEqual(p[1].length, 10);
172
+ assert.deepStrictEqual(await db.get(...p), { name: 'Charlie' });
173
+ });
251
174
 
252
- it('should decrement a value', async function() {
253
- await db.set('counter', 20);
254
- await db.dec('counter', 8);
255
-
256
- const result = await db.get('counter');
257
- assert.strictEqual(result, 12);
258
- });
175
+ it('should add multiple items with unique IDs', async function () {
176
+ const p1 = await db.add('items', { value: 1 });
177
+ const p2 = await db.add('items', { value: 2 });
178
+ assert.notStrictEqual(p1[1], p2[1]);
179
+ assert.deepStrictEqual(await db.get(...p1), { value: 1 });
180
+ assert.deepStrictEqual(await db.get(...p2), { value: 2 });
181
+ });
259
182
 
260
- it('should increment nested value', async function() {
261
- await db.set('user', 'balance', 100);
262
- await db.inc('user', 'balance', 50);
263
-
264
- const balance = await db.get('user', 'balance');
265
- assert.strictEqual(balance, 150);
183
+ it('should add items at nested paths', async function () {
184
+ const p = await db.add('categories', 'electronics', 'items', { name: 'Laptop' });
185
+ assert.strictEqual(p.length, 4);
186
+ assert.deepStrictEqual(await db.get(...p), { name: 'Laptop' });
187
+ });
266
188
  });
267
189
 
268
- it('should handle negative increments', async function() {
269
- await db.set('counter', 10);
270
- await db.inc('counter', -3);
271
-
272
- const result = await db.get('counter');
273
- assert.strictEqual(result, 7);
274
- });
275
- });
190
+ describe('Increment/Decrement', function () {
191
+ it('should increment a value', async function () {
192
+ await db.set('counter', 10);
193
+ await db.inc('counter', 5);
194
+ assert.strictEqual(await db.get('counter'), 15);
195
+ });
276
196
 
277
- describe('Update Operation', function() {
278
- it('should update value with function', async function() {
279
- await db.set('name', 'alice');
280
- await db.upd('name', name => name.toUpperCase());
281
-
282
- const result = await db.get('name');
283
- assert.strictEqual(result, 'ALICE');
284
- });
197
+ it('should decrement a value', async function () {
198
+ await db.set('counter', 20);
199
+ await db.dec('counter', 8);
200
+ assert.strictEqual(await db.get('counter'), 12);
201
+ });
285
202
 
286
- it('should update nested value with function', async function() {
287
- await db.set('user', 'age', 25);
288
- await db.upd('user', 'age', age => age + 1);
289
-
290
- const age = await db.get('user', 'age');
291
- assert.strictEqual(age, 26);
292
- });
203
+ it('should increment nested value', async function () {
204
+ await db.set('user', 'balance', 100);
205
+ await db.inc('user', 'balance', 50);
206
+ assert.strictEqual(await db.get('user', 'balance'), 150);
207
+ });
293
208
 
294
- it('should update object with function', async function() {
295
- await db.set('user', { name: 'Alice', age: 30 });
296
- await db.upd('user', user => ({ ...user, age: user.age + 1 }));
297
-
298
- const user = await db.get('user');
299
- assert.deepStrictEqual(user, { name: 'Alice', age: 31 });
209
+ it('should handle negative increments', async function () {
210
+ await db.set('counter', 10);
211
+ await db.inc('counter', -3);
212
+ assert.strictEqual(await db.get('counter'), 7);
213
+ });
300
214
  });
301
- });
302
215
 
303
- describe('Keys, Values, Entries', function() {
304
- beforeEach(async function() {
305
- await db.set('users', 'alice', { age: 30 });
306
- await db.set('users', 'bob', { age: 25 });
307
- });
216
+ describe('Update Operation', function () {
217
+ it('should update value with function', async function () {
218
+ await db.set('name', 'alice');
219
+ await db.upd('name', name => name.toUpperCase());
220
+ assert.strictEqual(await db.get('name'), 'ALICE');
221
+ });
308
222
 
309
- it('should get keys', async function() {
310
- const keys = await db.keys('users');
311
- assert.deepStrictEqual(keys.sort(), ['alice', 'bob']);
312
- });
223
+ it('should update nested value with function', async function () {
224
+ await db.set('user', 'age', 25);
225
+ await db.upd('user', 'age', age => age + 1);
226
+ assert.strictEqual(await db.get('user', 'age'), 26);
227
+ });
313
228
 
314
- it('should get values', async function() {
315
- const values = await db.values('users');
316
- assert.strictEqual(values.length, 2);
317
- assert.ok(values.some(v => v.age === 30));
318
- assert.ok(values.some(v => v.age === 25));
229
+ it('should update object with function', async function () {
230
+ await db.set('user', { name: 'Alice', age: 30 });
231
+ await db.upd('user', user => ({ ...user, age: user.age + 1 }));
232
+ assert.deepStrictEqual(await db.get('user'), { name: 'Alice', age: 31 });
233
+ });
319
234
  });
320
235
 
321
- it('should get entries', async function() {
322
- const entries = await db.entries('users');
323
- assert.strictEqual(entries.length, 2);
324
- assert.ok(entries.some(([k, v]) => k === 'alice' && v.age === 30));
325
- assert.ok(entries.some(([k, v]) => k === 'bob' && v.age === 25));
326
- });
236
+ describe('Keys, Values, Entries', function () {
237
+ beforeEach(async function () {
238
+ await db.set('users', 'alice', { age: 30 });
239
+ await db.set('users', 'bob', { age: 25 });
240
+ });
327
241
 
328
- it('should return empty arrays for non-object values', async function() {
329
- await db.set('simple', 'string');
330
-
331
- const keys = await db.keys('simple');
332
- const values = await db.values('simple');
333
- const entries = await db.entries('simple');
334
-
335
- assert.deepStrictEqual(keys, []);
336
- assert.deepStrictEqual(values, []);
337
- assert.deepStrictEqual(entries, []);
338
- });
339
- });
242
+ it('should get keys', async function () {
243
+ assert.deepStrictEqual((await db.keys('users')).sort(), ['alice', 'bob']);
244
+ });
340
245
 
341
- describe('Persistence', function() {
342
- it('should persist data to database file', async function() {
343
- // Use a separate db for this test
344
- const persistDb = new DeepBase(new SqliteDriver({
345
- name: 'persist-test',
346
- path: testDataPath
347
- }));
348
- await persistDb.connect();
349
- await persistDb.set('persistent', 'data');
350
- await persistDb.disconnect();
351
-
352
- const filePath = path.join(testDataPath, 'persist-test.db');
353
- assert.ok(fs.existsSync(filePath));
354
- });
246
+ it('should get values', async function () {
247
+ const values = await db.values('users');
248
+ assert.strictEqual(values.length, 2);
249
+ assert.ok(values.some(v => v.age === 30));
250
+ assert.ok(values.some(v => v.age === 25));
251
+ });
355
252
 
356
- it('should load existing data on connect', async function() {
357
- // Use a separate db for this test
358
- const db1 = new DeepBase(new SqliteDriver({
359
- name: 'reload-test',
360
- path: testDataPath
361
- }));
362
- await db1.connect();
363
- await db1.set('existing', 'value');
364
- await db1.set('nested', 'key', 'data');
365
- await db1.disconnect();
366
-
367
- // Create new instance pointing to same file
368
- const db2 = new DeepBase(new SqliteDriver({
369
- name: 'reload-test',
370
- path: testDataPath
371
- }));
372
- await db2.connect();
373
-
374
- const result = await db2.get('existing');
375
- assert.strictEqual(result, 'value');
376
-
377
- const nested = await db2.get('nested', 'key');
378
- assert.strictEqual(nested, 'data');
379
-
380
- await db2.disconnect();
381
- });
253
+ it('should get entries', async function () {
254
+ const entries = await db.entries('users');
255
+ assert.strictEqual(entries.length, 2);
256
+ assert.ok(entries.some(([k, v]) => k === 'alice' && v.age === 30));
257
+ assert.ok(entries.some(([k, v]) => k === 'bob' && v.age === 25));
258
+ });
382
259
 
383
- it('should handle reconnection', async function() {
384
- await db.set('before', 'disconnect');
385
- await db.disconnect();
386
-
387
- await db.connect();
388
- const result = await db.get('before');
389
- assert.strictEqual(result, 'disconnect');
260
+ it('should return empty arrays for non-object values', async function () {
261
+ await db.set('simple', 'string');
262
+ assert.deepStrictEqual(await db.keys('simple'), []);
263
+ assert.deepStrictEqual(await db.values('simple'), []);
264
+ assert.deepStrictEqual(await db.entries('simple'), []);
265
+ });
390
266
  });
391
267
 
392
- it('should be a no-op when already connected', async function() {
393
- await db.set('key', 'value');
394
-
395
- // connect again while already connected
396
- await db.connect();
397
-
398
- // data and operations should be unaffected
399
- const result = await db.get('key');
400
- assert.strictEqual(result, 'value');
401
-
402
- await db.set('key2', 'value2');
403
- assert.strictEqual(await db.get('key2'), 'value2');
404
- });
405
- });
268
+ describe('Persistence', function () {
269
+ it('should persist data to database file', async function () {
270
+ const persistDb = new DeepBase(new SqliteDriver({
271
+ name: `persist-${pragma}`,
272
+ path: testDataPath,
273
+ pragma,
274
+ }));
275
+ await persistDb.connect();
276
+ await persistDb.set('persistent', 'data');
277
+ await persistDb.disconnect();
278
+ assert.ok(fs.existsSync(path.join(testDataPath, `persist-${pragma}.db`)));
279
+ });
406
280
 
407
- describe('Singleton Pattern', function() {
408
- it('should return same instance for same file', function() {
409
- const driver1 = new SqliteDriver({ name: 'singleton', path: testDataPath });
410
- const driver2 = new SqliteDriver({ name: 'singleton', path: testDataPath });
411
-
412
- assert.strictEqual(driver1, driver2);
413
- });
281
+ it('should load existing data on connect', async function () {
282
+ const db1 = new DeepBase(new SqliteDriver({
283
+ name: `reload-${pragma}`,
284
+ path: testDataPath,
285
+ pragma,
286
+ }));
287
+ await db1.connect();
288
+ await db1.set('existing', 'value');
289
+ await db1.set('nested', 'key', 'data');
290
+ await db1.disconnect();
291
+
292
+ const db2 = new DeepBase(new SqliteDriver({
293
+ name: `reload-${pragma}`,
294
+ path: testDataPath,
295
+ pragma,
296
+ }));
297
+ await db2.connect();
298
+ assert.strictEqual(await db2.get('existing'), 'value');
299
+ assert.strictEqual(await db2.get('nested', 'key'), 'data');
300
+ await db2.disconnect();
301
+ });
414
302
 
415
- it('should return different instances for different files', function() {
416
- const driver1 = new SqliteDriver({ name: 'file1', path: testDataPath });
417
- const driver2 = new SqliteDriver({ name: 'file2', path: testDataPath });
418
-
419
- assert.notStrictEqual(driver1, driver2);
420
- });
421
- });
303
+ it('should handle reconnection', async function () {
304
+ await db.set('before', 'disconnect');
305
+ await db.disconnect();
306
+ await db.connect();
307
+ assert.strictEqual(await db.get('before'), 'disconnect');
308
+ });
422
309
 
423
- describe('Root Object Operations', function() {
424
- it('should set entire root object', async function() {
425
- const data = {
426
- users: { alice: { age: 30 }, bob: { age: 25 } },
427
- config: { theme: 'dark', lang: 'en' }
428
- };
429
-
430
- await db.set(data);
431
- const result = await db.get();
432
- assert.deepStrictEqual(result, data);
310
+ it('should be a no-op when already connected', async function () {
311
+ await db.set('key', 'value');
312
+ await db.connect();
313
+ assert.strictEqual(await db.get('key'), 'value');
314
+ await db.set('key2', 'value2');
315
+ assert.strictEqual(await db.get('key2'), 'value2');
316
+ });
433
317
  });
434
318
 
435
- it('should get entire root object', async function() {
436
- await db.set('key1', 'value1');
437
- await db.set('key2', 'value2');
438
- await db.set('nested', 'key', 'value');
439
-
440
- const result = await db.get();
441
- assert.deepStrictEqual(result, {
442
- key1: 'value1',
443
- key2: 'value2',
444
- nested: { key: 'value' }
319
+ describe('Singleton Pattern', function () {
320
+ it('should return same instance for same file', function () {
321
+ const d1 = new SqliteDriver({ name: `singleton-${pragma}`, path: testDataPath, pragma });
322
+ const d2 = new SqliteDriver({ name: `singleton-${pragma}`, path: testDataPath, pragma });
323
+ assert.strictEqual(d1, d2);
445
324
  });
446
- });
447
325
 
448
- it('should replace root object on set', async function() {
449
- await db.set('old', 'data');
450
-
451
- const newData = { new: 'data' };
452
- await db.set(newData);
453
-
454
- const result = await db.get();
455
- assert.deepStrictEqual(result, newData);
456
-
457
- const old = await db.get('old');
458
- assert.strictEqual(old, null);
326
+ it('should return different instances for different files', function () {
327
+ const d1 = new SqliteDriver({ name: `file1-${pragma}`, path: testDataPath, pragma });
328
+ const d2 = new SqliteDriver({ name: `file2-${pragma}`, path: testDataPath, pragma });
329
+ assert.notStrictEqual(d1, d2);
330
+ });
459
331
  });
460
- });
461
332
 
462
- describe('Deep Nesting', function() {
463
- it('should handle deeply nested paths', async function() {
464
- await db.set('a', 'b', 'c', 'd', 'e', 'deep value');
465
- const result = await db.get('a', 'b', 'c', 'd', 'e');
466
- assert.strictEqual(result, 'deep value');
467
- });
333
+ describe('Root Object Operations', function () {
334
+ it('should set entire root object', async function () {
335
+ const data = {
336
+ users: { alice: { age: 30 }, bob: { age: 25 } },
337
+ config: { theme: 'dark', lang: 'en' },
338
+ };
339
+ await db.set(data);
340
+ assert.deepStrictEqual(await db.get(), data);
341
+ });
468
342
 
469
- it('should get partial deep objects', async function() {
470
- await db.set('a', 'b', 'c', 'value1');
471
- await db.set('a', 'b', 'd', 'value2');
472
- await db.set('a', 'e', 'value3');
473
-
474
- const resultB = await db.get('a', 'b');
475
- assert.deepStrictEqual(resultB, { c: 'value1', d: 'value2' });
476
-
477
- const resultA = await db.get('a');
478
- assert.deepStrictEqual(resultA, {
479
- b: { c: 'value1', d: 'value2' },
480
- e: 'value3'
343
+ it('should get entire root object', async function () {
344
+ await db.set('key1', 'value1');
345
+ await db.set('key2', 'value2');
346
+ await db.set('nested', 'key', 'value');
347
+ assert.deepStrictEqual(await db.get(), {
348
+ key1: 'value1',
349
+ key2: 'value2',
350
+ nested: { key: 'value' },
351
+ });
481
352
  });
482
- });
483
- });
484
353
 
485
- describe('Overwriting Object with Nested Properties', function() {
486
- it('should handle setting object first then nested properties', async function() {
487
- // This is the scenario from example 05-sqlite.js
488
- await db.set('config', { lang: 'en', theme: 'dark' });
489
- await db.set('config', 'lang', 'en');
490
- await db.set('config', 'theme', 'light');
491
-
492
- const config = await db.get('config');
493
- assert.deepStrictEqual(config, { lang: 'en', theme: 'light' });
354
+ it('should replace root object on set', async function () {
355
+ await db.set('old', 'data');
356
+ await db.set({ new: 'data' });
357
+ assert.deepStrictEqual(await db.get(), { new: 'data' });
358
+ assert.strictEqual(await db.get('old'), null);
359
+ });
494
360
  });
495
361
 
496
- it('should prioritize nested properties over initial object', async function() {
497
- await db.set('settings', { a: 1, b: 2, c: 3 });
498
- await db.set('settings', 'b', 99);
499
- await db.set('settings', 'd', 4);
500
-
501
- const settings = await db.get('settings');
502
- assert.deepStrictEqual(settings, { a: 1, b: 99, c: 3, d: 4 });
503
- });
362
+ describe('Deep Nesting', function () {
363
+ it('should handle deeply nested paths', async function () {
364
+ await db.set('a', 'b', 'c', 'd', 'e', 'deep value');
365
+ assert.strictEqual(await db.get('a', 'b', 'c', 'd', 'e'), 'deep value');
366
+ });
504
367
 
505
- it('should handle nested object then deeper nesting', async function() {
506
- await db.set('user', { name: 'Alice', meta: { role: 'admin' } });
507
- await db.set('user', 'meta', 'role', 'user');
508
- await db.set('user', 'meta', 'active', true);
509
-
510
- const user = await db.get('user');
511
- assert.deepStrictEqual(user, {
512
- name: 'Alice',
513
- meta: { role: 'user', active: true }
368
+ it('should get partial deep objects', async function () {
369
+ await db.set('a', 'b', 'c', 'value1');
370
+ await db.set('a', 'b', 'd', 'value2');
371
+ await db.set('a', 'e', 'value3');
372
+ assert.deepStrictEqual(await db.get('a', 'b'), { c: 'value1', d: 'value2' });
373
+ assert.deepStrictEqual(await db.get('a'), {
374
+ b: { c: 'value1', d: 'value2' },
375
+ e: 'value3',
376
+ });
514
377
  });
515
378
  });
516
- });
517
379
 
518
- describe('Object Expansion with Special Keys', function() {
519
- it('should handle object with dotted keys when expanding', async function() {
520
- // Store an object with dots in its property names
521
- await db.set('config', { 'server.port': 8080, 'server.host': 'localhost' });
522
-
523
- // Trigger expansion by setting a nested property
524
- await db.set('config', 'debug', true);
525
-
526
- const config = await db.get('config');
527
- assert.strictEqual(config['server.port'], 8080);
528
- assert.strictEqual(config['server.host'], 'localhost');
529
- assert.strictEqual(config.debug, true);
530
- });
380
+ describe('Overwriting Object with Nested Properties', function () {
381
+ it('should handle setting object first then nested properties', async function () {
382
+ await db.set('config', { lang: 'en', theme: 'dark' });
383
+ await db.set('config', 'lang', 'en');
384
+ await db.set('config', 'theme', 'light');
385
+ assert.deepStrictEqual(await db.get('config'), { lang: 'en', theme: 'light' });
386
+ });
531
387
 
532
- it('should not crash on objects with empty string keys', async function() {
533
- // This was the root cause of the production crash:
534
- // empty string key produces trailing dot in DB, leading to empty path
535
- await db.set('data', { '': 'empty_key_value', 'normal': 'ok' });
536
-
537
- // Trigger expansion
538
- await db.set('data', 'extra', 123);
539
-
540
- const data = await db.get('data');
541
- assert.strictEqual(data[''], 'empty_key_value');
542
- assert.strictEqual(data.normal, 'ok');
543
- assert.strictEqual(data.extra, 123);
544
- });
388
+ it('should prioritize nested properties over initial object', async function () {
389
+ await db.set('settings', { a: 1, b: 2, c: 3 });
390
+ await db.set('settings', 'b', 99);
391
+ await db.set('settings', 'd', 4);
392
+ assert.deepStrictEqual(await db.get('settings'), { a: 1, b: 99, c: 3, d: 4 });
393
+ });
545
394
 
546
- it('should handle nested objects with dotted keys during expansion', async function() {
547
- await db.set('replace', {
548
- '{lang}': 'es',
549
- '{first_name}': 'Martin',
550
- '@main': 'martin'
551
- });
552
-
553
- // Trigger expansion
554
- await db.set('replace', '{lang}', 'en');
555
-
556
- const result = await db.get('replace');
557
- assert.strictEqual(result['{lang}'], 'en');
558
- assert.strictEqual(result['{first_name}'], 'Martin');
559
- assert.strictEqual(result['@main'], 'martin');
395
+ it('should handle nested object then deeper nesting', async function () {
396
+ await db.set('user', { name: 'Alice', meta: { role: 'admin' } });
397
+ await db.set('user', 'meta', 'role', 'user');
398
+ await db.set('user', 'meta', 'active', true);
399
+ assert.deepStrictEqual(await db.get('user'), {
400
+ name: 'Alice',
401
+ meta: { role: 'user', active: true },
402
+ });
403
+ });
560
404
  });
561
405
 
562
- it('should preserve dots in keys through set/get root object cycle', async function() {
563
- const data = {
564
- 'api.v1.endpoint': 'https://api.example.com',
565
- 'api.v2.endpoint': 'https://v2.api.example.com'
566
- };
567
-
568
- await db.set(data);
569
- const result = await db.get();
570
- assert.deepStrictEqual(result, data);
571
- });
572
- });
406
+ describe('Object Expansion with Special Keys', function () {
407
+ it('should handle object with dotted keys when expanding', async function () {
408
+ await db.set('config', { 'server.port': 8080, 'server.host': 'localhost' });
409
+ await db.set('config', 'debug', true);
410
+ const config = await db.get('config');
411
+ assert.strictEqual(config['server.port'], 8080);
412
+ assert.strictEqual(config['server.host'], 'localhost');
413
+ assert.strictEqual(config.debug, true);
414
+ });
573
415
 
574
- describe('Keys with Underscores (SQL LIKE safety)', function() {
575
- it('should not cross-match keys with underscores', async function() {
576
- // _ is a SQL LIKE wildcard that matches any single character
577
- await db.set('chat_1', 'msg', 'hello');
578
- await db.set('chatX1', 'msg', 'world');
579
-
580
- const chat1 = await db.get('chat_1');
581
- assert.deepStrictEqual(chat1, { msg: 'hello' });
582
-
583
- const chatX1 = await db.get('chatX1');
584
- assert.deepStrictEqual(chatX1, { msg: 'world' });
585
- });
416
+ it('should not crash on objects with empty string keys', async function () {
417
+ await db.set('data', { '': 'empty_key_value', normal: 'ok' });
418
+ await db.set('data', 'extra', 123);
419
+ const data = await db.get('data');
420
+ assert.strictEqual(data[''], 'empty_key_value');
421
+ assert.strictEqual(data.normal, 'ok');
422
+ assert.strictEqual(data.extra, 123);
423
+ });
586
424
 
587
- it('should isolate data between similar keys with underscores', async function() {
588
- await db.set('user_42', 'name', 'Alice');
589
- await db.set('userX42', 'name', 'Bob');
590
- await db.set('user.42', 'name', 'Charlie');
591
-
592
- const u1 = await db.get('user_42');
593
- const u2 = await db.get('userX42');
594
- const u3 = await db.get('user.42');
595
-
596
- assert.deepStrictEqual(u1, { name: 'Alice' });
597
- assert.deepStrictEqual(u2, { name: 'Bob' });
598
- assert.deepStrictEqual(u3, { name: 'Charlie' });
599
- });
425
+ it('should handle nested objects with dotted keys during expansion', async function () {
426
+ await db.set('replace', {
427
+ '{lang}': 'es',
428
+ '{first_name}': 'Martin',
429
+ '@main': 'martin',
430
+ });
431
+ await db.set('replace', '{lang}', 'en');
432
+ const result = await db.get('replace');
433
+ assert.strictEqual(result['{lang}'], 'en');
434
+ assert.strictEqual(result['{first_name}'], 'Martin');
435
+ assert.strictEqual(result['@main'], 'martin');
436
+ });
600
437
 
601
- it('should delete only matching keys with underscores', async function() {
602
- await db.set('item_a', 'x', 1);
603
- await db.set('itemXa', 'x', 2);
604
-
605
- await db.del('item_a');
606
-
607
- assert.strictEqual(await db.get('item_a'), null);
608
- assert.deepStrictEqual(await db.get('itemXa'), { x: 2 });
438
+ it('should preserve dots in keys through set/get root object cycle', async function () {
439
+ const data = {
440
+ 'api.v1.endpoint': 'https://api.example.com',
441
+ 'api.v2.endpoint': 'https://v2.api.example.com',
442
+ };
443
+ await db.set(data);
444
+ assert.deepStrictEqual(await db.get(), data);
445
+ });
609
446
  });
610
447
 
611
- it('should handle keys with percent signs', async function() {
612
- await db.set('progress', '100%', 'done', true);
613
- await db.set('progress', 'abc', 'done', false);
614
-
615
- const p100 = await db.get('progress', '100%');
616
- assert.deepStrictEqual(p100, { done: true });
617
-
618
- const pabc = await db.get('progress', 'abc');
619
- assert.deepStrictEqual(pabc, { done: false });
620
- });
621
- });
448
+ describe('Keys with Underscores (SQL LIKE safety)', function () {
449
+ it('should not cross-match keys with underscores', async function () {
450
+ await db.set('chat_1', 'msg', 'hello');
451
+ await db.set('chatX1', 'msg', 'world');
452
+ assert.deepStrictEqual(await db.get('chat_1'), { msg: 'hello' });
453
+ assert.deepStrictEqual(await db.get('chatX1'), { msg: 'world' });
454
+ });
622
455
 
623
- describe('Storybot-like Structure', function() {
624
- it('should handle dotted top-level keys with deep nesting', async function() {
625
- const sessionKey = 'stm.1769201539421.dawduxooy';
626
-
627
- await db.set(sessionKey, 'started', 'LuaGardenbot', true);
628
- await db.set(sessionKey, 'active', 'LuaGardenbot', true);
629
- await db.set(sessionKey, 'active', 'AmyWaybot', true);
630
-
631
- const session = await db.get(sessionKey);
632
- assert.deepStrictEqual(session, {
633
- started: { LuaGardenbot: true },
634
- active: { LuaGardenbot: true, AmyWaybot: true }
456
+ it('should isolate data between similar keys with underscores', async function () {
457
+ await db.set('user_42', 'name', 'Alice');
458
+ await db.set('userX42', 'name', 'Bob');
459
+ await db.set('user.42', 'name', 'Charlie');
460
+ assert.deepStrictEqual(await db.get('user_42'), { name: 'Alice' });
461
+ assert.deepStrictEqual(await db.get('userX42'), { name: 'Bob' });
462
+ assert.deepStrictEqual(await db.get('user.42'), { name: 'Charlie' });
635
463
  });
636
- });
637
464
 
638
- it('should handle replace map with special chars in keys', async function() {
639
- const sessionKey = 'stm.12345.abc';
640
-
641
- await db.set(sessionKey, 'replace', {
642
- '{lang}': 'es',
643
- '{vip_token}': '',
644
- '{first_name}': 'STM User',
645
- '{last_name}': '',
646
- '{crypto_name}': 'kkerfhkkzgf',
647
- '@main': 'martin'
648
- });
649
-
650
- const replace = await db.get(sessionKey, 'replace');
651
- assert.strictEqual(replace['{lang}'], 'es');
652
- assert.strictEqual(replace['@main'], 'martin');
653
- assert.strictEqual(replace['{first_name}'], 'STM User');
654
- });
465
+ it('should delete only matching keys with underscores', async function () {
466
+ await db.set('item_a', 'x', 1);
467
+ await db.set('itemXa', 'x', 2);
468
+ await db.del('item_a');
469
+ assert.strictEqual(await db.get('item_a'), null);
470
+ assert.deepStrictEqual(await db.get('itemXa'), { x: 2 });
471
+ });
655
472
 
656
- it('should handle node history with numeric-like keys', async function() {
657
- const sessionKey = 'stm.999.xyz';
658
-
659
- await db.set(sessionKey, 'node', 'history', 'c0', {
660
- from: 'LuaGardenbot',
661
- message: 'new contact',
662
- timestamp: 1771012955839
663
- });
664
- await db.set(sessionKey, 'node', 'history', '0-2760293686', {
665
- from: 'LuaGardenbot',
666
- message: 'Hey, I\'m Lua',
667
- timestamp: 1771012956863
668
- });
669
- await db.set(sessionKey, 'node', 'history', '0-4151942372-o', {
670
- from: 'main',
671
- to: 'LuaGardenbot',
672
- message: 'hola',
673
- timestamp: 1771012960058
674
- });
675
-
676
- const history = await db.get(sessionKey, 'node', 'history');
677
- assert.strictEqual(Object.keys(history).length, 3);
678
- assert.strictEqual(history['c0'].from, 'LuaGardenbot');
679
- assert.strictEqual(history['0-2760293686'].message, 'Hey, I\'m Lua');
680
- assert.strictEqual(history['0-4151942372-o'].to, 'LuaGardenbot');
473
+ it('should handle keys with percent signs', async function () {
474
+ await db.set('progress', '100%', 'done', true);
475
+ await db.set('progress', 'abc', 'done', false);
476
+ assert.deepStrictEqual(await db.get('progress', '100%'), { done: true });
477
+ assert.deepStrictEqual(await db.get('progress', 'abc'), { done: false });
478
+ });
681
479
  });
682
480
 
683
- it('should handle object expansion with replace map then set nested', async function() {
684
- const sessionKey = 'stm.12345.abc';
685
-
686
- // Store replace map as object
687
- await db.set(sessionKey, 'replace', {
688
- '{lang}': 'es',
689
- '{first_name}': 'User'
690
- });
691
-
692
- // Now update a single key — triggers _expandParentObjects
693
- await db.set(sessionKey, 'replace', '{lang}', 'en');
694
-
695
- const replace = await db.get(sessionKey, 'replace');
696
- assert.strictEqual(replace['{lang}'], 'en');
697
- assert.strictEqual(replace['{first_name}'], 'User');
698
- });
481
+ describe('Storybot-like Structure', function () {
482
+ it('should handle dotted top-level keys with deep nesting', async function () {
483
+ const sk = 'stm.1769201539421.dawduxooy';
484
+ await db.set(sk, 'started', 'LuaGardenbot', true);
485
+ await db.set(sk, 'active', 'LuaGardenbot', true);
486
+ await db.set(sk, 'active', 'AmyWaybot', true);
487
+ assert.deepStrictEqual(await db.get(sk), {
488
+ started: { LuaGardenbot: true },
489
+ active: { LuaGardenbot: true, AmyWaybot: true },
490
+ });
491
+ });
699
492
 
700
- it('should handle full session lifecycle without crash', async function() {
701
- const sessionKey = 'stm.1769201539421.dawduxooy';
702
-
703
- // Initial object-style set
704
- await db.set(sessionKey, 'replace', {
705
- '{lang}': 'es',
706
- '{vip_token}': '',
707
- '{first_name}': 'STM User'
708
- });
709
-
710
- await db.set(sessionKey, 'config', { country: '', gender: 'M' });
711
- await db.set(sessionKey, 'tokens', { input: 4554, output: 229, cost: 0.0079825 });
712
-
713
- // Nested individual sets
714
- await db.set(sessionKey, 'started', 'LuaGardenbot', true);
715
- await db.set(sessionKey, 'active', 'LuaGardenbot', true);
716
- await db.set(sessionKey, 'active', 'AmyWaybot', true);
717
- await db.set(sessionKey, 'node', 'current', 'chapter', 2);
718
- await db.set(sessionKey, 'node', 'current', 'id', '50-326833713');
719
-
720
- // Update inside previously stored object — triggers expansion
721
- await db.set(sessionKey, 'replace', '{lang}', 'en');
722
- await db.set(sessionKey, 'config', 'country', 'AR');
723
-
724
- // Verify everything
725
- const session = await db.get(sessionKey);
726
-
727
- assert.strictEqual(session.replace['{lang}'], 'en');
728
- assert.strictEqual(session.replace['{first_name}'], 'STM User');
729
- assert.strictEqual(session.config.country, 'AR');
730
- assert.strictEqual(session.config.gender, 'M');
731
- assert.strictEqual(session.tokens.cost, 0.0079825);
732
- assert.strictEqual(session.started.LuaGardenbot, true);
733
- assert.strictEqual(session.active.AmyWaybot, true);
734
- assert.strictEqual(session.node.current.chapter, 2);
735
- assert.strictEqual(session.node.current.id, '50-326833713');
736
- });
493
+ it('should handle replace map with special chars in keys', async function () {
494
+ const sk = 'stm.12345.abc';
495
+ await db.set(sk, 'replace', {
496
+ '{lang}': 'es',
497
+ '{vip_token}': '',
498
+ '{first_name}': 'STM User',
499
+ '{last_name}': '',
500
+ '{crypto_name}': 'kkerfhkkzgf',
501
+ '@main': 'martin',
502
+ });
503
+ const replace = await db.get(sk, 'replace');
504
+ assert.strictEqual(replace['{lang}'], 'es');
505
+ assert.strictEqual(replace['@main'], 'martin');
506
+ assert.strictEqual(replace['{first_name}'], 'STM User');
507
+ });
737
508
 
738
- it('should not mix sessions with similar dotted keys', async function() {
739
- await db.set('stm.111.aaa', 'data', 'session1');
740
- await db.set('stm.111.bbb', 'data', 'session2');
741
- await db.set('stm.222.aaa', 'data', 'session3');
742
-
743
- assert.deepStrictEqual(await db.get('stm.111.aaa'), { data: 'session1' });
744
- assert.deepStrictEqual(await db.get('stm.111.bbb'), { data: 'session2' });
745
- assert.deepStrictEqual(await db.get('stm.222.aaa'), { data: 'session3' });
746
- });
747
- });
509
+ it('should handle node history with numeric-like keys', async function () {
510
+ const sk = 'stm.999.xyz';
511
+ await db.set(sk, 'node', 'history', 'c0', {
512
+ from: 'LuaGardenbot', message: 'new contact', timestamp: 1771012955839,
513
+ });
514
+ await db.set(sk, 'node', 'history', '0-2760293686', {
515
+ from: 'LuaGardenbot', message: 'Hey, I\'m Lua', timestamp: 1771012956863,
516
+ });
517
+ await db.set(sk, 'node', 'history', '0-4151942372-o', {
518
+ from: 'main', to: 'LuaGardenbot', message: 'hola', timestamp: 1771012960058,
519
+ });
520
+ const history = await db.get(sk, 'node', 'history');
521
+ assert.strictEqual(Object.keys(history).length, 3);
522
+ assert.strictEqual(history['c0'].from, 'LuaGardenbot');
523
+ assert.strictEqual(history['0-2760293686'].message, 'Hey, I\'m Lua');
524
+ assert.strictEqual(history['0-4151942372-o'].to, 'LuaGardenbot');
525
+ });
748
526
 
749
- describe('Race Conditions', function() {
750
- it('should handle 100 concurrent increments correctly', async function() {
751
- this.timeout(5000);
752
-
753
- await db.set('counter', 0);
754
-
755
- // Run 100 concurrent increments
756
- const promises = Array.from({ length: 100 }, () => db.inc('counter', 1));
757
- await Promise.all(promises);
758
-
759
- const result = await db.get('counter');
760
- assert.strictEqual(result, 100, 'All increments should be applied atomically');
761
- });
527
+ it('should handle object expansion with replace map then set nested', async function () {
528
+ const sk = 'stm.12345.abc';
529
+ await db.set(sk, 'replace', { '{lang}': 'es', '{first_name}': 'User' });
530
+ await db.set(sk, 'replace', '{lang}', 'en');
531
+ const replace = await db.get(sk, 'replace');
532
+ assert.strictEqual(replace['{lang}'], 'en');
533
+ assert.strictEqual(replace['{first_name}'], 'User');
534
+ });
762
535
 
763
- it('should handle concurrent read-modify-write operations', async function() {
764
- this.timeout(5000);
765
-
766
- await db.set('data', { counter: 0, items: [] });
767
-
768
- // Run 50 concurrent updates that read, modify, and write
769
- const promises = Array.from({ length: 50 }, (_, i) =>
770
- db.upd('data', (current) => ({
771
- counter: current.counter + 1,
772
- items: [...current.items, `item-${current.counter}`]
773
- }))
774
- );
775
-
776
- await Promise.all(promises);
777
-
778
- const finalData = await db.get('data');
779
- assert.strictEqual(finalData.counter, 50, 'Counter should be exactly 50');
780
- assert.strictEqual(finalData.items.length, 50, 'Should have exactly 50 items');
781
-
782
- // Check that all items have unique values
783
- const uniqueItems = new Set(finalData.items);
784
- assert.strictEqual(uniqueItems.size, 50, 'All items should be unique');
785
- });
536
+ it('should handle full session lifecycle without crash', async function () {
537
+ const sk = 'stm.1769201539421.dawduxooy';
538
+ await db.set(sk, 'replace', { '{lang}': 'es', '{vip_token}': '', '{first_name}': 'STM User' });
539
+ await db.set(sk, 'config', { country: '', gender: 'M' });
540
+ await db.set(sk, 'tokens', { input: 4554, output: 229, cost: 0.0079825 });
541
+ await db.set(sk, 'started', 'LuaGardenbot', true);
542
+ await db.set(sk, 'active', 'LuaGardenbot', true);
543
+ await db.set(sk, 'active', 'AmyWaybot', true);
544
+ await db.set(sk, 'node', 'current', 'chapter', 2);
545
+ await db.set(sk, 'node', 'current', 'id', '50-326833713');
546
+ await db.set(sk, 'replace', '{lang}', 'en');
547
+ await db.set(sk, 'config', 'country', 'AR');
548
+ const session = await db.get(sk);
549
+ assert.strictEqual(session.replace['{lang}'], 'en');
550
+ assert.strictEqual(session.replace['{first_name}'], 'STM User');
551
+ assert.strictEqual(session.config.country, 'AR');
552
+ assert.strictEqual(session.config.gender, 'M');
553
+ assert.strictEqual(session.tokens.cost, 0.0079825);
554
+ assert.strictEqual(session.started.LuaGardenbot, true);
555
+ assert.strictEqual(session.active.AmyWaybot, true);
556
+ assert.strictEqual(session.node.current.chapter, 2);
557
+ assert.strictEqual(session.node.current.id, '50-326833713');
558
+ });
786
559
 
787
- it('should handle concurrent sets on different keys', async function() {
788
- this.timeout(5000);
789
-
790
- // Run 50 concurrent set operations on different keys
791
- const promises = Array.from({ length: 50 }, (_, i) =>
792
- db.set('users', `user${i}`, { name: `User ${i}`, value: i })
793
- );
794
-
795
- await Promise.all(promises);
796
-
797
- // Verify all keys exist and have correct values
798
- const users = await db.get('users');
799
- assert.strictEqual(Object.keys(users).length, 50, 'Should have 50 users');
800
-
801
- for (let i = 0; i < 50; i++) {
802
- assert.deepStrictEqual(
803
- users[`user${i}`],
804
- { name: `User ${i}`, value: i },
805
- `User ${i} should have correct data`
806
- );
807
- }
560
+ it('should not mix sessions with similar dotted keys', async function () {
561
+ await db.set('stm.111.aaa', 'data', 'session1');
562
+ await db.set('stm.111.bbb', 'data', 'session2');
563
+ await db.set('stm.222.aaa', 'data', 'session3');
564
+ assert.deepStrictEqual(await db.get('stm.111.aaa'), { data: 'session1' });
565
+ assert.deepStrictEqual(await db.get('stm.111.bbb'), { data: 'session2' });
566
+ assert.deepStrictEqual(await db.get('stm.222.aaa'), { data: 'session3' });
567
+ });
808
568
  });
809
569
 
810
- it('should handle concurrent add operations with unique IDs', async function() {
811
- this.timeout(5000);
812
-
813
- await db.set('items', {});
814
-
815
- // Run 50 concurrent add operations
816
- const promises = Array.from({ length: 50 }, (_, i) =>
817
- db.add('items', { value: i })
818
- );
819
-
820
- const results = await Promise.all(promises);
821
-
822
- // Verify all items were added with unique IDs
823
- const items = await db.get('items');
824
- assert.strictEqual(Object.keys(items).length, 50, 'Should have 50 items');
825
-
826
- // Check that all IDs are unique
827
- const ids = results.map(r => r[r.length - 1]);
828
- const uniqueIds = new Set(ids);
829
- assert.strictEqual(uniqueIds.size, 50, 'All IDs should be unique');
830
- });
570
+ describe('Race Conditions', function () {
571
+ it('should handle 100 concurrent increments correctly', async function () {
572
+ this.timeout(5000);
573
+ await db.set('counter', 0);
574
+ await Promise.all(Array.from({ length: 100 }, () => db.inc('counter', 1)));
575
+ assert.strictEqual(await db.get('counter'), 100);
576
+ });
831
577
 
832
- it('should handle concurrent decrements correctly', async function() {
833
- this.timeout(5000);
834
-
835
- await db.set('inventory', 1000);
836
-
837
- // Run 100 concurrent decrements
838
- const promises = Array.from({ length: 100 }, () => db.dec('inventory', 5));
839
- await Promise.all(promises);
840
-
841
- const result = await db.get('inventory');
842
- assert.strictEqual(result, 500, 'All decrements should be applied atomically');
843
- });
844
- });
845
- });
578
+ it('should handle concurrent read-modify-write operations', async function () {
579
+ this.timeout(5000);
580
+ await db.set('data', { counter: 0, items: [] });
581
+ await Promise.all(Array.from({ length: 50 }, () =>
582
+ db.upd('data', cur => ({
583
+ counter: cur.counter + 1,
584
+ items: [...cur.items, `item-${cur.counter}`],
585
+ })),
586
+ ));
587
+ const final = await db.get('data');
588
+ assert.strictEqual(final.counter, 50);
589
+ assert.strictEqual(final.items.length, 50);
590
+ assert.strictEqual(new Set(final.items).size, 50);
591
+ });
846
592
 
593
+ it('should handle concurrent sets on different keys', async function () {
594
+ this.timeout(5000);
595
+ await Promise.all(Array.from({ length: 50 }, (_, i) =>
596
+ db.set('users', `user${i}`, { name: `User ${i}`, value: i }),
597
+ ));
598
+ const users = await db.get('users');
599
+ assert.strictEqual(Object.keys(users).length, 50);
600
+ for (let i = 0; i < 50; i++) {
601
+ assert.deepStrictEqual(users[`user${i}`], { name: `User ${i}`, value: i });
602
+ }
603
+ });
847
604
 
605
+ it('should handle concurrent add operations with unique IDs', async function () {
606
+ this.timeout(5000);
607
+ await db.set('items', {});
608
+ const results = await Promise.all(Array.from({ length: 50 }, (_, i) =>
609
+ db.add('items', { value: i }),
610
+ ));
611
+ const items = await db.get('items');
612
+ assert.strictEqual(Object.keys(items).length, 50);
613
+ assert.strictEqual(new Set(results.map(r => r[r.length - 1])).size, 50);
614
+ });
615
+
616
+ it('should handle concurrent decrements correctly', async function () {
617
+ this.timeout(5000);
618
+ await db.set('inventory', 1000);
619
+ await Promise.all(Array.from({ length: 100 }, () => db.dec('inventory', 5)));
620
+ assert.strictEqual(await db.get('inventory'), 500);
621
+ });
622
+ });
623
+ });
624
+ }