UNPKG

@joystick.js/db-canary

Version:

JoystickDB - A minimalist database server for the Joystick framework

212 lines (179 loc) 6.03 kB
import test from 'ava'; import net from 'net'; import { create_server } from '../../../src/server/index.js'; import { encode_message, create_message_parser } from '../../../src/server/lib/tcp_protocol.js'; import { initialize_database, cleanup_database } from '../../../src/server/lib/query_engine.js'; import { reset_auth_state } from '../../../src/server/lib/user_auth_manager.js'; // Dynamic port allocation let current_port = 4000; const get_next_port = () => { return ++current_port; }; let server; let port; let original_node_env; test.beforeEach(async () => { // Store original NODE_ENV original_node_env = process.env.NODE_ENV; // Set NODE_ENV to development process.env.NODE_ENV = 'development'; // Reset auth state and clean up environment variables await reset_auth_state(); delete process.env.JOYSTICK_DB_SETTINGS; // Initialize database for testing initialize_database(); // Create server with dynamic port const test_port = get_next_port(); server = await create_server(); // Start server on specific port await new Promise((resolve) => { server.listen(test_port, () => { port = test_port; setTimeout(resolve, 100); }); }); }); test.afterEach(async () => { // Restore original NODE_ENV process.env.NODE_ENV = original_node_env; if (server) { await server.cleanup(); await new Promise((resolve) => { server.close(resolve); }); server = null; } // Clean up database try { await cleanup_database(true); // Remove test database directory } catch (error) { // Ignore cleanup errors } // Clean up environment variables await reset_auth_state(); delete process.env.JOYSTICK_DB_SETTINGS; }); const create_client = () => { return new Promise((resolve, reject) => { const client = net.createConnection(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('development mode - database operations work without authentication', async (t) => { const { client, send, receive, close } = await create_client(); try { // Try ping operation without authentication - should work in development mode send({ op: 'ping' }); const response = await receive(); t.is(response.ok, 1); // Ping operation just returns { ok: 1 } without a message } finally { close(); } }); test('development mode - find operation works without authentication', async (t) => { const { client, send, receive, close } = await create_client(); try { // Try find operation without authentication - should work in development mode send({ op: 'find', data: { collection: 'users', filter: {} } }); const response = await receive(); t.is(response.ok, 1); t.true(Array.isArray(response.documents)); } finally { close(); } }); test('development mode - insert operation works without authentication', async (t) => { const { client, send, receive, close } = await create_client(); try { // Try insert operation without authentication - should work in development mode send({ op: 'insert_one', data: { collection: 'users', document: { name: 'Test User', email: 'test@example.com' } } }); const response = await receive(); t.is(response.ok, 1); t.true(typeof response.inserted_id === 'string'); } finally { close(); } }); test('development mode - admin operation works without authentication', async (t) => { const { client, send, receive, close } = await create_client(); try { // Try admin operation without authentication - should work in development mode send({ op: 'admin' }); const response = await receive(); t.true(typeof response.authentication === 'object'); t.is(response.authentication.authenticated_clients, 0); // No clients authenticated since we bypassed auth t.true(typeof response.server === 'object'); t.true(typeof response.database === 'object'); } finally { close(); } }); test('development mode - authentication still works if explicitly used', async (t) => { const { client, send, receive, close } = await create_client(); try { // Setup authentication first send({ op: 'admin', data: { admin_action: 'setup_initial_admin', username: 'admin', password: 'admin123', email: 'admin@test.com' } }); const setup_response = await receive(); t.true(setup_response.ok === 1 || setup_response.ok === true); // Check for message existence and content with null safety if (setup_response.message) { t.true(setup_response.message.includes('Initial admin user created')); } else { // Just verify the operation succeeded t.pass('Setup operation completed successfully'); } // Now authenticate with the username and password send({ op: 'authentication', data: { username: 'admin', password: 'admin123' } }); const auth_response = await receive(); t.is(auth_response.ok, 1); t.is(auth_response.version, '1.0.0'); t.is(auth_response.message, 'Authentication successful'); } finally { close(); } });