@joystick.js/db-canary
Version:
JoystickDB - A minimalist database server for the Joystick framework
212 lines (179 loc) • 6.03 kB
JavaScript
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();
}
});