@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.
- package/README.md +13 -0
- package/dist/client/index.js +1 -1
- package/dist/server/cluster/worker.js +1 -1
- package/dist/server/index.js +1 -1
- package/dist/server/lib/development_mode.js +1 -1
- package/dist/server/lib/op_types.js +1 -1
- package/dist/server/lib/operation_dispatcher.js +1 -1
- package/dist/server/lib/operations/delete_many.js +1 -0
- package/package.json +2 -2
- package/src/client/index.js +33 -0
- package/src/server/cluster/worker.js +6 -5
- package/src/server/index.js +1 -0
- package/src/server/lib/development_mode.js +2 -2
- package/src/server/lib/op_types.js +1 -0
- package/src/server/lib/operation_dispatcher.js +5 -0
- package/src/server/lib/operations/delete_many.js +130 -0
- package/test_data_api_key_1758220165907_u8d8j2z37/data.mdb +0 -0
- package/test_data_api_key_1758220165907_u8d8j2z37/lock.mdb +0 -0
- package/test_data_api_key_1758220166138_q079fkt44/data.mdb +0 -0
- package/test_data_api_key_1758220166138_q079fkt44/lock.mdb +0 -0
- package/test_data_api_key_1758220166370_elaxmwvuj/data.mdb +0 -0
- package/test_data_api_key_1758220166370_elaxmwvuj/lock.mdb +0 -0
- package/test_data_api_key_1758220166487_689q46e05/data.mdb +0 -0
- package/test_data_api_key_1758220166487_689q46e05/lock.mdb +0 -0
- package/test_data_api_key_1758220174956_9lhw6u6la/data.mdb +0 -0
- package/test_data_api_key_1758220174956_9lhw6u6la/lock.mdb +0 -0
- package/test_data_api_key_1758220175070_1hruzftqf/data.mdb +0 -0
- package/test_data_api_key_1758220175070_1hruzftqf/lock.mdb +0 -0
- package/test_data_api_key_1758220175183_ean83s4od/data.mdb +0 -0
- package/test_data_api_key_1758220175183_ean83s4od/lock.mdb +0 -0
- package/test_data_api_key_1758220736169_8xv9swdwn/data.mdb +0 -0
- package/test_data_api_key_1758220736169_8xv9swdwn/lock.mdb +0 -0
- package/test_data_api_key_1758220736405_tzltnwwf5/data.mdb +0 -0
- package/test_data_api_key_1758220736405_tzltnwwf5/lock.mdb +0 -0
- package/test_data_api_key_1758220736640_avs9xxx7j/data.mdb +0 -0
- package/test_data_api_key_1758220736640_avs9xxx7j/lock.mdb +0 -0
- package/test_data_api_key_1758220736757_c4hhzan82/data.mdb +0 -0
- package/test_data_api_key_1758220736757_c4hhzan82/lock.mdb +0 -0
- package/test_data_api_key_1758220744324_89rzc4cak/data.mdb +0 -0
- package/test_data_api_key_1758220744324_89rzc4cak/lock.mdb +0 -0
- package/test_data_api_key_1758220744436_y2244449u/data.mdb +0 -0
- package/test_data_api_key_1758220744436_y2244449u/lock.mdb +0 -0
- package/test_data_api_key_1758220744548_968u1bkrk/data.mdb +0 -0
- package/test_data_api_key_1758220744548_968u1bkrk/lock.mdb +0 -0
- package/tests/client/index.test.js +393 -0
- package/tests/server/cluster/master_read_write_operations.test.js +7 -6
- package/tests/server/lib/operations/delete_many.test.js +263 -0
- 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.
|
|
184
|
-
t.
|
|
185
|
-
t.is(find_response.
|
|
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
|
+
});
|