UNPKG

@joystick.js/db-canary

Version:

JoystickDB - A minimalist database server for the Joystick framework

220 lines (187 loc) 6.16 kB
import test from 'ava'; import fs from 'fs'; import net from 'net'; import { create_server } from '../../../src/server/index.js'; import { setup_initial_admin, reset_auth_state } from '../../../src/server/lib/user_auth_manager.js'; import { initialize_database } from '../../../src/server/lib/query_engine.js'; import { encode_message, create_message_parser } from '../../../src/server/lib/tcp_protocol.js'; const AUTH_FILE_PATH = './settings.db.json'; // Shared server instance let shared_server = null; let shared_port = null; let admin_username = 'admin'; let admin_password = 'admin123'; test.before(async () => { // Set test environment process.env.NODE_ENV = 'test'; // Reset auth state and clean up any existing auth files await reset_auth_state(); if (fs.existsSync(AUTH_FILE_PATH)) { fs.unlinkSync(AUTH_FILE_PATH); } // Initialize database for testing initialize_database(); // Create shared server instance shared_server = await create_server(); // Start server on random port await new Promise((resolve, reject) => { const timeout = setTimeout(() => { reject(new Error('Server start timeout')); }, 5000); shared_server.listen(0, (error) => { clearTimeout(timeout); if (error) { reject(error); } else { shared_port = shared_server.address().port; resolve(); } }); }); // Set up initial admin user await setup_initial_admin(admin_username, admin_password, 'admin@test.com'); }); test.after.always(async () => { // Clean up shared server if (shared_server) { try { if (shared_server.cleanup) { await shared_server.cleanup(); } } catch (error) { // Ignore cleanup errors } await new Promise((resolve) => { const timeout = setTimeout(() => { try { shared_server.close(); } catch (error) { // Ignore errors } resolve(); }, 2000); try { shared_server.close(() => { clearTimeout(timeout); resolve(); }); } catch (error) { clearTimeout(timeout); resolve(); } }); shared_server = null; } // Reset auth state and clean up auth files await reset_auth_state(); if (fs.existsSync(AUTH_FILE_PATH)) { try { fs.unlinkSync(AUTH_FILE_PATH); } catch (error) { // Ignore cleanup errors } } // Force garbage collection if available if (global.gc) { global.gc(); } }); test.beforeEach(async () => { // Ensure admin user is set up for each test await setup_initial_admin(admin_username, admin_password, 'admin@test.com'); }); const create_client = () => { return new Promise((resolve, reject) => { const client = net.createConnection(shared_port, 'localhost'); const parser = create_message_parser(); client.on('connect', () => { resolve({ client, send: (data) => { const encoded = encode_message(data); client.write(encoded); }, receive: () => { return new Promise((resolve) => { const handler = (data) => { try { const messages = parser.parse_messages(data); for (const message of messages) { client.off('data', handler); resolve(message); return; } } catch (error) { // Continue listening } }; client.on('data', handler); }); }, close: () => { client.end(); } }); }); client.on('error', reject); }); }; test('replication admin operations - get status when disabled', async t => { const { client, send, receive, close } = await create_client(); try { // First authenticate with username and password send({ op: 'authentication', data: { username: admin_username, password: admin_password } }); const auth_response = await receive(); t.is(auth_response.ok, 1); // Get admin status send({ op: 'admin' }); const admin_response = await receive(); // Admin response should include server info t.is(typeof admin_response.server, 'object'); t.is(typeof admin_response.database, 'object'); t.is(typeof admin_response.authentication, 'object'); } finally { close(); } }); test('replication integration - basic server functionality works', async t => { const { client, send, receive, close } = await create_client(); try { // First authenticate with username and password send({ op: 'authentication', data: { username: admin_username, password: admin_password } }); const auth_response = await receive(); t.is(auth_response.ok, 1); // Test basic ping operation send({ op: 'ping' }); const ping_response = await receive(); t.is(ping_response.ok, 1); // Test admin operation works send({ op: 'admin' }); const admin_response = await receive(); // Admin response should include basic server info t.is(typeof admin_response.server, 'object'); t.is(typeof admin_response.database, 'object'); } finally { close(); } }); test('replication integration - database operations work normally', async t => { const { client, send, receive, close } = await create_client(); try { // First authenticate with username and password send({ op: 'authentication', data: { username: admin_username, password: admin_password } }); const auth_response = await receive(); t.is(auth_response.ok, 1); // Test insert operation send({ op: 'insert_one', data: { collection: 'test', document: { name: 'test', value: 1 } } }); const insert_response = await receive(); t.is(insert_response.ok, 1); // Test find operation send({ op: 'find_one', data: { collection: 'test', filter: { name: 'test' } } }); const find_response = await receive(); t.is(find_response.ok, 1); t.is(find_response.document.name, 'test'); } finally { close(); } });