deepbase-sqlite 3.4.11 → 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,826 +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
- });
392
267
 
393
- describe('Singleton Pattern', function() {
394
- it('should return same instance for same file', function() {
395
- const driver1 = new SqliteDriver({ name: 'singleton', path: testDataPath });
396
- const driver2 = new SqliteDriver({ name: 'singleton', path: testDataPath });
397
-
398
- assert.strictEqual(driver1, driver2);
399
- });
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
+ });
400
280
 
401
- it('should return different instances for different files', function() {
402
- const driver1 = new SqliteDriver({ name: 'file1', path: testDataPath });
403
- const driver2 = new SqliteDriver({ name: 'file2', path: testDataPath });
404
-
405
- assert.notStrictEqual(driver1, driver2);
406
- });
407
- });
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
+ });
408
302
 
409
- describe('Root Object Operations', function() {
410
- it('should set entire root object', async function() {
411
- const data = {
412
- users: { alice: { age: 30 }, bob: { age: 25 } },
413
- config: { theme: 'dark', lang: 'en' }
414
- };
415
-
416
- await db.set(data);
417
- const result = await db.get();
418
- assert.deepStrictEqual(result, data);
419
- });
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
+ });
420
309
 
421
- it('should get entire root object', async function() {
422
- await db.set('key1', 'value1');
423
- await db.set('key2', 'value2');
424
- await db.set('nested', 'key', 'value');
425
-
426
- const result = await db.get();
427
- assert.deepStrictEqual(result, {
428
- key1: 'value1',
429
- key2: 'value2',
430
- nested: { key: 'value' }
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');
431
316
  });
432
317
  });
433
318
 
434
- it('should replace root object on set', async function() {
435
- await db.set('old', 'data');
436
-
437
- const newData = { new: 'data' };
438
- await db.set(newData);
439
-
440
- const result = await db.get();
441
- assert.deepStrictEqual(result, newData);
442
-
443
- const old = await db.get('old');
444
- assert.strictEqual(old, null);
445
- });
446
- });
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);
324
+ });
447
325
 
448
- describe('Deep Nesting', function() {
449
- it('should handle deeply nested paths', async function() {
450
- await db.set('a', 'b', 'c', 'd', 'e', 'deep value');
451
- const result = await db.get('a', 'b', 'c', 'd', 'e');
452
- assert.strictEqual(result, 'deep value');
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
+ });
453
331
  });
454
332
 
455
- it('should get partial deep objects', async function() {
456
- await db.set('a', 'b', 'c', 'value1');
457
- await db.set('a', 'b', 'd', 'value2');
458
- await db.set('a', 'e', 'value3');
459
-
460
- const resultB = await db.get('a', 'b');
461
- assert.deepStrictEqual(resultB, { c: 'value1', d: 'value2' });
462
-
463
- const resultA = await db.get('a');
464
- assert.deepStrictEqual(resultA, {
465
- b: { c: 'value1', d: 'value2' },
466
- e: 'value3'
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);
467
341
  });
468
- });
469
- });
470
342
 
471
- describe('Overwriting Object with Nested Properties', function() {
472
- it('should handle setting object first then nested properties', async function() {
473
- // This is the scenario from example 05-sqlite.js
474
- await db.set('config', { lang: 'en', theme: 'dark' });
475
- await db.set('config', 'lang', 'en');
476
- await db.set('config', 'theme', 'light');
477
-
478
- const config = await db.get('config');
479
- assert.deepStrictEqual(config, { lang: 'en', theme: 'light' });
480
- });
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
+ });
352
+ });
481
353
 
482
- it('should prioritize nested properties over initial object', async function() {
483
- await db.set('settings', { a: 1, b: 2, c: 3 });
484
- await db.set('settings', 'b', 99);
485
- await db.set('settings', 'd', 4);
486
-
487
- const settings = await db.get('settings');
488
- assert.deepStrictEqual(settings, { a: 1, b: 99, c: 3, d: 4 });
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
+ });
489
360
  });
490
361
 
491
- it('should handle nested object then deeper nesting', async function() {
492
- await db.set('user', { name: 'Alice', meta: { role: 'admin' } });
493
- await db.set('user', 'meta', 'role', 'user');
494
- await db.set('user', 'meta', 'active', true);
495
-
496
- const user = await db.get('user');
497
- assert.deepStrictEqual(user, {
498
- name: 'Alice',
499
- meta: { role: 'user', active: true }
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');
500
366
  });
501
- });
502
- });
503
367
 
504
- describe('Object Expansion with Special Keys', function() {
505
- it('should handle object with dotted keys when expanding', async function() {
506
- // Store an object with dots in its property names
507
- await db.set('config', { 'server.port': 8080, 'server.host': 'localhost' });
508
-
509
- // Trigger expansion by setting a nested property
510
- await db.set('config', 'debug', true);
511
-
512
- const config = await db.get('config');
513
- assert.strictEqual(config['server.port'], 8080);
514
- assert.strictEqual(config['server.host'], 'localhost');
515
- assert.strictEqual(config.debug, 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
+ });
377
+ });
516
378
  });
517
379
 
518
- it('should not crash on objects with empty string keys', async function() {
519
- // This was the root cause of the production crash:
520
- // empty string key produces trailing dot in DB, leading to empty path
521
- await db.set('data', { '': 'empty_key_value', 'normal': 'ok' });
522
-
523
- // Trigger expansion
524
- await db.set('data', 'extra', 123);
525
-
526
- const data = await db.get('data');
527
- assert.strictEqual(data[''], 'empty_key_value');
528
- assert.strictEqual(data.normal, 'ok');
529
- assert.strictEqual(data.extra, 123);
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 handle nested objects with dotted keys during expansion', async function() {
533
- await db.set('replace', {
534
- '{lang}': 'es',
535
- '{first_name}': 'Martin',
536
- '@main': 'martin'
537
- });
538
-
539
- // Trigger expansion
540
- await db.set('replace', '{lang}', 'en');
541
-
542
- const result = await db.get('replace');
543
- assert.strictEqual(result['{lang}'], 'en');
544
- assert.strictEqual(result['{first_name}'], 'Martin');
545
- assert.strictEqual(result['@main'], 'martin');
546
- });
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
+ });
547
394
 
548
- it('should preserve dots in keys through set/get root object cycle', async function() {
549
- const data = {
550
- 'api.v1.endpoint': 'https://api.example.com',
551
- 'api.v2.endpoint': 'https://v2.api.example.com'
552
- };
553
-
554
- await db.set(data);
555
- const result = await db.get();
556
- assert.deepStrictEqual(result, data);
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
+ });
557
404
  });
558
- });
559
405
 
560
- describe('Keys with Underscores (SQL LIKE safety)', function() {
561
- it('should not cross-match keys with underscores', async function() {
562
- // _ is a SQL LIKE wildcard that matches any single character
563
- await db.set('chat_1', 'msg', 'hello');
564
- await db.set('chatX1', 'msg', 'world');
565
-
566
- const chat1 = await db.get('chat_1');
567
- assert.deepStrictEqual(chat1, { msg: 'hello' });
568
-
569
- const chatX1 = await db.get('chatX1');
570
- assert.deepStrictEqual(chatX1, { msg: 'world' });
571
- });
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
+ });
572
415
 
573
- it('should isolate data between similar keys with underscores', async function() {
574
- await db.set('user_42', 'name', 'Alice');
575
- await db.set('userX42', 'name', 'Bob');
576
- await db.set('user.42', 'name', 'Charlie');
577
-
578
- const u1 = await db.get('user_42');
579
- const u2 = await db.get('userX42');
580
- const u3 = await db.get('user.42');
581
-
582
- assert.deepStrictEqual(u1, { name: 'Alice' });
583
- assert.deepStrictEqual(u2, { name: 'Bob' });
584
- assert.deepStrictEqual(u3, { name: 'Charlie' });
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 delete only matching keys with underscores', async function() {
588
- await db.set('item_a', 'x', 1);
589
- await db.set('itemXa', 'x', 2);
590
-
591
- await db.del('item_a');
592
-
593
- assert.strictEqual(await db.get('item_a'), null);
594
- assert.deepStrictEqual(await db.get('itemXa'), { x: 2 });
595
- });
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
+ });
596
437
 
597
- it('should handle keys with percent signs', async function() {
598
- await db.set('progress', '100%', 'done', true);
599
- await db.set('progress', 'abc', 'done', false);
600
-
601
- const p100 = await db.get('progress', '100%');
602
- assert.deepStrictEqual(p100, { done: true });
603
-
604
- const pabc = await db.get('progress', 'abc');
605
- assert.deepStrictEqual(pabc, { done: false });
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
+ });
606
446
  });
607
- });
608
447
 
609
- describe('Storybot-like Structure', function() {
610
- it('should handle dotted top-level keys with deep nesting', async function() {
611
- const sessionKey = 'stm.1769201539421.dawduxooy';
612
-
613
- await db.set(sessionKey, 'started', 'LuaGardenbot', true);
614
- await db.set(sessionKey, 'active', 'LuaGardenbot', true);
615
- await db.set(sessionKey, 'active', 'AmyWaybot', true);
616
-
617
- const session = await db.get(sessionKey);
618
- assert.deepStrictEqual(session, {
619
- started: { LuaGardenbot: true },
620
- active: { LuaGardenbot: true, AmyWaybot: true }
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' });
621
454
  });
622
- });
623
455
 
624
- it('should handle replace map with special chars in keys', async function() {
625
- const sessionKey = 'stm.12345.abc';
626
-
627
- await db.set(sessionKey, 'replace', {
628
- '{lang}': 'es',
629
- '{vip_token}': '',
630
- '{first_name}': 'STM User',
631
- '{last_name}': '',
632
- '{crypto_name}': 'kkerfhkkzgf',
633
- '@main': 'martin'
634
- });
635
-
636
- const replace = await db.get(sessionKey, 'replace');
637
- assert.strictEqual(replace['{lang}'], 'es');
638
- assert.strictEqual(replace['@main'], 'martin');
639
- assert.strictEqual(replace['{first_name}'], 'STM User');
640
- });
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' });
463
+ });
641
464
 
642
- it('should handle node history with numeric-like keys', async function() {
643
- const sessionKey = 'stm.999.xyz';
644
-
645
- await db.set(sessionKey, 'node', 'history', 'c0', {
646
- from: 'LuaGardenbot',
647
- message: 'new contact',
648
- timestamp: 1771012955839
649
- });
650
- await db.set(sessionKey, 'node', 'history', '0-2760293686', {
651
- from: 'LuaGardenbot',
652
- message: 'Hey, I\'m Lua',
653
- timestamp: 1771012956863
654
- });
655
- await db.set(sessionKey, 'node', 'history', '0-4151942372-o', {
656
- from: 'main',
657
- to: 'LuaGardenbot',
658
- message: 'hola',
659
- timestamp: 1771012960058
660
- });
661
-
662
- const history = await db.get(sessionKey, 'node', 'history');
663
- assert.strictEqual(Object.keys(history).length, 3);
664
- assert.strictEqual(history['c0'].from, 'LuaGardenbot');
665
- assert.strictEqual(history['0-2760293686'].message, 'Hey, I\'m Lua');
666
- assert.strictEqual(history['0-4151942372-o'].to, 'LuaGardenbot');
667
- });
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
+ });
668
472
 
669
- it('should handle object expansion with replace map then set nested', async function() {
670
- const sessionKey = 'stm.12345.abc';
671
-
672
- // Store replace map as object
673
- await db.set(sessionKey, 'replace', {
674
- '{lang}': 'es',
675
- '{first_name}': 'User'
676
- });
677
-
678
- // Now update a single key — triggers _expandParentObjects
679
- await db.set(sessionKey, 'replace', '{lang}', 'en');
680
-
681
- const replace = await db.get(sessionKey, 'replace');
682
- assert.strictEqual(replace['{lang}'], 'en');
683
- assert.strictEqual(replace['{first_name}'], 'User');
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
+ });
684
479
  });
685
480
 
686
- it('should handle full session lifecycle without crash', async function() {
687
- const sessionKey = 'stm.1769201539421.dawduxooy';
688
-
689
- // Initial object-style set
690
- await db.set(sessionKey, 'replace', {
691
- '{lang}': 'es',
692
- '{vip_token}': '',
693
- '{first_name}': 'STM User'
694
- });
695
-
696
- await db.set(sessionKey, 'config', { country: '', gender: 'M' });
697
- await db.set(sessionKey, 'tokens', { input: 4554, output: 229, cost: 0.0079825 });
698
-
699
- // Nested individual sets
700
- await db.set(sessionKey, 'started', 'LuaGardenbot', true);
701
- await db.set(sessionKey, 'active', 'LuaGardenbot', true);
702
- await db.set(sessionKey, 'active', 'AmyWaybot', true);
703
- await db.set(sessionKey, 'node', 'current', 'chapter', 2);
704
- await db.set(sessionKey, 'node', 'current', 'id', '50-326833713');
705
-
706
- // Update inside previously stored object — triggers expansion
707
- await db.set(sessionKey, 'replace', '{lang}', 'en');
708
- await db.set(sessionKey, 'config', 'country', 'AR');
709
-
710
- // Verify everything
711
- const session = await db.get(sessionKey);
712
-
713
- assert.strictEqual(session.replace['{lang}'], 'en');
714
- assert.strictEqual(session.replace['{first_name}'], 'STM User');
715
- assert.strictEqual(session.config.country, 'AR');
716
- assert.strictEqual(session.config.gender, 'M');
717
- assert.strictEqual(session.tokens.cost, 0.0079825);
718
- assert.strictEqual(session.started.LuaGardenbot, true);
719
- assert.strictEqual(session.active.AmyWaybot, true);
720
- assert.strictEqual(session.node.current.chapter, 2);
721
- assert.strictEqual(session.node.current.id, '50-326833713');
722
- });
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
+ });
723
492
 
724
- it('should not mix sessions with similar dotted keys', async function() {
725
- await db.set('stm.111.aaa', 'data', 'session1');
726
- await db.set('stm.111.bbb', 'data', 'session2');
727
- await db.set('stm.222.aaa', 'data', 'session3');
728
-
729
- assert.deepStrictEqual(await db.get('stm.111.aaa'), { data: 'session1' });
730
- assert.deepStrictEqual(await db.get('stm.111.bbb'), { data: 'session2' });
731
- assert.deepStrictEqual(await db.get('stm.222.aaa'), { data: 'session3' });
732
- });
733
- });
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
+ });
734
508
 
735
- describe('Race Conditions', function() {
736
- it('should handle 100 concurrent increments correctly', async function() {
737
- this.timeout(5000);
738
-
739
- await db.set('counter', 0);
740
-
741
- // Run 100 concurrent increments
742
- const promises = Array.from({ length: 100 }, () => db.inc('counter', 1));
743
- await Promise.all(promises);
744
-
745
- const result = await db.get('counter');
746
- assert.strictEqual(result, 100, 'All increments should be applied atomically');
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
- it('should handle concurrent read-modify-write operations', async function() {
750
- this.timeout(5000);
751
-
752
- await db.set('data', { counter: 0, items: [] });
753
-
754
- // Run 50 concurrent updates that read, modify, and write
755
- const promises = Array.from({ length: 50 }, (_, i) =>
756
- db.upd('data', (current) => ({
757
- counter: current.counter + 1,
758
- items: [...current.items, `item-${current.counter}`]
759
- }))
760
- );
761
-
762
- await Promise.all(promises);
763
-
764
- const finalData = await db.get('data');
765
- assert.strictEqual(finalData.counter, 50, 'Counter should be exactly 50');
766
- assert.strictEqual(finalData.items.length, 50, 'Should have exactly 50 items');
767
-
768
- // Check that all items have unique values
769
- const uniqueItems = new Set(finalData.items);
770
- assert.strictEqual(uniqueItems.size, 50, 'All items should be unique');
771
- });
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
+ });
772
535
 
773
- it('should handle concurrent sets on different keys', async function() {
774
- this.timeout(5000);
775
-
776
- // Run 50 concurrent set operations on different keys
777
- const promises = Array.from({ length: 50 }, (_, i) =>
778
- db.set('users', `user${i}`, { name: `User ${i}`, value: i })
779
- );
780
-
781
- await Promise.all(promises);
782
-
783
- // Verify all keys exist and have correct values
784
- const users = await db.get('users');
785
- assert.strictEqual(Object.keys(users).length, 50, 'Should have 50 users');
786
-
787
- for (let i = 0; i < 50; i++) {
788
- assert.deepStrictEqual(
789
- users[`user${i}`],
790
- { name: `User ${i}`, value: i },
791
- `User ${i} should have correct data`
792
- );
793
- }
794
- });
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
+ });
795
559
 
796
- it('should handle concurrent add operations with unique IDs', async function() {
797
- this.timeout(5000);
798
-
799
- await db.set('items', {});
800
-
801
- // Run 50 concurrent add operations
802
- const promises = Array.from({ length: 50 }, (_, i) =>
803
- db.add('items', { value: i })
804
- );
805
-
806
- const results = await Promise.all(promises);
807
-
808
- // Verify all items were added with unique IDs
809
- const items = await db.get('items');
810
- assert.strictEqual(Object.keys(items).length, 50, 'Should have 50 items');
811
-
812
- // Check that all IDs are unique
813
- const ids = results.map(r => r[r.length - 1]);
814
- const uniqueIds = new Set(ids);
815
- assert.strictEqual(uniqueIds.size, 50, 'All IDs should be unique');
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
+ });
816
568
  });
817
569
 
818
- it('should handle concurrent decrements correctly', async function() {
819
- this.timeout(5000);
820
-
821
- await db.set('inventory', 1000);
822
-
823
- // Run 100 concurrent decrements
824
- const promises = Array.from({ length: 100 }, () => db.dec('inventory', 5));
825
- await Promise.all(promises);
826
-
827
- const result = await db.get('inventory');
828
- assert.strictEqual(result, 500, 'All decrements should be applied atomically');
829
- });
830
- });
831
- });
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
+ });
832
577
 
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
+ });
833
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
+ });
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
+ }