@joystick.js/db-canary 0.0.0-canary.2242 → 0.0.0-canary.2244

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.
Files changed (48) hide show
  1. package/README.md +13 -0
  2. package/dist/client/index.js +1 -1
  3. package/dist/server/cluster/worker.js +1 -1
  4. package/dist/server/index.js +1 -1
  5. package/dist/server/lib/development_mode.js +1 -1
  6. package/dist/server/lib/op_types.js +1 -1
  7. package/dist/server/lib/operation_dispatcher.js +1 -1
  8. package/dist/server/lib/operations/delete_many.js +1 -0
  9. package/package.json +2 -2
  10. package/src/client/index.js +33 -0
  11. package/src/server/cluster/worker.js +6 -5
  12. package/src/server/index.js +1 -0
  13. package/src/server/lib/development_mode.js +2 -2
  14. package/src/server/lib/op_types.js +1 -0
  15. package/src/server/lib/operation_dispatcher.js +5 -0
  16. package/src/server/lib/operations/delete_many.js +130 -0
  17. package/test_data_api_key_1758220165907_u8d8j2z37/data.mdb +0 -0
  18. package/test_data_api_key_1758220165907_u8d8j2z37/lock.mdb +0 -0
  19. package/test_data_api_key_1758220166138_q079fkt44/data.mdb +0 -0
  20. package/test_data_api_key_1758220166138_q079fkt44/lock.mdb +0 -0
  21. package/test_data_api_key_1758220166370_elaxmwvuj/data.mdb +0 -0
  22. package/test_data_api_key_1758220166370_elaxmwvuj/lock.mdb +0 -0
  23. package/test_data_api_key_1758220166487_689q46e05/data.mdb +0 -0
  24. package/test_data_api_key_1758220166487_689q46e05/lock.mdb +0 -0
  25. package/test_data_api_key_1758220174956_9lhw6u6la/data.mdb +0 -0
  26. package/test_data_api_key_1758220174956_9lhw6u6la/lock.mdb +0 -0
  27. package/test_data_api_key_1758220175070_1hruzftqf/data.mdb +0 -0
  28. package/test_data_api_key_1758220175070_1hruzftqf/lock.mdb +0 -0
  29. package/test_data_api_key_1758220175183_ean83s4od/data.mdb +0 -0
  30. package/test_data_api_key_1758220175183_ean83s4od/lock.mdb +0 -0
  31. package/test_data_api_key_1758220736169_8xv9swdwn/data.mdb +0 -0
  32. package/test_data_api_key_1758220736169_8xv9swdwn/lock.mdb +0 -0
  33. package/test_data_api_key_1758220736405_tzltnwwf5/data.mdb +0 -0
  34. package/test_data_api_key_1758220736405_tzltnwwf5/lock.mdb +0 -0
  35. package/test_data_api_key_1758220736640_avs9xxx7j/data.mdb +0 -0
  36. package/test_data_api_key_1758220736640_avs9xxx7j/lock.mdb +0 -0
  37. package/test_data_api_key_1758220736757_c4hhzan82/data.mdb +0 -0
  38. package/test_data_api_key_1758220736757_c4hhzan82/lock.mdb +0 -0
  39. package/test_data_api_key_1758220744324_89rzc4cak/data.mdb +0 -0
  40. package/test_data_api_key_1758220744324_89rzc4cak/lock.mdb +0 -0
  41. package/test_data_api_key_1758220744436_y2244449u/data.mdb +0 -0
  42. package/test_data_api_key_1758220744436_y2244449u/lock.mdb +0 -0
  43. package/test_data_api_key_1758220744548_968u1bkrk/data.mdb +0 -0
  44. package/test_data_api_key_1758220744548_968u1bkrk/lock.mdb +0 -0
  45. package/tests/client/index.test.js +393 -0
  46. package/tests/server/cluster/master_read_write_operations.test.js +7 -6
  47. package/tests/server/lib/operations/delete_many.test.js +263 -0
  48. package/types/client/index.d.ts +17 -0
@@ -1230,3 +1230,396 @@ test('collection chaining API handles errors properly', async (t) => {
1230
1230
 
1231
1231
  t.truthy(error);
1232
1232
  });
1233
+
1234
+ // delete_many operation tests
1235
+ test('client can perform delete_many operation via main client method', async (t) => {
1236
+ const test_port = get_next_port();
1237
+ server = await create_server();
1238
+
1239
+ await new Promise((resolve) => {
1240
+ server.listen(test_port, () => {
1241
+ setTimeout(resolve, 100);
1242
+ });
1243
+ });
1244
+
1245
+ const setup_client = joystickdb.client({
1246
+ port: test_port,
1247
+ reconnect: false
1248
+ });
1249
+
1250
+ await new Promise((resolve) => {
1251
+ setup_client.on('connect', resolve);
1252
+ });
1253
+
1254
+ const setup_result = await setup_client.setup();
1255
+ setup_client.disconnect();
1256
+
1257
+ client = joystickdb.client({
1258
+ port: test_port,
1259
+ reconnect: false,
1260
+ password: setup_result.password
1261
+ });
1262
+
1263
+ await new Promise((resolve) => {
1264
+ client.on('authenticated', resolve);
1265
+ });
1266
+
1267
+ // Insert test documents
1268
+ await client.db('default').collection('delete_many_test').insert_one({ group: 'admin', name: 'Alice' });
1269
+ await client.db('default').collection('delete_many_test').insert_one({ group: 'admin', name: 'Bob' });
1270
+ await client.db('default').collection('delete_many_test').insert_one({ group: 'user', name: 'Carol' });
1271
+
1272
+ // Use main client delete_many method
1273
+ const result = await client.delete_many('delete_many_test', { group: 'admin' });
1274
+
1275
+ t.is(result.ok, 1);
1276
+ t.is(result.deleted_count, 2);
1277
+ t.truthy(result.operation_time);
1278
+
1279
+ // Verify remaining documents
1280
+ const remaining = await client.db('default').collection('delete_many_test').find({});
1281
+ t.is(remaining.length, 1);
1282
+ t.is(remaining[0].name, 'Carol');
1283
+ });
1284
+
1285
+ test('client can perform delete_many operation via collection chaining', async (t) => {
1286
+ const test_port = get_next_port();
1287
+ server = await create_server();
1288
+
1289
+ await new Promise((resolve) => {
1290
+ server.listen(test_port, () => {
1291
+ setTimeout(resolve, 100);
1292
+ });
1293
+ });
1294
+
1295
+ const setup_client = joystickdb.client({
1296
+ port: test_port,
1297
+ reconnect: false
1298
+ });
1299
+
1300
+ await new Promise((resolve) => {
1301
+ setup_client.on('connect', resolve);
1302
+ });
1303
+
1304
+ const setup_result = await setup_client.setup();
1305
+ setup_client.disconnect();
1306
+
1307
+ client = joystickdb.client({
1308
+ port: test_port,
1309
+ reconnect: false,
1310
+ password: setup_result.password
1311
+ });
1312
+
1313
+ await new Promise((resolve) => {
1314
+ client.on('authenticated', resolve);
1315
+ });
1316
+
1317
+ const collection = client.db('default').collection('delete_many_chain_test');
1318
+
1319
+ // Insert test documents
1320
+ await collection.insert_one({ status: 'inactive', name: 'User1' });
1321
+ await collection.insert_one({ status: 'inactive', name: 'User2' });
1322
+ await collection.insert_one({ status: 'inactive', name: 'User3' });
1323
+ await collection.insert_one({ status: 'active', name: 'User4' });
1324
+
1325
+ // Use collection chaining delete_many method
1326
+ const result = await collection.delete_many({ status: 'inactive' });
1327
+
1328
+ t.is(result.ok, 1);
1329
+ t.is(result.deleted_count, 3);
1330
+ t.truthy(result.operation_time);
1331
+
1332
+ // Verify remaining documents
1333
+ const remaining = await collection.find({});
1334
+ t.is(remaining.length, 1);
1335
+ t.is(remaining[0].name, 'User4');
1336
+ });
1337
+
1338
+ test('client delete_many respects limit option', async (t) => {
1339
+ const test_port = get_next_port();
1340
+ server = await create_server();
1341
+
1342
+ await new Promise((resolve) => {
1343
+ server.listen(test_port, () => {
1344
+ setTimeout(resolve, 100);
1345
+ });
1346
+ });
1347
+
1348
+ const setup_client = joystickdb.client({
1349
+ port: test_port,
1350
+ reconnect: false
1351
+ });
1352
+
1353
+ await new Promise((resolve) => {
1354
+ setup_client.on('connect', resolve);
1355
+ });
1356
+
1357
+ const setup_result = await setup_client.setup();
1358
+ setup_client.disconnect();
1359
+
1360
+ client = joystickdb.client({
1361
+ port: test_port,
1362
+ reconnect: false,
1363
+ password: setup_result.password
1364
+ });
1365
+
1366
+ await new Promise((resolve) => {
1367
+ client.on('authenticated', resolve);
1368
+ });
1369
+
1370
+ const collection = client.db('default').collection('delete_many_limit_test');
1371
+
1372
+ // Insert test documents
1373
+ await collection.insert_one({ category: 'temp', value: 1 });
1374
+ await collection.insert_one({ category: 'temp', value: 2 });
1375
+ await collection.insert_one({ category: 'temp', value: 3 });
1376
+ await collection.insert_one({ category: 'temp', value: 4 });
1377
+
1378
+ // Delete with limit
1379
+ const result = await collection.delete_many({ category: 'temp' }, { limit: 2 });
1380
+
1381
+ t.is(result.ok, 1);
1382
+ t.is(result.deleted_count, 2);
1383
+
1384
+ // Verify remaining documents
1385
+ const remaining = await collection.find({});
1386
+ t.is(remaining.length, 2);
1387
+ });
1388
+
1389
+ test('client delete_many returns zero count when no matches', async (t) => {
1390
+ const test_port = get_next_port();
1391
+ server = await create_server();
1392
+
1393
+ await new Promise((resolve) => {
1394
+ server.listen(test_port, () => {
1395
+ setTimeout(resolve, 100);
1396
+ });
1397
+ });
1398
+
1399
+ const setup_client = joystickdb.client({
1400
+ port: test_port,
1401
+ reconnect: false
1402
+ });
1403
+
1404
+ await new Promise((resolve) => {
1405
+ setup_client.on('connect', resolve);
1406
+ });
1407
+
1408
+ const setup_result = await setup_client.setup();
1409
+ setup_client.disconnect();
1410
+
1411
+ client = joystickdb.client({
1412
+ port: test_port,
1413
+ reconnect: false,
1414
+ password: setup_result.password
1415
+ });
1416
+
1417
+ await new Promise((resolve) => {
1418
+ client.on('authenticated', resolve);
1419
+ });
1420
+
1421
+ const collection = client.db('default').collection('delete_many_no_match_test');
1422
+
1423
+ // Insert test documents
1424
+ await collection.insert_one({ type: 'keep', name: 'Document1' });
1425
+ await collection.insert_one({ type: 'keep', name: 'Document2' });
1426
+
1427
+ // Try to delete non-matching documents
1428
+ const result = await collection.delete_many({ type: 'nonexistent' });
1429
+
1430
+ t.is(result.ok, 1);
1431
+ t.is(result.deleted_count, 0);
1432
+ t.truthy(result.operation_time);
1433
+
1434
+ // Verify all documents remain
1435
+ const remaining = await collection.find({});
1436
+ t.is(remaining.length, 2);
1437
+ });
1438
+
1439
+ test('client delete_many deletes all documents with empty filter', async (t) => {
1440
+ const test_port = get_next_port();
1441
+ server = await create_server();
1442
+
1443
+ await new Promise((resolve) => {
1444
+ server.listen(test_port, () => {
1445
+ setTimeout(resolve, 100);
1446
+ });
1447
+ });
1448
+
1449
+ const setup_client = joystickdb.client({
1450
+ port: test_port,
1451
+ reconnect: false
1452
+ });
1453
+
1454
+ await new Promise((resolve) => {
1455
+ setup_client.on('connect', resolve);
1456
+ });
1457
+
1458
+ const setup_result = await setup_client.setup();
1459
+ setup_client.disconnect();
1460
+
1461
+ client = joystickdb.client({
1462
+ port: test_port,
1463
+ reconnect: false,
1464
+ password: setup_result.password
1465
+ });
1466
+
1467
+ await new Promise((resolve) => {
1468
+ client.on('authenticated', resolve);
1469
+ });
1470
+
1471
+ const collection = client.db('default').collection('delete_many_all_test');
1472
+
1473
+ // Insert test documents
1474
+ await collection.insert_one({ name: 'Doc1' });
1475
+ await collection.insert_one({ name: 'Doc2' });
1476
+ await collection.insert_one({ name: 'Doc3' });
1477
+
1478
+ // Delete all documents with empty filter
1479
+ const result = await collection.delete_many({});
1480
+
1481
+ t.is(result.ok, 1);
1482
+ t.is(result.deleted_count, 3);
1483
+
1484
+ // Verify no documents remain
1485
+ const remaining = await collection.find({});
1486
+ t.is(remaining.length, 0);
1487
+ });
1488
+
1489
+ test('client delete_many handles complex filters', async (t) => {
1490
+ const test_port = get_next_port();
1491
+ server = await create_server();
1492
+
1493
+ await new Promise((resolve) => {
1494
+ server.listen(test_port, () => {
1495
+ setTimeout(resolve, 100);
1496
+ });
1497
+ });
1498
+
1499
+ const setup_client = joystickdb.client({
1500
+ port: test_port,
1501
+ reconnect: false
1502
+ });
1503
+
1504
+ await new Promise((resolve) => {
1505
+ setup_client.on('connect', resolve);
1506
+ });
1507
+
1508
+ const setup_result = await setup_client.setup();
1509
+ setup_client.disconnect();
1510
+
1511
+ client = joystickdb.client({
1512
+ port: test_port,
1513
+ reconnect: false,
1514
+ password: setup_result.password
1515
+ });
1516
+
1517
+ await new Promise((resolve) => {
1518
+ client.on('authenticated', resolve);
1519
+ });
1520
+
1521
+ const collection = client.db('default').collection('delete_many_complex_test');
1522
+
1523
+ // Insert test documents
1524
+ await collection.insert_one({ age: 25, active: true, role: 'admin' });
1525
+ await collection.insert_one({ age: 25, active: false, role: 'user' });
1526
+ await collection.insert_one({ age: 30, active: true, role: 'admin' });
1527
+ await collection.insert_one({ age: 30, active: true, role: 'user' });
1528
+
1529
+ // Delete with complex filter
1530
+ const result = await collection.delete_many({ age: 25, active: true });
1531
+
1532
+ t.is(result.ok, 1);
1533
+ t.is(result.deleted_count, 1);
1534
+
1535
+ // Verify correct documents remain
1536
+ const remaining = await collection.find({});
1537
+ t.is(remaining.length, 3);
1538
+ t.false(remaining.some(doc => doc.age === 25 && doc.active === true));
1539
+ });
1540
+
1541
+ test('client delete_many method exists on collection interface', async (t) => {
1542
+ const test_port = get_next_port();
1543
+ server = await create_server();
1544
+
1545
+ await new Promise((resolve) => {
1546
+ server.listen(test_port, () => {
1547
+ setTimeout(resolve, 100);
1548
+ });
1549
+ });
1550
+
1551
+ const setup_client = joystickdb.client({
1552
+ port: test_port,
1553
+ reconnect: false
1554
+ });
1555
+
1556
+ await new Promise((resolve) => {
1557
+ setup_client.on('connect', resolve);
1558
+ });
1559
+
1560
+ const setup_result = await setup_client.setup();
1561
+ setup_client.disconnect();
1562
+
1563
+ client = joystickdb.client({
1564
+ port: test_port,
1565
+ reconnect: false,
1566
+ password: setup_result.password
1567
+ });
1568
+
1569
+ await new Promise((resolve) => {
1570
+ client.on('authenticated', resolve);
1571
+ });
1572
+
1573
+ const collection = client.db('default').collection('method_check_test');
1574
+
1575
+ // Verify delete_many method exists
1576
+ t.is(typeof collection.delete_many, 'function');
1577
+ t.is(typeof client.delete_many, 'function');
1578
+ });
1579
+
1580
+ test('client delete_many handles error scenarios properly', async (t) => {
1581
+ const test_port = get_next_port();
1582
+ server = await create_server();
1583
+
1584
+ await new Promise((resolve) => {
1585
+ server.listen(test_port, () => {
1586
+ setTimeout(resolve, 100);
1587
+ });
1588
+ });
1589
+
1590
+ const setup_client = joystickdb.client({
1591
+ port: test_port,
1592
+ reconnect: false
1593
+ });
1594
+
1595
+ await new Promise((resolve) => {
1596
+ setup_client.on('connect', resolve);
1597
+ });
1598
+
1599
+ const setup_result = await setup_client.setup();
1600
+ setup_client.disconnect();
1601
+
1602
+ client = joystickdb.client({
1603
+ port: test_port,
1604
+ reconnect: false,
1605
+ password: setup_result.password
1606
+ });
1607
+
1608
+ await new Promise((resolve) => {
1609
+ client.on('authenticated', resolve);
1610
+ });
1611
+
1612
+ const collection = client.db('default').collection('error_handling_test');
1613
+
1614
+ // Test invalid filter
1615
+ const error1 = await t.throwsAsync(async () => {
1616
+ await collection.delete_many(null);
1617
+ });
1618
+ t.truthy(error1.message.includes('Filter must be a valid object'));
1619
+
1620
+ // Test invalid limit
1621
+ const error2 = await t.throwsAsync(async () => {
1622
+ await collection.delete_many({}, { limit: -1 });
1623
+ });
1624
+ t.truthy(error2.message.includes('Limit must be a non-negative number'));
1625
+ });
@@ -180,9 +180,10 @@ test.serial('master node handles read operations - find_one', async (t) => {
180
180
  });
181
181
  const find_response = await client.receive();
182
182
  t.is(find_response.ok, 1);
183
- t.truthy(find_response.name, 'Response should have name field');
184
- t.is(find_response.name, 'test_doc');
185
- t.is(find_response.value, 42);
183
+ t.truthy(find_response.document, 'Response should have document field');
184
+ t.truthy(find_response.document.name, 'Document should have name field');
185
+ t.is(find_response.document.name, 'test_doc');
186
+ t.is(find_response.document.value, 42);
186
187
 
187
188
  } finally {
188
189
  if (client) {
@@ -410,8 +411,8 @@ test.serial('master node handles mixed read and write operations', async (t) =>
410
411
  });
411
412
  const find_response = await client.receive();
412
413
  t.is(find_response.ok, 1);
413
- t.is(find_response.name, unique_doc_name);
414
- t.is(find_response.status, 'active');
414
+ t.is(find_response.document.name, unique_doc_name);
415
+ t.is(find_response.document.status, 'active');
415
416
 
416
417
  // 3. Write: Update document
417
418
  client.send({
@@ -439,7 +440,7 @@ test.serial('master node handles mixed read and write operations', async (t) =>
439
440
  });
440
441
  const find_updated_response = await client.receive();
441
442
  t.is(find_updated_response.ok, 1);
442
- t.is(find_updated_response.status, 'updated');
443
+ t.is(find_updated_response.document.status, 'updated');
443
444
 
444
445
  // 5. Write: Create index
445
446
  client.send({
@@ -0,0 +1,263 @@
1
+ import test from 'ava';
2
+ import sinon from 'sinon';
3
+ import delete_many from '../../../../src/server/lib/operations/delete_many.js';
4
+ import insert_one from '../../../../src/server/lib/operations/insert_one.js';
5
+ import find from '../../../../src/server/lib/operations/find.js';
6
+ import { initialize_database, get_database, cleanup_database } from '../../../../src/server/lib/query_engine.js';
7
+
8
+ test.beforeEach(() => {
9
+ initialize_database('./test_data');
10
+ const db = get_database();
11
+ db.clearSync();
12
+ });
13
+
14
+ test.afterEach(async () => {
15
+ await cleanup_database();
16
+ });
17
+
18
+ test('delete_many - should delete multiple documents by filter', async (t) => {
19
+ await insert_one('default', 'users', { name: 'Alice', group: 'admin' });
20
+ await insert_one('default', 'users', { name: 'Bob', group: 'admin' });
21
+ await insert_one('default', 'users', { name: 'Carol', group: 'user' });
22
+
23
+ const result = await delete_many('default', 'users', { group: 'admin' });
24
+
25
+ t.true(result.acknowledged);
26
+ t.is(result.deleted_count, 2);
27
+ t.truthy(result.operation_time);
28
+
29
+ const remaining_docs = await find('default', 'users', {});
30
+ t.is(remaining_docs.length, 1);
31
+ t.is(remaining_docs[0].name, 'Carol');
32
+ });
33
+
34
+ test('delete_many - should delete all documents with empty filter', async (t) => {
35
+ await insert_one('default', 'users', { name: 'Alice' });
36
+ await insert_one('default', 'users', { name: 'Bob' });
37
+ await insert_one('default', 'users', { name: 'Carol' });
38
+
39
+ const result = await delete_many('default', 'users', {});
40
+
41
+ t.true(result.acknowledged);
42
+ t.is(result.deleted_count, 3);
43
+
44
+ const remaining_docs = await find('default', 'users', {});
45
+ t.is(remaining_docs.length, 0);
46
+ });
47
+
48
+ test('delete_many - should respect limit option', async (t) => {
49
+ await insert_one('default', 'users', { name: 'Alice', group: 'test' });
50
+ await insert_one('default', 'users', { name: 'Bob', group: 'test' });
51
+ await insert_one('default', 'users', { name: 'Carol', group: 'test' });
52
+ await insert_one('default', 'users', { name: 'Dan', group: 'test' });
53
+
54
+ const result = await delete_many('default', 'users', { group: 'test' }, { limit: 2 });
55
+
56
+ t.true(result.acknowledged);
57
+ t.is(result.deleted_count, 2);
58
+
59
+ const remaining_docs = await find('default', 'users', {});
60
+ t.is(remaining_docs.length, 2);
61
+ });
62
+
63
+ test('delete_many - should return deleted_count 0 if no matches', async (t) => {
64
+ await insert_one('default', 'users', { name: 'Alice', group: 'admin' });
65
+ await insert_one('default', 'users', { name: 'Bob', group: 'user' });
66
+
67
+ const result = await delete_many('default', 'users', { group: 'nonexistent' });
68
+
69
+ t.true(result.acknowledged);
70
+ t.is(result.deleted_count, 0);
71
+
72
+ const remaining_docs = await find('default', 'users', {});
73
+ t.is(remaining_docs.length, 2);
74
+ });
75
+
76
+ test('delete_many - should handle limit of 0', async (t) => {
77
+ await insert_one('default', 'users', { name: 'Alice', group: 'test' });
78
+ await insert_one('default', 'users', { name: 'Bob', group: 'test' });
79
+
80
+ const result = await delete_many('default', 'users', { group: 'test' }, { limit: 0 });
81
+
82
+ t.true(result.acknowledged);
83
+ t.is(result.deleted_count, 0);
84
+
85
+ const remaining_docs = await find('default', 'users', {});
86
+ t.is(remaining_docs.length, 2);
87
+ });
88
+
89
+ test('delete_many - should handle limit of 1', async (t) => {
90
+ await insert_one('default', 'users', { name: 'Alice', group: 'test' });
91
+ await insert_one('default', 'users', { name: 'Bob', group: 'test' });
92
+
93
+ const result = await delete_many('default', 'users', { group: 'test' }, { limit: 1 });
94
+
95
+ t.true(result.acknowledged);
96
+ t.is(result.deleted_count, 1);
97
+
98
+ const remaining_docs = await find('default', 'users', {});
99
+ t.is(remaining_docs.length, 1);
100
+ });
101
+
102
+ test('delete_many - should work with complex filters', async (t) => {
103
+ await insert_one('default', 'users', { name: 'Alice', age: 25, active: true });
104
+ await insert_one('default', 'users', { name: 'Bob', age: 30, active: true });
105
+ await insert_one('default', 'users', { name: 'Carol', age: 25, active: false });
106
+ await insert_one('default', 'users', { name: 'Dan', age: 30, active: false });
107
+
108
+ const result = await delete_many('default', 'users', { age: 25, active: true });
109
+
110
+ t.true(result.acknowledged);
111
+ t.is(result.deleted_count, 1);
112
+
113
+ const remaining_docs = await find('default', 'users', {});
114
+ t.is(remaining_docs.length, 3);
115
+ t.false(remaining_docs.some(doc => doc.name === 'Alice'));
116
+ });
117
+
118
+ test('delete_many - should work across different collections', async (t) => {
119
+ await insert_one('default', 'users', { name: 'Alice', type: 'temp' });
120
+ await insert_one('default', 'users', { name: 'Bob', type: 'temp' });
121
+ await insert_one('default', 'posts', { title: 'Post 1', type: 'temp' });
122
+ await insert_one('default', 'posts', { title: 'Post 2', type: 'temp' });
123
+
124
+ const users_result = await delete_many('default', 'users', { type: 'temp' });
125
+ const posts_result = await delete_many('default', 'posts', { type: 'temp' });
126
+
127
+ t.is(users_result.deleted_count, 2);
128
+ t.is(posts_result.deleted_count, 2);
129
+
130
+ const remaining_users = await find('default', 'users', {});
131
+ const remaining_posts = await find('default', 'posts', {});
132
+ t.is(remaining_users.length, 0);
133
+ t.is(remaining_posts.length, 0);
134
+ });
135
+
136
+ test('delete_many - should work with different databases', async (t) => {
137
+ await insert_one('db1', 'users', { name: 'Alice', group: 'test' });
138
+ await insert_one('db1', 'users', { name: 'Bob', group: 'test' });
139
+ await insert_one('db2', 'users', { name: 'Carol', group: 'test' });
140
+ await insert_one('db2', 'users', { name: 'Dan', group: 'test' });
141
+
142
+ const db1_result = await delete_many('db1', 'users', { group: 'test' });
143
+
144
+ t.is(db1_result.deleted_count, 2);
145
+
146
+ const db1_remaining = await find('db1', 'users', {});
147
+ const db2_remaining = await find('db2', 'users', {});
148
+ t.is(db1_remaining.length, 0);
149
+ t.is(db2_remaining.length, 2);
150
+ });
151
+
152
+ test('delete_many - should handle documents with parsing errors gracefully', async (t) => {
153
+ const db = get_database();
154
+
155
+ // Insert valid documents
156
+ await insert_one('default', 'users', { name: 'Alice', group: 'test' });
157
+ await insert_one('default', 'users', { name: 'Bob', group: 'test' });
158
+
159
+ // Insert invalid JSON directly
160
+ db.put('default:users:invalid', 'invalid-json-data');
161
+
162
+ const result = await delete_many('default', 'users', { group: 'test' });
163
+
164
+ t.true(result.acknowledged);
165
+ t.is(result.deleted_count, 2);
166
+
167
+ // Invalid document should still exist
168
+ const invalid_doc = db.get('default:users:invalid');
169
+ t.is(invalid_doc, 'invalid-json-data');
170
+ });
171
+
172
+ // Error handling tests
173
+ test('delete_many - should throw if database name is missing', async (t) => {
174
+ await t.throwsAsync(
175
+ () => delete_many('', 'users', { name: 'Alice' }),
176
+ { message: 'Database name is required' }
177
+ );
178
+ });
179
+
180
+ test('delete_many - should throw if collection name is missing', async (t) => {
181
+ await t.throwsAsync(
182
+ () => delete_many('default', '', { name: 'Alice' }),
183
+ { message: 'Collection name is required' }
184
+ );
185
+ });
186
+
187
+ test('delete_many - should throw if filter is not an object', async (t) => {
188
+ await t.throwsAsync(
189
+ () => delete_many('default', 'users', null),
190
+ { message: 'Filter must be a valid object' }
191
+ );
192
+
193
+ await t.throwsAsync(
194
+ () => delete_many('default', 'users', 'not-an-object'),
195
+ { message: 'Filter must be a valid object' }
196
+ );
197
+
198
+ await t.throwsAsync(
199
+ () => delete_many('default', 'users', 123),
200
+ { message: 'Filter must be a valid object' }
201
+ );
202
+ });
203
+
204
+ test('delete_many - should throw if limit is not a valid number', async (t) => {
205
+ await t.throwsAsync(
206
+ () => delete_many('default', 'users', {}, { limit: -1 }),
207
+ { message: 'Limit must be a non-negative number' }
208
+ );
209
+
210
+ await t.throwsAsync(
211
+ () => delete_many('default', 'users', {}, { limit: 'invalid' }),
212
+ { message: 'Limit must be a non-negative number' }
213
+ );
214
+
215
+ await t.throwsAsync(
216
+ () => delete_many('default', 'users', {}, { limit: null }),
217
+ { message: 'Limit must be a non-negative number' }
218
+ );
219
+ });
220
+
221
+ // Performance and edge case tests
222
+ test('delete_many - should handle large number of documents efficiently', async (t) => {
223
+ // Insert 100 documents
224
+ const insert_promises = [];
225
+ for (let i = 0; i < 100; i++) {
226
+ insert_promises.push(insert_one('default', 'users', {
227
+ name: `User${i}`,
228
+ group: i < 50 ? 'delete' : 'keep'
229
+ }));
230
+ }
231
+ await Promise.all(insert_promises);
232
+
233
+ const start_time = Date.now();
234
+ const result = await delete_many('default', 'users', { group: 'delete' });
235
+ const end_time = Date.now();
236
+
237
+ t.true(result.acknowledged);
238
+ t.is(result.deleted_count, 50);
239
+
240
+ const remaining_docs = await find('default', 'users', {});
241
+ t.is(remaining_docs.length, 50);
242
+
243
+ // Should complete within reasonable time (less than 1 second)
244
+ t.true(end_time - start_time < 1000);
245
+ });
246
+
247
+ test('delete_many - should handle empty collection', async (t) => {
248
+ const result = await delete_many('default', 'empty_collection', {});
249
+
250
+ t.true(result.acknowledged);
251
+ t.is(result.deleted_count, 0);
252
+ t.truthy(result.operation_time);
253
+ });
254
+
255
+ test('delete_many - should return proper operation_time format', async (t) => {
256
+ await insert_one('default', 'users', { name: 'Alice' });
257
+
258
+ const result = await delete_many('default', 'users', {});
259
+
260
+ t.truthy(result.operation_time);
261
+ t.true(typeof result.operation_time === 'string');
262
+ t.true(new Date(result.operation_time).getTime() > 0);
263
+ });