Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 121 additions & 0 deletions tests/unit/sqlite_db.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@

import { describe, it, before, after } from 'node:test';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The after function is imported but not used within this test suite. To maintain code cleanliness and avoid confusion, it's best to remove unused imports.

Suggested change
import { describe, it, before, after } from 'node:test';
import { describe, it, before } from 'node:test';

import assert from 'node:assert';
import { createDatabaseEngine } from '../../src/core/sqlite-db';
import type { DatabaseOperations, CellUpdate } from '../../src/core/types';

describe('WasmDatabaseEngine', () => {
let engine: DatabaseOperations;

before(async () => {
// Initialize with empty DB
const result = await createDatabaseEngine({
content: null,
maxSize: 0,
readOnlyMode: false
});
engine = result.operations;

// Setup table
await engine.executeQuery("CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, age INTEGER, data TEXT)");
});

it('should update multiple cells in a batch', async () => {
// Setup initial data
await engine.executeQuery("DELETE FROM users");
await engine.insertRow('users', { id: 1, name: 'Alice', age: 30, data: '{}' });
await engine.insertRow('users', { id: 2, name: 'Bob', age: 25, data: '{}' });

const updates: CellUpdate[] = [
{ rowId: 1, column: 'name', value: 'Alice Smith' },
{ rowId: 2, column: 'age', value: 26 }
];

await engine.updateCellBatch('users', updates);

const result = await engine.executeQuery("SELECT * FROM users ORDER BY id");
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Using SELECT * can lead to brittle tests because the order of columns is not guaranteed. If the table schema changes, tests that rely on column indices can fail in non-obvious ways. It's better to explicitly select the columns you need for your assertions. This makes the test more robust and self-documenting.

Suggested change
const result = await engine.executeQuery("SELECT * FROM users ORDER BY id");
const result = await engine.executeQuery("SELECT id, name, age FROM users ORDER BY id");

const rows = result[0].rows;

// Alice updated
assert.strictEqual(rows[0][1], 'Alice Smith');
assert.strictEqual(rows[0][2], 30); // Age unchanged

// Bob updated
assert.strictEqual(rows[1][1], 'Bob'); // Name unchanged
assert.strictEqual(rows[1][2], 26);
});

it('should handle JSON patch updates', async () => {
// Setup initial data
await engine.executeQuery("DELETE FROM users");
await engine.insertRow('users', { id: 1, name: 'Charlie', age: 40, data: '{"a": 1, "b": 2}' });

const updates: CellUpdate[] = [
{ rowId: 1, column: 'data', value: '{"b": 3, "c": 4}', operation: 'json_patch' }
];

await engine.updateCellBatch('users', updates);

const result = await engine.executeQuery("SELECT data FROM users WHERE id = 1");
const dataStr = result[0].rows[0][0] as string;
const data = JSON.parse(dataStr);

assert.deepStrictEqual(data, { a: 1, b: 3, c: 4 });
});

it('should handle mixed standard and JSON patch updates', async () => {
// Setup initial data
await engine.executeQuery("DELETE FROM users");
await engine.insertRow('users', { id: 1, name: 'David', age: 50, data: '{"x": 10}' });
await engine.insertRow('users', { id: 2, name: 'Eve', age: 55, data: '{"y": 20}' });

const updates: CellUpdate[] = [
{ rowId: 1, column: 'name', value: 'David Jones' },
{ rowId: 1, column: 'data', value: '{"x": 11}', operation: 'json_patch' },
{ rowId: 2, column: 'age', value: 56 },
{ rowId: 2, column: 'data', value: '{"z": 30}', operation: 'json_patch' }
];

await engine.updateCellBatch('users', updates);

const result = await engine.executeQuery("SELECT name, age, data FROM users ORDER BY id");
const rows = result[0].rows;

// David
assert.strictEqual(rows[0][0], 'David Jones');
assert.strictEqual(rows[0][1], 50); // Age unchanged
assert.deepStrictEqual(JSON.parse(rows[0][2] as string), { x: 11 });

// Eve
assert.strictEqual(rows[1][0], 'Eve');
assert.strictEqual(rows[1][1], 56);
assert.deepStrictEqual(JSON.parse(rows[1][2] as string), { y: 20, z: 30 });
});

it('should handle empty updates gracefully', async () => {
await engine.updateCellBatch('users', []);
assert.ok(true);
Comment on lines +96 to +97
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The assertion assert.ok(true) only confirms that the function didn't throw an error, which is a weak test. A more robust test would verify that the database state remains unchanged after calling updateCellBatch with an empty array. You could insert a known row, call the function, and then query that row to ensure its data has not been modified.

Suggested change
await engine.updateCellBatch('users', []);
assert.ok(true);
await engine.executeQuery("DELETE FROM users");
await engine.insertRow('users', { id: 1, name: 'NoOp', age: 99, data: '{}' });
await engine.updateCellBatch('users', []);
const result = await engine.executeQuery("SELECT name, age FROM users WHERE id = 1");
const row = result[0].rows[0];
assert.strictEqual(row[0], 'NoOp');
assert.strictEqual(row[1], 99);

});

it('should rollback transaction on error', async () => {
// Setup initial data
await engine.executeQuery("DELETE FROM users");
await engine.insertRow('users', { id: 1, name: 'Frank', age: 60, data: '{}' });

const updates: CellUpdate[] = [
{ rowId: 1, column: 'name', value: 'Frank Sinatra' }, // Valid update
{ rowId: 1, column: 'non_existent_column', value: 'Error' } // Invalid update
];

try {
await engine.updateCellBatch('users', updates);
assert.fail('Should have thrown an error');
} catch (e) {
assert.ok(e);
}
Comment on lines +110 to +115
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Instead of a try/catch block with assert.fail, you can use assert.rejects for a more concise and readable way to test for expected promise rejections. This is the modern standard for testing async errors in node:test and allows for more specific error validation, such as matching the error message.

        await assert.rejects(
            engine.updateCellBatch('users', updates),
            /no such column: non_existent_column/i,
            'Should have thrown an error for invalid column'
        );


// Verify rollback
const result = await engine.executeQuery("SELECT name FROM users WHERE id = 1");
assert.strictEqual(result[0].rows[0][0], 'Frank'); // Should remain original value
});
});