milodb
Version:
A simple mini database with optional encryption to store key-value pairs
159 lines (136 loc) • 6.03 kB
JavaScript
const assert = require('assert');
const fs = require('fs').promises;
const path = require('path');
const MinoDB = require('../src/index');
// Helper function to clean up test database
async function cleanup() {
try {
await fs.unlink(path.join(__dirname, '..', 'db', 'database.json'));
} catch (err) {
if (err.code !== 'ENOENT') throw err;
}
}
// Helper function to delay execution
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
async function runTests() {
console.log('Starting tests...\n');
// Test 1: Basic Operations
console.log('Test 1: Basic Operations');
const db = new MinoDB('test-password', true);
// Test set and get
await db.set('test-key', 'test-value');
const value = await db.get('test-key');
assert.strictEqual(value, 'test-value', 'Basic set/get failed');
console.log('✓ Basic set/get passed');
// Test delete
await db.delete('test-key');
const deletedValue = await db.get('test-key');
assert.strictEqual(deletedValue, null, 'Delete operation failed');
console.log('✓ Delete operation passed');
// Test 2: TTL Support
console.log('\nTest 2: TTL Support');
await db.set('temp-key', 'temp-value', 1000); // 1 second TTL
const tempValue = await db.get('temp-key');
assert.strictEqual(tempValue, 'temp-value', 'TTL set failed');
console.log('✓ TTL set passed');
await delay(1100); // Wait for TTL to expire
const expiredValue = await db.get('temp-key');
assert.strictEqual(expiredValue, null, 'TTL expiration failed');
console.log('✓ TTL expiration passed');
// Test 3: Batch Operations
console.log('\nTest 3: Batch Operations');
const batchResults = await db.batch([
{ type: 'set', key: 'batch1', value: 'value1' },
{ type: 'set', key: 'batch2', value: 'value2', ttl: 1000 },
{ type: 'delete', key: 'batch1' }
]);
assert.strictEqual(batchResults.length, 3, 'Batch operation count mismatch');
assert.strictEqual(batchResults[0].success, true, 'Batch set failed');
assert.strictEqual(batchResults[1].success, true, 'Batch set with TTL failed');
assert.strictEqual(batchResults[2].success, true, 'Batch delete failed');
console.log('✓ Batch operations passed');
// Test 4: Search Operations
console.log('\nTest 4: Search Operations');
await db.set('search1', 'hello world');
await db.set('search2', 'hello universe');
await db.set('search3', 'different text');
// Test basic search
const searchResults = await db.search('hello');
assert.strictEqual(Object.keys(searchResults).length, 2, 'Basic search failed');
console.log('✓ Basic search passed');
// Test regex search
const regexResults = await db.search('hello', { regex: true });
assert.strictEqual(Object.keys(regexResults).length, 2, 'Regex search failed');
console.log('✓ Regex search passed');
// Test case-sensitive search
const caseResults = await db.search('HELLO', { caseSensitive: true });
assert.strictEqual(Object.keys(caseResults).length, 0, 'Case-sensitive search failed');
console.log('✓ Case-sensitive search passed');
// Test 5: Database Statistics
console.log('\nTest 5: Database Statistics');
const stats = await db.stats();
assert.strictEqual(typeof stats.total, 'number', 'Stats total type mismatch');
assert.strictEqual(typeof stats.expired, 'number', 'Stats expired type mismatch');
assert.strictEqual(typeof stats.active, 'number', 'Stats active type mismatch');
console.log('✓ Database statistics passed');
// Test 6: Error Handling
console.log('\nTest 6: Error Handling');
// Test invalid key type
try {
await db.set(123, 'value');
assert.fail('Should have thrown error for invalid key type');
} catch (err) {
assert.strictEqual(err.message, 'Key must be a string', 'Invalid key type error message mismatch');
}
console.log('✓ Invalid key type handling passed');
// Test empty key
try {
await db.set('', 'value');
assert.fail('Should have thrown error for empty key');
} catch (err) {
assert.strictEqual(err.message, 'Key cannot be empty', 'Empty key error message mismatch');
}
console.log('✓ Empty key handling passed');
// Test undefined value
try {
await db.set('key', undefined);
assert.fail('Should have thrown error for undefined value');
} catch (err) {
assert.strictEqual(err.message, 'Value cannot be undefined', 'Undefined value error message mismatch');
}
console.log('✓ Undefined value handling passed');
// Test 7: Encryption
console.log('\nTest 7: Encryption');
const encDbPath = path.join(__dirname, '..', 'db', 'encryption-test.json');
// Clean up before test
try { await fs.unlink(encDbPath); } catch (e) {}
const EncryptedMinoDB = require('../src/index');
const encryptedDb = new EncryptedMinoDB('encryption-password', true);
encryptedDb.dbPath = encDbPath;
await encryptedDb.set('encrypted-key', 'sensitive-data');
// Try to read with wrong password
const wrongDb = new EncryptedMinoDB('wrong-password', true);
wrongDb.dbPath = encDbPath;
try {
await wrongDb.get('encrypted-key');
assert.fail('Should have thrown error for wrong password');
} catch (err) {
assert(
err.message.includes('decrypt') ||
err.message.includes('unable to authenticate') ||
err.message.includes('Unsupported state'),
'Wrong password error message mismatch'
);
}
console.log('✓ Encryption handling passed');
// Clean up after test
try { await fs.unlink(encDbPath); } catch (e) {}
// Cleanup
await cleanup();
console.log('\nAll tests passed successfully! 🎉');
}
// Run the tests
runTests().catch(err => {
console.error('Test failed:', err);
process.exit(1);
});