@joystick.js/db-canary
Version:
JoystickDB - A minimalist database server for the Joystick framework
276 lines (217 loc) • 7.99 kB
JavaScript
import test from 'ava';
import {
initialize_auto_index_database,
cleanup_auto_index_database,
record_query,
record_index_usage,
get_query_statistics,
get_auto_index_statistics,
force_index_evaluation,
remove_automatic_indexes,
is_auto_created_index
} from '../../../src/server/lib/auto_index_manager.js';
import { initialize_database, cleanup_database } from '../../../src/server/lib/query_engine.js';
import { initialize_index_database, create_index, get_indexes, cleanup_index_database } from '../../../src/server/lib/index_manager.js';
test.beforeEach(async (t) => {
initialize_database('./test_data');
initialize_index_database();
initialize_auto_index_database();
});
test.afterEach(async (t) => {
cleanup_auto_index_database();
cleanup_index_database();
await cleanup_database();
});
test('should initialize auto index database', (t) => {
t.notThrows(() => {
initialize_auto_index_database();
});
});
test('should record query statistics', (t) => {
const collection = 'test_collection';
const filter = { name: 'John', age: 25 };
const execution_time = 50;
t.notThrows(() => {
record_query(collection, filter, execution_time, false);
});
const stats = get_query_statistics(collection);
t.truthy(stats.name);
t.truthy(stats.age);
t.is(stats.name.query_count, 1);
t.is(stats.age.query_count, 1);
t.is(stats.name.total_time_ms, execution_time);
t.is(stats.age.total_time_ms, execution_time);
});
test('should record multiple queries and aggregate statistics', (t) => {
const collection = 'test_collection';
const filter = { name: 'John' };
record_query(collection, filter, 30, false);
record_query(collection, filter, 50, false);
record_query(collection, filter, 70, false);
const stats = get_query_statistics(collection);
t.is(stats.name.query_count, 3);
t.is(stats.name.total_time_ms, 150);
t.is(stats.name.avg_time_ms, 50);
});
test('should track slow queries', (t) => {
const collection = 'test_collection';
const filter = { name: 'John' };
record_query(collection, filter, 100, false);
record_query(collection, filter, 20, false);
record_query(collection, filter, 80, false);
const stats = get_query_statistics(collection);
t.is(stats.name.slow_query_count, 2);
});
test('should record index usage', (t) => {
const collection = 'test_collection';
const field = 'name';
record_query(collection, { [field]: 'John' }, 30, true);
record_index_usage(collection, field);
const stats = get_query_statistics(collection);
t.is(stats[field].used_index_count, 1);
});
test('should get query statistics for all collections', (t) => {
record_query('collection1', { name: 'John' }, 30, false);
record_query('collection2', { age: 25 }, 40, false);
const all_stats = get_query_statistics();
t.truthy(all_stats.collection1);
t.truthy(all_stats.collection2);
t.truthy(all_stats.collection1.name);
t.truthy(all_stats.collection2.age);
});
test('should get empty statistics for non-existent collection', (t) => {
const stats = get_query_statistics('non_existent');
t.deepEqual(stats, {});
});
test('should get auto index statistics', (t) => {
const stats = get_auto_index_statistics();
t.is(typeof stats, 'object');
t.is(stats.total_auto_indexes, 0);
t.is(typeof stats.collections, 'object');
});
test('should force index evaluation', async (t) => {
const collection = 'test_collection';
const field = 'name';
for (let i = 0; i < 150; i++) {
record_query(collection, { [field]: `user${i}` }, 60, false);
}
const result = await force_index_evaluation(collection);
t.is(result.acknowledged, true);
});
test('should force index evaluation for all collections', async (t) => {
record_query('collection1', { name: 'John' }, 60, false);
record_query('collection2', { age: 25 }, 70, false);
const result = await force_index_evaluation();
t.is(result.acknowledged, true);
});
test('should remove automatic indexes', async (t) => {
const collection = 'test_collection';
const field = 'name';
await create_index('default', collection, field);
const result = await remove_automatic_indexes(collection, [field]);
t.is(result.acknowledged, true);
t.is(result.removed_count, 0);
});
test('should remove all automatic indexes from collection', async (t) => {
const collection = 'test_collection';
const result = await remove_automatic_indexes(collection);
t.is(result.acknowledged, true);
t.is(typeof result.removed_count, 'number');
});
test('should identify auto-created indexes', (t) => {
const collection = 'test_collection';
const field = 'name';
const is_auto = is_auto_created_index(collection, field);
t.is(typeof is_auto, 'boolean');
});
test('should handle excluded fields', (t) => {
const collection = 'test_collection';
const filter = { _id: '123', created_at: new Date(), name: 'John' };
record_query(collection, filter, 30, false);
const stats = get_query_statistics(collection);
t.falsy(stats._id);
t.falsy(stats.created_at);
t.truthy(stats.name);
});
test('should handle complex filter objects', (t) => {
const collection = 'test_collection';
const filter = {
name: { $regex: 'John' },
age: { $gt: 18, $lt: 65 },
status: { $in: ['active', 'pending'] }
};
record_query(collection, filter, 45, false);
const stats = get_query_statistics(collection);
t.truthy(stats.name);
t.truthy(stats.age);
t.truthy(stats.status);
t.is(stats.name.query_count, 1);
t.is(stats.age.query_count, 1);
t.is(stats.status.query_count, 1);
});
test('should handle null and undefined filters', (t) => {
const collection = 'test_collection';
t.notThrows(() => {
record_query(collection, null, 30, false);
record_query(collection, undefined, 30, false);
record_query(collection, {}, 30, false);
});
const stats = get_query_statistics(collection);
t.deepEqual(stats, {});
});
test('should handle invalid collection names', (t) => {
t.notThrows(() => {
record_query('', { name: 'John' }, 30, false);
record_query(null, { name: 'John' }, 30, false);
record_query(undefined, { name: 'John' }, 30, false);
});
});
test('should handle negative execution times', (t) => {
const collection = 'test_collection';
const filter = { name: 'John' };
t.notThrows(() => {
record_query(collection, filter, -10, false);
});
const stats = get_query_statistics(collection);
t.is(stats.name.total_time_ms, -10);
});
test('should handle very large execution times', (t) => {
const collection = 'test_collection';
const filter = { name: 'John' };
t.notThrows(() => {
record_query(collection, filter, 999999, false);
});
const stats = get_query_statistics(collection);
t.is(stats.name.total_time_ms, 999999);
t.is(stats.name.slow_query_count, 1);
});
test('should update last_queried timestamp', (t) => {
const collection = 'test_collection';
const filter = { name: 'John' };
const before = new Date();
record_query(collection, filter, 30, false);
const stats = get_query_statistics(collection);
const after = new Date();
t.true(stats.name.last_queried >= before);
t.true(stats.name.last_queried <= after);
});
test('should handle concurrent query recording', (t) => {
const collection = 'test_collection';
const filter = { name: 'John' };
for (let i = 0; i < 100; i++) {
record_query(collection, filter, 30 + i, false);
}
const stats = get_query_statistics(collection);
t.is(stats.name.query_count, 100);
t.is(stats.name.total_time_ms, 100 * 30 + (99 * 100) / 2);
});
test('should cleanup auto index database', (t) => {
const collection = 'test_collection';
const filter = { name: 'John' };
record_query(collection, filter, 30, false);
t.notThrows(() => {
cleanup_auto_index_database();
});
const stats = get_query_statistics(collection);
t.deepEqual(stats, {});
});