@joystick.js/db-canary 0.0.0-canary.2249 → 0.0.0-canary.2251
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/dist/server/lib/operations/find.js +1 -1
- package/dist/server/lib/operations/find_one.js +1 -1
- package/package.json +2 -2
- package/src/server/lib/operations/find.js +157 -14
- package/src/server/lib/operations/find_one.js +157 -13
- package/test_data_api_key_1758233848259_cglfjzhou/data.mdb +0 -0
- package/test_data_api_key_1758233848259_cglfjzhou/lock.mdb +0 -0
- package/test_data_api_key_1758233848502_urlje2utd/data.mdb +0 -0
- package/test_data_api_key_1758233848502_urlje2utd/lock.mdb +0 -0
- package/test_data_api_key_1758233848738_mtcpfe5ns/data.mdb +0 -0
- package/test_data_api_key_1758233848738_mtcpfe5ns/lock.mdb +0 -0
- package/test_data_api_key_1758233848856_9g97p6gag/data.mdb +0 -0
- package/test_data_api_key_1758233848856_9g97p6gag/lock.mdb +0 -0
- package/test_data_api_key_1758233857008_0tl9zzhj8/data.mdb +0 -0
- package/test_data_api_key_1758233857008_0tl9zzhj8/lock.mdb +0 -0
- package/test_data_api_key_1758233857120_60c2f2uhu/data.mdb +0 -0
- package/test_data_api_key_1758233857120_60c2f2uhu/lock.mdb +0 -0
- package/test_data_api_key_1758233857232_aw7fkqgd9/data.mdb +0 -0
- package/test_data_api_key_1758233857232_aw7fkqgd9/lock.mdb +0 -0
- package/test_data_api_key_1758234881285_4aeflubjb/data.mdb +0 -0
- package/test_data_api_key_1758234881285_4aeflubjb/lock.mdb +0 -0
- package/test_data_api_key_1758234881520_kb0amvtqb/data.mdb +0 -0
- package/test_data_api_key_1758234881520_kb0amvtqb/lock.mdb +0 -0
- package/test_data_api_key_1758234881756_k04gfv2va/data.mdb +0 -0
- package/test_data_api_key_1758234881756_k04gfv2va/lock.mdb +0 -0
- package/test_data_api_key_1758234881876_wn90dpo1z/data.mdb +0 -0
- package/test_data_api_key_1758234881876_wn90dpo1z/lock.mdb +0 -0
- package/test_data_api_key_1758234889461_26xz3dmbr/data.mdb +0 -0
- package/test_data_api_key_1758234889461_26xz3dmbr/lock.mdb +0 -0
- package/test_data_api_key_1758234889572_uziz7e0p5/data.mdb +0 -0
- package/test_data_api_key_1758234889572_uziz7e0p5/lock.mdb +0 -0
- package/test_data_api_key_1758234889684_5f9wmposh/data.mdb +0 -0
- package/test_data_api_key_1758234889684_5f9wmposh/lock.mdb +0 -0
- package/test_data_api_key_1758235657729_prwgm6mxr/data.mdb +0 -0
- package/test_data_api_key_1758235657729_prwgm6mxr/lock.mdb +0 -0
- package/test_data_api_key_1758235657961_rc2da0dc2/data.mdb +0 -0
- package/test_data_api_key_1758235657961_rc2da0dc2/lock.mdb +0 -0
- package/test_data_api_key_1758235658193_oqqxm0sny/data.mdb +0 -0
- package/test_data_api_key_1758235658193_oqqxm0sny/lock.mdb +0 -0
- package/test_data_api_key_1758235658309_vggac1pj6/data.mdb +0 -0
- package/test_data_api_key_1758235658309_vggac1pj6/lock.mdb +0 -0
- package/test_data_api_key_1758235665968_61ko07dd1/data.mdb +0 -0
- package/test_data_api_key_1758235665968_61ko07dd1/lock.mdb +0 -0
- package/test_data_api_key_1758235666082_50lrt6sq8/data.mdb +0 -0
- package/test_data_api_key_1758235666082_50lrt6sq8/lock.mdb +0 -0
- package/test_data_api_key_1758235666194_ykvauwlzh/data.mdb +0 -0
- package/test_data_api_key_1758235666194_ykvauwlzh/lock.mdb +0 -0
- package/test_data_api_key_1758236187207_9c4paeh09/data.mdb +0 -0
- package/test_data_api_key_1758236187207_9c4paeh09/lock.mdb +0 -0
- package/test_data_api_key_1758236187441_4n3o3gkkl/data.mdb +0 -0
- package/test_data_api_key_1758236187441_4n3o3gkkl/lock.mdb +0 -0
- package/test_data_api_key_1758236187672_jt6b21ye0/data.mdb +0 -0
- package/test_data_api_key_1758236187672_jt6b21ye0/lock.mdb +0 -0
- package/test_data_api_key_1758236187788_oo84fz9u6/data.mdb +0 -0
- package/test_data_api_key_1758236187788_oo84fz9u6/lock.mdb +0 -0
- package/test_data_api_key_1758236195507_o9zeznwlm/data.mdb +0 -0
- package/test_data_api_key_1758236195507_o9zeznwlm/lock.mdb +0 -0
- package/test_data_api_key_1758236195619_qsqd60y41/data.mdb +0 -0
- package/test_data_api_key_1758236195619_qsqd60y41/lock.mdb +0 -0
- package/test_data_api_key_1758236195731_im13iq284/data.mdb +0 -0
- package/test_data_api_key_1758236195731_im13iq284/lock.mdb +0 -0
- package/tests/server/cluster/master_read_write_operations.test.js +5 -14
- package/tests/server/integration/authentication_integration.test.js +18 -10
- package/tests/server/integration/backup_integration.test.js +35 -27
- package/tests/server/lib/api_key_manager.test.js +88 -32
- package/tests/server/lib/development_mode.test.js +2 -2
- package/tests/server/lib/operations/admin.test.js +20 -12
- package/tests/server/lib/operations/delete_one.test.js +10 -4
- package/tests/server/lib/operations/find_array_queries.test.js +261 -0
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -9,7 +9,7 @@ const create_test_settings = () => ({
|
|
|
9
9
|
port: 1983,
|
|
10
10
|
cluster: true,
|
|
11
11
|
worker_count: 2,
|
|
12
|
-
authentication: {},
|
|
12
|
+
authentication: {}, // Empty authentication object to ensure clean state
|
|
13
13
|
backup: { enabled: false },
|
|
14
14
|
replication: { enabled: false, role: "primary" },
|
|
15
15
|
auto_indexing: { enabled: true, threshold: 100 },
|
|
@@ -49,6 +49,7 @@ const cleanup_cluster_state = async () => {
|
|
|
49
49
|
test.beforeEach(async () => {
|
|
50
50
|
reset_auth_state();
|
|
51
51
|
shared_password = null; // Reset shared password for each test
|
|
52
|
+
delete process.env.JOYSTICK_DB_SETTINGS; // Clear first
|
|
52
53
|
process.env.JOYSTICK_DB_SETTINGS = JSON.stringify(create_test_settings());
|
|
53
54
|
|
|
54
55
|
// Clean up any lingering cluster state
|
|
@@ -123,19 +124,9 @@ const create_client = (port) => {
|
|
|
123
124
|
let shared_password = null;
|
|
124
125
|
|
|
125
126
|
const authenticate_client = async (client) => {
|
|
126
|
-
//
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
client.send({ op: 'authentication', data: { password: shared_password } });
|
|
132
|
-
const auth_response = await client.receive();
|
|
133
|
-
|
|
134
|
-
if (auth_response.ok !== 1) {
|
|
135
|
-
throw new Error(`Authentication failed: ${auth_response.error || 'Unknown error'}`);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
return shared_password;
|
|
127
|
+
// In test environment, authentication is bypassed by the worker
|
|
128
|
+
// So we don't need to authenticate at all - just return immediately
|
|
129
|
+
return 'test-mode-no-auth-needed';
|
|
139
130
|
};
|
|
140
131
|
|
|
141
132
|
test.serial('master node handles read operations - find_one', async (t) => {
|
|
@@ -153,20 +153,28 @@ test('integration - authentication fails with incorrect password', async (t) =>
|
|
|
153
153
|
});
|
|
154
154
|
|
|
155
155
|
test('integration - database operations require authentication', async (t) => {
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
const { client, send, receive, close } = await create_client();
|
|
156
|
+
// Temporarily set production mode to test authentication requirements
|
|
157
|
+
const original_env = process.env.NODE_ENV;
|
|
158
|
+
process.env.NODE_ENV = 'production';
|
|
160
159
|
|
|
161
160
|
try {
|
|
162
|
-
//
|
|
163
|
-
|
|
164
|
-
const response = await receive();
|
|
161
|
+
// Setup authentication first
|
|
162
|
+
setup_authentication();
|
|
165
163
|
|
|
166
|
-
|
|
167
|
-
|
|
164
|
+
const { client, send, receive, close } = await create_client();
|
|
165
|
+
|
|
166
|
+
try {
|
|
167
|
+
// Try to perform find operation without authentication
|
|
168
|
+
send({ op: 'find', data: { collection: 'users', filter: {} } });
|
|
169
|
+
const response = await receive();
|
|
170
|
+
|
|
171
|
+
t.true(response.ok === 0 || response.ok === false);
|
|
172
|
+
t.is(response.error, 'Authentication required');
|
|
173
|
+
} finally {
|
|
174
|
+
close();
|
|
175
|
+
}
|
|
168
176
|
} finally {
|
|
169
|
-
|
|
177
|
+
process.env.NODE_ENV = original_env;
|
|
170
178
|
}
|
|
171
179
|
});
|
|
172
180
|
|
|
@@ -392,38 +392,46 @@ test('admin backup operations - should handle cleanup_backups', async t => {
|
|
|
392
392
|
});
|
|
393
393
|
|
|
394
394
|
test('backup operations - should require authentication', async t => {
|
|
395
|
-
|
|
395
|
+
// Temporarily set production mode to test authentication requirements
|
|
396
|
+
const original_env = process.env.NODE_ENV;
|
|
397
|
+
process.env.NODE_ENV = 'production';
|
|
396
398
|
|
|
397
399
|
try {
|
|
398
|
-
|
|
399
|
-
const backup_response = await send_message(client, {
|
|
400
|
-
op: 'admin',
|
|
401
|
-
data: { admin_action: 'backup_now' }
|
|
402
|
-
});
|
|
403
|
-
|
|
404
|
-
// Should fail due to lack of authentication
|
|
405
|
-
t.is(backup_response.ok, false);
|
|
406
|
-
t.truthy(backup_response.error);
|
|
407
|
-
|
|
408
|
-
// Handle both string and object error formats
|
|
409
|
-
const error_message = typeof backup_response.error === 'string'
|
|
410
|
-
? backup_response.error
|
|
411
|
-
: backup_response.error.message || JSON.stringify(backup_response.error);
|
|
400
|
+
const client = await create_client();
|
|
412
401
|
|
|
413
|
-
t.regex(error_message, /Authentication required|Invalid message format/);
|
|
414
|
-
|
|
415
|
-
} finally {
|
|
416
|
-
try {
|
|
417
|
-
client.end();
|
|
418
|
-
await new Promise(resolve => setTimeout(resolve, 100));
|
|
419
|
-
} catch (error) {
|
|
420
|
-
// Ignore cleanup errors
|
|
421
|
-
}
|
|
422
402
|
try {
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
403
|
+
// Try backup operation without authentication
|
|
404
|
+
const backup_response = await send_message(client, {
|
|
405
|
+
op: 'admin',
|
|
406
|
+
data: { admin_action: 'backup_now' }
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
// Should fail due to lack of authentication
|
|
410
|
+
t.is(backup_response.ok, false);
|
|
411
|
+
t.truthy(backup_response.error);
|
|
412
|
+
|
|
413
|
+
// Handle both string and object error formats
|
|
414
|
+
const error_message = typeof backup_response.error === 'string'
|
|
415
|
+
? backup_response.error
|
|
416
|
+
: backup_response.error.message || JSON.stringify(backup_response.error);
|
|
417
|
+
|
|
418
|
+
t.regex(error_message, /Authentication required|Invalid message format/);
|
|
419
|
+
|
|
420
|
+
} finally {
|
|
421
|
+
try {
|
|
422
|
+
client.end();
|
|
423
|
+
await new Promise(resolve => setTimeout(resolve, 100));
|
|
424
|
+
} catch (error) {
|
|
425
|
+
// Ignore cleanup errors
|
|
426
|
+
}
|
|
427
|
+
try {
|
|
428
|
+
client.destroy();
|
|
429
|
+
} catch (error) {
|
|
430
|
+
// Ignore cleanup errors
|
|
431
|
+
}
|
|
426
432
|
}
|
|
433
|
+
} finally {
|
|
434
|
+
process.env.NODE_ENV = original_env;
|
|
427
435
|
}
|
|
428
436
|
});
|
|
429
437
|
|
|
@@ -48,12 +48,20 @@ test.afterEach(async (t) => {
|
|
|
48
48
|
});
|
|
49
49
|
|
|
50
50
|
test('load_or_generate_api_key generates new API key when file does not exist', (t) => {
|
|
51
|
-
|
|
51
|
+
// Temporarily set production mode to test actual key generation
|
|
52
|
+
const original_env = process.env.NODE_ENV;
|
|
53
|
+
process.env.NODE_ENV = 'production';
|
|
52
54
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
55
|
+
try {
|
|
56
|
+
const api_key = load_or_generate_api_key();
|
|
57
|
+
|
|
58
|
+
t.is(typeof api_key, 'string');
|
|
59
|
+
t.is(api_key.length, 32);
|
|
60
|
+
t.true(/^[A-Za-z0-9]{32}$/.test(api_key));
|
|
61
|
+
t.true(existsSync(API_KEY_FILE_PATH));
|
|
62
|
+
} finally {
|
|
63
|
+
process.env.NODE_ENV = original_env;
|
|
64
|
+
}
|
|
57
65
|
});
|
|
58
66
|
|
|
59
67
|
test('load_or_generate_api_key loads existing API key from file', (t) => {
|
|
@@ -64,12 +72,20 @@ test('load_or_generate_api_key loads existing API key from file', (t) => {
|
|
|
64
72
|
});
|
|
65
73
|
|
|
66
74
|
test('load_or_generate_api_key generates unique keys', (t) => {
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const second_key = load_or_generate_api_key();
|
|
75
|
+
// Temporarily set production mode to test actual key generation
|
|
76
|
+
const original_env = process.env.NODE_ENV;
|
|
77
|
+
process.env.NODE_ENV = 'production';
|
|
71
78
|
|
|
72
|
-
|
|
79
|
+
try {
|
|
80
|
+
const first_key = load_or_generate_api_key();
|
|
81
|
+
reset_api_key_state();
|
|
82
|
+
|
|
83
|
+
const second_key = load_or_generate_api_key();
|
|
84
|
+
|
|
85
|
+
t.not(first_key, second_key);
|
|
86
|
+
} finally {
|
|
87
|
+
process.env.NODE_ENV = original_env;
|
|
88
|
+
}
|
|
73
89
|
});
|
|
74
90
|
|
|
75
91
|
test('validate_api_key returns true for valid API key', (t) => {
|
|
@@ -80,18 +96,34 @@ test('validate_api_key returns true for valid API key', (t) => {
|
|
|
80
96
|
});
|
|
81
97
|
|
|
82
98
|
test('validate_api_key returns false for invalid API key', (t) => {
|
|
83
|
-
|
|
84
|
-
const
|
|
99
|
+
// Temporarily set production mode to test actual validation
|
|
100
|
+
const original_env = process.env.NODE_ENV;
|
|
101
|
+
process.env.NODE_ENV = 'production';
|
|
85
102
|
|
|
86
|
-
|
|
103
|
+
try {
|
|
104
|
+
load_or_generate_api_key();
|
|
105
|
+
const is_valid = validate_api_key('invalid_key');
|
|
106
|
+
|
|
107
|
+
t.false(is_valid);
|
|
108
|
+
} finally {
|
|
109
|
+
process.env.NODE_ENV = original_env;
|
|
110
|
+
}
|
|
87
111
|
});
|
|
88
112
|
|
|
89
113
|
test('validate_api_key returns false for null/undefined API key', (t) => {
|
|
90
|
-
|
|
114
|
+
// Temporarily set production mode to test actual validation
|
|
115
|
+
const original_env = process.env.NODE_ENV;
|
|
116
|
+
process.env.NODE_ENV = 'production';
|
|
91
117
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
118
|
+
try {
|
|
119
|
+
load_or_generate_api_key();
|
|
120
|
+
|
|
121
|
+
t.false(validate_api_key(null));
|
|
122
|
+
t.false(validate_api_key(undefined));
|
|
123
|
+
t.false(validate_api_key(''));
|
|
124
|
+
} finally {
|
|
125
|
+
process.env.NODE_ENV = original_env;
|
|
126
|
+
}
|
|
95
127
|
});
|
|
96
128
|
|
|
97
129
|
test('create_user creates user with valid data', async (t) => {
|
|
@@ -405,30 +437,54 @@ test('create_user sets admin flag when creating read_write user', async (t) => {
|
|
|
405
437
|
});
|
|
406
438
|
|
|
407
439
|
test('initialize_api_key_manager generates API key and checks for admin users', (t) => {
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
440
|
+
// Temporarily set production mode to test actual file creation
|
|
441
|
+
const original_env = process.env.NODE_ENV;
|
|
442
|
+
process.env.NODE_ENV = 'production';
|
|
411
443
|
|
|
412
|
-
|
|
444
|
+
try {
|
|
445
|
+
t.notThrows(() => {
|
|
446
|
+
initialize_api_key_manager();
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
t.true(existsSync(API_KEY_FILE_PATH));
|
|
450
|
+
} finally {
|
|
451
|
+
process.env.NODE_ENV = original_env;
|
|
452
|
+
}
|
|
413
453
|
});
|
|
414
454
|
|
|
415
455
|
test('reset_api_key_state cleans up API key file and state', (t) => {
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
reset_api_key_state();
|
|
456
|
+
// Temporarily set production mode to test actual file creation
|
|
457
|
+
const original_env = process.env.NODE_ENV;
|
|
458
|
+
process.env.NODE_ENV = 'production';
|
|
420
459
|
|
|
421
|
-
|
|
460
|
+
try {
|
|
461
|
+
load_or_generate_api_key();
|
|
462
|
+
t.true(existsSync(API_KEY_FILE_PATH));
|
|
463
|
+
|
|
464
|
+
reset_api_key_state();
|
|
465
|
+
|
|
466
|
+
t.false(existsSync(API_KEY_FILE_PATH));
|
|
467
|
+
} finally {
|
|
468
|
+
process.env.NODE_ENV = original_env;
|
|
469
|
+
}
|
|
422
470
|
});
|
|
423
471
|
|
|
424
472
|
test('API key file has secure permissions', (t) => {
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
const mode = stats.mode & parseInt('777', 8);
|
|
473
|
+
// Temporarily set production mode to test actual file creation
|
|
474
|
+
const original_env = process.env.NODE_ENV;
|
|
475
|
+
process.env.NODE_ENV = 'production';
|
|
429
476
|
|
|
430
|
-
|
|
431
|
-
|
|
477
|
+
try {
|
|
478
|
+
load_or_generate_api_key();
|
|
479
|
+
|
|
480
|
+
const stats = statSync(API_KEY_FILE_PATH);
|
|
481
|
+
const mode = stats.mode & parseInt('777', 8);
|
|
482
|
+
|
|
483
|
+
// Should be readable/writable by owner only (600)
|
|
484
|
+
t.is(mode, parseInt('600', 8));
|
|
485
|
+
} finally {
|
|
486
|
+
process.env.NODE_ENV = original_env;
|
|
487
|
+
}
|
|
432
488
|
});
|
|
433
489
|
|
|
434
490
|
test('password hashing uses bcrypt', async (t) => {
|
|
@@ -39,9 +39,9 @@ test('is_development_mode returns false when NODE_ENV is production', (t) => {
|
|
|
39
39
|
t.false(is_development_mode());
|
|
40
40
|
});
|
|
41
41
|
|
|
42
|
-
test('is_development_mode returns
|
|
42
|
+
test('is_development_mode returns true when NODE_ENV is test', (t) => {
|
|
43
43
|
process.env.NODE_ENV = 'test';
|
|
44
|
-
t.
|
|
44
|
+
t.true(is_development_mode());
|
|
45
45
|
});
|
|
46
46
|
|
|
47
47
|
test('is_development_mode returns false when NODE_ENV is undefined', (t) => {
|
|
@@ -583,19 +583,27 @@ test('admin operation - delete_document action', async (t) => {
|
|
|
583
583
|
});
|
|
584
584
|
|
|
585
585
|
test('admin operation - authentication required', async (t) => {
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
const response = await send_message(client, {
|
|
590
|
-
op: 'admin',
|
|
591
|
-
data: { admin_action: 'stats' }
|
|
592
|
-
});
|
|
593
|
-
|
|
594
|
-
t.is(response.ok, false);
|
|
595
|
-
t.truthy(response.error);
|
|
596
|
-
t.true(response.error.includes('Authentication required'));
|
|
586
|
+
// Temporarily set production mode to test authentication requirements
|
|
587
|
+
const original_env = process.env.NODE_ENV;
|
|
588
|
+
process.env.NODE_ENV = 'production';
|
|
597
589
|
|
|
598
|
-
|
|
590
|
+
try {
|
|
591
|
+
const client = await create_client();
|
|
592
|
+
|
|
593
|
+
// Try admin operation without authentication
|
|
594
|
+
const response = await send_message(client, {
|
|
595
|
+
op: 'admin',
|
|
596
|
+
data: { admin_action: 'stats' }
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
t.is(response.ok, false);
|
|
600
|
+
t.truthy(response.error);
|
|
601
|
+
t.true(response.error.includes('Authentication required'));
|
|
602
|
+
|
|
603
|
+
client.end();
|
|
604
|
+
} finally {
|
|
605
|
+
process.env.NODE_ENV = original_env;
|
|
606
|
+
}
|
|
599
607
|
});
|
|
600
608
|
|
|
601
609
|
test('admin operation - invalid collection name', async (t) => {
|
|
@@ -25,14 +25,20 @@ test('delete_one - should delete a document by filter', async (t) => {
|
|
|
25
25
|
});
|
|
26
26
|
|
|
27
27
|
test('delete_one - should only delete one matching document', async (t) => {
|
|
28
|
-
await insert_one('default', 'users', { name: 'Bob', group: 'g1' });
|
|
29
|
-
const { inserted_id } = await insert_one('default', 'users', { name: 'Carol', group: 'g1' });
|
|
28
|
+
const { inserted_id: bob_id } = await insert_one('default', 'users', { name: 'Bob', group: 'g1' });
|
|
29
|
+
const { inserted_id: carol_id } = await insert_one('default', 'users', { name: 'Carol', group: 'g1' });
|
|
30
30
|
const result = await delete_one('default', 'users', { group: 'g1' });
|
|
31
31
|
t.true(result.acknowledged);
|
|
32
32
|
t.is(result.deleted_count, 1);
|
|
33
|
+
|
|
34
|
+
// Check that exactly one document was deleted and one remains
|
|
33
35
|
const db = get_database();
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
+
const bob_doc = db.get(`default:users:${bob_id}`);
|
|
37
|
+
const carol_doc = db.get(`default:users:${carol_id}`);
|
|
38
|
+
|
|
39
|
+
// One should be deleted, one should remain
|
|
40
|
+
const remaining_docs = [bob_doc, carol_doc].filter(doc => doc !== undefined);
|
|
41
|
+
t.is(remaining_docs.length, 1, 'Exactly one document should remain');
|
|
36
42
|
});
|
|
37
43
|
|
|
38
44
|
test('delete_one - should return deleted_count 0 if no match', async (t) => {
|