Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'
This commit is contained in:
123
vendor/ruvector/crates/rvlite/examples/dashboard/scripts/debug-keys.mjs
vendored
Normal file
123
vendor/ruvector/crates/rvlite/examples/dashboard/scripts/debug-keys.mjs
vendored
Normal file
@@ -0,0 +1,123 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Debug key format mismatch in SPARQL triple store
|
||||
*/
|
||||
|
||||
import { readFile } from 'fs/promises';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, join } from 'path';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
async function test() {
|
||||
console.log('=== Debug Key Format Mismatch ===\n');
|
||||
|
||||
// Load WASM
|
||||
const wasmPath = join(__dirname, '../public/pkg/rvlite_bg.wasm');
|
||||
const wasmBytes = await readFile(wasmPath);
|
||||
const rvliteModule = await import('../public/pkg/rvlite.js');
|
||||
const { default: initRvLite, RvLite, RvLiteConfig } = rvliteModule;
|
||||
await initRvLite(wasmBytes);
|
||||
|
||||
const config = new RvLiteConfig(384);
|
||||
const db = new RvLite(config);
|
||||
console.log('✓ WASM initialized');
|
||||
|
||||
// Test 1: Check what format the add_triple expects
|
||||
console.log('\n=== Test 1: Adding triples with different formats ===');
|
||||
|
||||
// Format A: With angle brackets
|
||||
try {
|
||||
db.add_triple('<http://ex.org/a1>', '<http://ex.org/p1>', '<http://ex.org/o1>');
|
||||
console.log('✓ Format A (with <>) accepted');
|
||||
} catch (e) {
|
||||
console.log('✗ Format A (with <>) rejected:', e.message);
|
||||
}
|
||||
|
||||
// Format B: Without angle brackets
|
||||
try {
|
||||
db.add_triple('http://ex.org/a2', 'http://ex.org/p2', 'http://ex.org/o2');
|
||||
console.log('✓ Format B (without <>) accepted');
|
||||
} catch (e) {
|
||||
console.log('✗ Format B (without <>) rejected:', e.message);
|
||||
}
|
||||
|
||||
console.log(`Total triples: ${db.triple_count()}`);
|
||||
|
||||
// Test 2: Try SPARQL queries that match each format
|
||||
console.log('\n=== Test 2: SPARQL queries with different predicate formats ===');
|
||||
|
||||
const testQueries = [
|
||||
// Query for triples added with format A
|
||||
'SELECT ?s WHERE { ?s <http://ex.org/p1> ?o }',
|
||||
// Query for triples added with format B
|
||||
'SELECT ?s WHERE { ?s <http://ex.org/p2> ?o }',
|
||||
// Wildcard predicate (variable)
|
||||
// 'SELECT ?s ?p WHERE { ?s ?p ?o }', // This fails with "Complex property paths not yet supported"
|
||||
];
|
||||
|
||||
for (const query of testQueries) {
|
||||
console.log(`\nQuery: ${query}`);
|
||||
try {
|
||||
const result = db.sparql(query);
|
||||
console.log('Result:', JSON.stringify(result, null, 2));
|
||||
} catch (e) {
|
||||
console.log('ERROR:', e.message || e);
|
||||
}
|
||||
}
|
||||
|
||||
// Test 3: Add rdf:type triple and test with actual RDF type query
|
||||
console.log('\n=== Test 3: RDF type query ===');
|
||||
|
||||
db.add_triple(
|
||||
'<http://example.org/Alice>',
|
||||
'<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>',
|
||||
'<http://example.org/Person>'
|
||||
);
|
||||
console.log(`Triple count after adding rdf:type: ${db.triple_count()}`);
|
||||
|
||||
const typeQuery = 'SELECT ?s WHERE { ?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Person> }';
|
||||
console.log(`Query: ${typeQuery}`);
|
||||
try {
|
||||
const result = db.sparql(typeQuery);
|
||||
console.log('Result:', JSON.stringify(result, null, 2));
|
||||
|
||||
if (result.bindings && result.bindings.length > 0) {
|
||||
console.log('✓ SPARQL is working!');
|
||||
} else {
|
||||
console.log('✗ No bindings returned - key mismatch suspected');
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('ERROR:', e.message || e);
|
||||
}
|
||||
|
||||
// Test 4: Simple triple with known data
|
||||
console.log('\n=== Test 4: Minimal test case ===');
|
||||
|
||||
db.add_triple('<http://a>', '<http://b>', '<http://c>');
|
||||
console.log(`Triple count: ${db.triple_count()}`);
|
||||
|
||||
const minimalQuery = 'SELECT ?s WHERE { ?s <http://b> ?o }';
|
||||
console.log(`Query: ${minimalQuery}`);
|
||||
try {
|
||||
const result = db.sparql(minimalQuery);
|
||||
console.log('Result:', JSON.stringify(result, null, 2));
|
||||
} catch (e) {
|
||||
console.log('ERROR:', e.message || e);
|
||||
}
|
||||
|
||||
// Test 5: Get all triples using 'a' keyword (rdf:type shortcut)
|
||||
console.log('\n=== Test 5: Using "a" keyword ===');
|
||||
const aQuery = 'SELECT ?s WHERE { ?s a <http://example.org/Person> }';
|
||||
console.log(`Query: ${aQuery}`);
|
||||
try {
|
||||
const result = db.sparql(aQuery);
|
||||
console.log('Result:', JSON.stringify(result, null, 2));
|
||||
} catch (e) {
|
||||
console.log('ERROR:', e.message || e);
|
||||
}
|
||||
|
||||
db.free();
|
||||
}
|
||||
|
||||
test().catch(console.error);
|
||||
74
vendor/ruvector/crates/rvlite/examples/dashboard/scripts/debug-sparql.mjs
vendored
Normal file
74
vendor/ruvector/crates/rvlite/examples/dashboard/scripts/debug-sparql.mjs
vendored
Normal file
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Debug SPARQL execution to understand why results are empty
|
||||
*/
|
||||
|
||||
import { readFile } from 'fs/promises';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, join } from 'path';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
async function test() {
|
||||
console.log('=== Debug SPARQL Execution ===\n');
|
||||
|
||||
// Load WASM
|
||||
const wasmPath = join(__dirname, '../public/pkg/rvlite_bg.wasm');
|
||||
const wasmBytes = await readFile(wasmPath);
|
||||
const rvliteModule = await import('../public/pkg/rvlite.js');
|
||||
const { default: initRvLite, RvLite, RvLiteConfig } = rvliteModule;
|
||||
await initRvLite(wasmBytes);
|
||||
|
||||
const config = new RvLiteConfig(384);
|
||||
const db = new RvLite(config);
|
||||
console.log('✓ WASM initialized');
|
||||
|
||||
// Add triples
|
||||
console.log('\n=== Adding Triples ===');
|
||||
const triples = [
|
||||
['<http://example.org/Alice>', '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>', '<http://example.org/Person>'],
|
||||
['<http://example.org/Alice>', '<http://example.org/name>', '"Alice"'],
|
||||
['<http://example.org/Bob>', '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>', '<http://example.org/Person>'],
|
||||
['<http://example.org/Bob>', '<http://example.org/name>', '"Bob"'],
|
||||
['<http://example.org/Alice>', '<http://example.org/knows>', '<http://example.org/Bob>'],
|
||||
];
|
||||
|
||||
for (const [s, p, o] of triples) {
|
||||
try {
|
||||
db.add_triple(s, p, o);
|
||||
console.log(` Added: ${s} ${p} ${o}`);
|
||||
} catch (e) {
|
||||
console.log(` ERROR adding triple: ${e.message}`);
|
||||
}
|
||||
}
|
||||
console.log(`Triple count: ${db.triple_count()}`);
|
||||
|
||||
// Test queries with full debug output
|
||||
console.log('\n=== Testing SPARQL Queries ===');
|
||||
|
||||
const queries = [
|
||||
// Simple SELECT with variable predicate
|
||||
"SELECT ?s ?p ?o WHERE { ?s ?p ?o }",
|
||||
// SELECT with specific predicate
|
||||
"SELECT ?s WHERE { ?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Person> }",
|
||||
// SELECT with specific predicate (no angle brackets in predicate)
|
||||
"SELECT ?s WHERE { ?s <http://example.org/knows> ?o }",
|
||||
// ASK query
|
||||
"ASK { <http://example.org/Alice> <http://example.org/knows> <http://example.org/Bob> }",
|
||||
];
|
||||
|
||||
for (const query of queries) {
|
||||
console.log(`\nQuery: ${query}`);
|
||||
try {
|
||||
const result = db.sparql(query);
|
||||
console.log('Result type:', typeof result);
|
||||
console.log('Result:', JSON.stringify(result, null, 2));
|
||||
} catch (e) {
|
||||
console.log('ERROR:', e.message || e);
|
||||
}
|
||||
}
|
||||
|
||||
db.free();
|
||||
}
|
||||
|
||||
test().catch(console.error);
|
||||
318
vendor/ruvector/crates/rvlite/examples/dashboard/scripts/e2e-wasm-test.mjs
vendored
Normal file
318
vendor/ruvector/crates/rvlite/examples/dashboard/scripts/e2e-wasm-test.mjs
vendored
Normal file
@@ -0,0 +1,318 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Comprehensive E2E Test for RvLite WASM
|
||||
* Tests: Vector API, SPARQL, Cypher, SQL
|
||||
*/
|
||||
|
||||
import { readFile } from 'fs/promises';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, join } from 'path';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Test results tracking
|
||||
const results = {
|
||||
passed: 0,
|
||||
failed: 0,
|
||||
tests: []
|
||||
};
|
||||
|
||||
function test(name, fn) {
|
||||
return async () => {
|
||||
try {
|
||||
await fn();
|
||||
results.passed++;
|
||||
results.tests.push({ name, status: 'PASS' });
|
||||
console.log(` \x1b[32m✓\x1b[0m ${name}`);
|
||||
} catch (e) {
|
||||
results.failed++;
|
||||
results.tests.push({ name, status: 'FAIL', error: e.message });
|
||||
console.log(` \x1b[31m✗\x1b[0m ${name}`);
|
||||
console.log(` Error: ${e.message}`);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function assert(condition, message) {
|
||||
if (!condition) throw new Error(message || 'Assertion failed');
|
||||
}
|
||||
|
||||
function assertEqual(actual, expected, message) {
|
||||
if (actual !== expected) {
|
||||
throw new Error(message || `Expected ${expected}, got ${actual}`);
|
||||
}
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
console.log('╔════════════════════════════════════════════════════════════╗');
|
||||
console.log('║ RvLite WASM Comprehensive E2E Test Suite ║');
|
||||
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
||||
|
||||
// Load WASM
|
||||
console.log('Loading WASM module...');
|
||||
const wasmPath = join(__dirname, '../public/pkg/rvlite_bg.wasm');
|
||||
const wasmBytes = await readFile(wasmPath);
|
||||
const rvliteModule = await import('../public/pkg/rvlite.js');
|
||||
const { default: initRvLite, RvLite, RvLiteConfig } = rvliteModule;
|
||||
await initRvLite(wasmBytes);
|
||||
console.log('WASM module loaded successfully!\n');
|
||||
|
||||
const config = new RvLiteConfig(384);
|
||||
const db = new RvLite(config);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 1: VECTOR API TESTS
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
console.log('═══════════════════════════════════════════════════════════════');
|
||||
console.log('SECTION 1: Vector API Tests');
|
||||
console.log('═══════════════════════════════════════════════════════════════');
|
||||
|
||||
await test('Vector insert works', async () => {
|
||||
const vector = Array.from({ length: 384 }, () => Math.random());
|
||||
const id = db.insert(vector, { label: 'test-vector-1' });
|
||||
assert(id !== undefined && id !== null, 'Insert should return an ID');
|
||||
})();
|
||||
|
||||
await test('Vector multiple inserts work', async () => {
|
||||
const insertedIds = [];
|
||||
for (let i = 0; i < 10; i++) {
|
||||
const vector = Array.from({ length: 384 }, () => Math.random());
|
||||
const id = db.insert(vector, { index: i, batch: 'batch-1' });
|
||||
insertedIds.push(id);
|
||||
}
|
||||
assertEqual(insertedIds.length, 10, 'Should insert 10 vectors');
|
||||
})();
|
||||
|
||||
await test('Vector search returns results', async () => {
|
||||
const query = Array.from({ length: 384 }, () => Math.random());
|
||||
const results = db.search(query, 5);
|
||||
assert(Array.isArray(results), 'Search should return array');
|
||||
assert(results.length > 0, 'Should find vectors');
|
||||
assert(results[0].id !== undefined, 'Results should have IDs');
|
||||
assert(results[0].score !== undefined, 'Results should have scores');
|
||||
})();
|
||||
|
||||
await test('Vector count is correct', async () => {
|
||||
const count = db.len();
|
||||
assert(count >= 11, 'Should have at least 11 vectors');
|
||||
})();
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 2: SPARQL TESTS
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
console.log('\n═══════════════════════════════════════════════════════════════');
|
||||
console.log('SECTION 2: SPARQL Tests');
|
||||
console.log('═══════════════════════════════════════════════════════════════');
|
||||
|
||||
// Add RDF triples for testing
|
||||
const rdfTriples = [
|
||||
// People
|
||||
['<http://example.org/Alice>', '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>', '<http://example.org/Person>'],
|
||||
['<http://example.org/Alice>', '<http://example.org/name>', '"Alice Smith"'],
|
||||
['<http://example.org/Alice>', '<http://example.org/age>', '"30"'],
|
||||
['<http://example.org/Alice>', '<http://example.org/email>', '"alice@example.org"'],
|
||||
|
||||
['<http://example.org/Bob>', '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>', '<http://example.org/Person>'],
|
||||
['<http://example.org/Bob>', '<http://example.org/name>', '"Bob Jones"'],
|
||||
['<http://example.org/Bob>', '<http://example.org/age>', '"25"'],
|
||||
|
||||
['<http://example.org/Carol>', '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>', '<http://example.org/Person>'],
|
||||
['<http://example.org/Carol>', '<http://example.org/name>', '"Carol White"'],
|
||||
|
||||
// Relationships
|
||||
['<http://example.org/Alice>', '<http://example.org/knows>', '<http://example.org/Bob>'],
|
||||
['<http://example.org/Alice>', '<http://example.org/knows>', '<http://example.org/Carol>'],
|
||||
['<http://example.org/Bob>', '<http://example.org/knows>', '<http://example.org/Carol>'],
|
||||
|
||||
// Projects
|
||||
['<http://example.org/ProjectX>', '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>', '<http://example.org/Project>'],
|
||||
['<http://example.org/ProjectX>', '<http://example.org/name>', '"Project X"'],
|
||||
['<http://example.org/Alice>', '<http://example.org/worksOn>', '<http://example.org/ProjectX>'],
|
||||
['<http://example.org/Bob>', '<http://example.org/worksOn>', '<http://example.org/ProjectX>'],
|
||||
];
|
||||
|
||||
await test('SPARQL: Add triples', async () => {
|
||||
for (const [s, p, o] of rdfTriples) {
|
||||
db.add_triple(s, p, o);
|
||||
}
|
||||
const count = db.triple_count();
|
||||
assert(count >= rdfTriples.length, `Should have at least ${rdfTriples.length} triples`);
|
||||
})();
|
||||
|
||||
await test('SPARQL: SELECT with rdf:type', async () => {
|
||||
const result = db.sparql('SELECT ?person WHERE { ?person <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Person> }');
|
||||
assertEqual(result.type, 'select', 'Should be SELECT result');
|
||||
assert(result.bindings.length >= 3, 'Should find at least 3 people');
|
||||
assert(result.bindings.some(b => b.person.value === 'http://example.org/Alice'), 'Should find Alice');
|
||||
})();
|
||||
|
||||
await test('SPARQL: SELECT with "a" keyword (rdf:type shortcut)', async () => {
|
||||
const result = db.sparql('SELECT ?project WHERE { ?project a <http://example.org/Project> }');
|
||||
assertEqual(result.type, 'select', 'Should be SELECT result');
|
||||
assert(result.bindings.length >= 1, 'Should find at least 1 project');
|
||||
})();
|
||||
|
||||
await test('SPARQL: SELECT with specific predicate', async () => {
|
||||
const result = db.sparql('SELECT ?who WHERE { <http://example.org/Alice> <http://example.org/knows> ?who }');
|
||||
assertEqual(result.type, 'select', 'Should be SELECT result');
|
||||
assertEqual(result.bindings.length, 2, 'Alice knows 2 people');
|
||||
})();
|
||||
|
||||
await test('SPARQL: ASK query (true case)', async () => {
|
||||
const result = db.sparql('ASK { <http://example.org/Alice> <http://example.org/knows> <http://example.org/Bob> }');
|
||||
assertEqual(result.type, 'ask', 'Should be ASK result');
|
||||
assertEqual(result.result, true, 'Alice should know Bob');
|
||||
})();
|
||||
|
||||
await test('SPARQL: ASK query (false case)', async () => {
|
||||
const result = db.sparql('ASK { <http://example.org/Carol> <http://example.org/knows> <http://example.org/Alice> }');
|
||||
assertEqual(result.type, 'ask', 'Should be ASK result');
|
||||
assertEqual(result.result, false, 'Carol does not know Alice');
|
||||
})();
|
||||
|
||||
await test('SPARQL: SELECT with LIMIT', async () => {
|
||||
const result = db.sparql('SELECT ?s WHERE { ?s a <http://example.org/Person> } LIMIT 2');
|
||||
assertEqual(result.type, 'select', 'Should be SELECT result');
|
||||
assertEqual(result.bindings.length, 2, 'Should return exactly 2 results');
|
||||
})();
|
||||
|
||||
await test('SPARQL: SELECT with literal values', async () => {
|
||||
const result = db.sparql('SELECT ?name WHERE { <http://example.org/Alice> <http://example.org/name> ?name }');
|
||||
assertEqual(result.type, 'select', 'Should be SELECT result');
|
||||
assertEqual(result.bindings.length, 1, 'Should find Alice\'s name');
|
||||
assertEqual(result.bindings[0].name.type, 'literal', 'Name should be literal');
|
||||
})();
|
||||
|
||||
await test('SPARQL: Result binding format (IRI)', async () => {
|
||||
const result = db.sparql('SELECT ?s WHERE { ?s a <http://example.org/Person> } LIMIT 1');
|
||||
const binding = result.bindings[0];
|
||||
assertEqual(binding.s.type, 'iri', 'Should have type=iri');
|
||||
assert(binding.s.value.startsWith('http://'), 'Value should be clean IRI');
|
||||
})();
|
||||
|
||||
await test('SPARQL: Result binding format (Literal)', async () => {
|
||||
const result = db.sparql('SELECT ?name WHERE { <http://example.org/Bob> <http://example.org/name> ?name }');
|
||||
const binding = result.bindings[0];
|
||||
assertEqual(binding.name.type, 'literal', 'Should have type=literal');
|
||||
assert(binding.name.datatype, 'Should have datatype');
|
||||
})();
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 3: CYPHER TESTS
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
console.log('\n═══════════════════════════════════════════════════════════════');
|
||||
console.log('SECTION 3: Cypher Tests');
|
||||
console.log('═══════════════════════════════════════════════════════════════');
|
||||
|
||||
await test('Cypher: CREATE node', async () => {
|
||||
const result = db.cypher('CREATE (n:TestNode {name: "TestCypher"}) RETURN n');
|
||||
assert(result !== undefined, 'Should return result');
|
||||
})();
|
||||
|
||||
await test('Cypher: MATCH query', async () => {
|
||||
const result = db.cypher('MATCH (n:TestNode) RETURN n.name');
|
||||
assert(result !== undefined, 'Should return result');
|
||||
})();
|
||||
|
||||
await test('Cypher: CREATE relationship', async () => {
|
||||
db.cypher('CREATE (a:CypherPerson {name: "Dave"})');
|
||||
db.cypher('CREATE (b:CypherPerson {name: "Eve"})');
|
||||
const result = db.cypher('MATCH (a:CypherPerson {name: "Dave"}), (b:CypherPerson {name: "Eve"}) CREATE (a)-[r:KNOWS]->(b) RETURN r');
|
||||
assert(result !== undefined, 'Should create relationship');
|
||||
})();
|
||||
|
||||
await test('Cypher: MATCH with relationship', async () => {
|
||||
const result = db.cypher('MATCH (a:CypherPerson)-[r:KNOWS]->(b:CypherPerson) RETURN a.name, b.name');
|
||||
assert(result !== undefined, 'Should match relationships');
|
||||
})();
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 4: DATABASE INFO & STATS
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
console.log('\n═══════════════════════════════════════════════════════════════');
|
||||
console.log('SECTION 4: Database Statistics');
|
||||
console.log('═══════════════════════════════════════════════════════════════');
|
||||
|
||||
await test('Get vector count', async () => {
|
||||
const count = db.len();
|
||||
console.log(` Vector count: ${count}`);
|
||||
assert(count >= 0, 'Should return valid count');
|
||||
})();
|
||||
|
||||
await test('Get triple count', async () => {
|
||||
const count = db.triple_count();
|
||||
console.log(` Triple count: ${count}`);
|
||||
assert(count >= rdfTriples.length, 'Should return valid count');
|
||||
})();
|
||||
|
||||
await test('Get database config', async () => {
|
||||
const config = db.get_config();
|
||||
assert(config.dimensions, 'Should have dimensions');
|
||||
const version = db.get_version();
|
||||
assert(version, 'Should have version');
|
||||
const features = db.get_features();
|
||||
assert(features, 'Should have features');
|
||||
console.log(` Version: ${version}`);
|
||||
console.log(` Dimensions: ${config.dimensions}`);
|
||||
console.log(` Distance metric: ${config.distance_metric}`);
|
||||
})();
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 5: EDGE CASES & ERROR HANDLING
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
console.log('\n═══════════════════════════════════════════════════════════════');
|
||||
console.log('SECTION 5: Edge Cases & Error Handling');
|
||||
console.log('═══════════════════════════════════════════════════════════════');
|
||||
|
||||
await test('SPARQL: Empty result for non-existent data', async () => {
|
||||
const result = db.sparql('SELECT ?s WHERE { ?s a <http://example.org/NonExistent> }');
|
||||
assertEqual(result.bindings.length, 0, 'Should return empty bindings');
|
||||
})();
|
||||
|
||||
await test('SPARQL: Handle special characters in IRIs', async () => {
|
||||
db.add_triple('<http://example.org/item#1>', '<http://example.org/type>', '<http://example.org/Thing>');
|
||||
const result = db.sparql('SELECT ?s WHERE { ?s <http://example.org/type> <http://example.org/Thing> }');
|
||||
assert(result.bindings.length >= 1, 'Should handle # in IRIs');
|
||||
})();
|
||||
|
||||
await test('Vector: Search with empty database returns empty array', async () => {
|
||||
// Create fresh instance for this test
|
||||
const freshConfig = new RvLiteConfig(64);
|
||||
const freshDb = new RvLite(freshConfig);
|
||||
const query = Array.from({ length: 64 }, () => Math.random());
|
||||
const searchResults = freshDb.search(query, 5);
|
||||
assert(Array.isArray(searchResults), 'Should return array');
|
||||
assertEqual(searchResults.length, 0, 'Should return empty array');
|
||||
freshDb.free();
|
||||
})();
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SUMMARY
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
console.log('\n╔════════════════════════════════════════════════════════════╗');
|
||||
console.log('║ TEST SUMMARY ║');
|
||||
console.log('╚════════════════════════════════════════════════════════════╝');
|
||||
console.log(`\n Total: ${results.passed + results.failed} tests`);
|
||||
console.log(` \x1b[32mPassed: ${results.passed}\x1b[0m`);
|
||||
console.log(` \x1b[31mFailed: ${results.failed}\x1b[0m`);
|
||||
|
||||
if (results.failed > 0) {
|
||||
console.log('\n\x1b[31mFailed Tests:\x1b[0m');
|
||||
results.tests.filter(t => t.status === 'FAIL').forEach(t => {
|
||||
console.log(` - ${t.name}: ${t.error}`);
|
||||
});
|
||||
}
|
||||
|
||||
console.log('\n');
|
||||
|
||||
// Cleanup
|
||||
db.free();
|
||||
|
||||
// Exit with appropriate code
|
||||
process.exit(results.failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
runTests().catch(err => {
|
||||
console.error('Test suite crashed:', err);
|
||||
process.exit(1);
|
||||
});
|
||||
409
vendor/ruvector/crates/rvlite/examples/dashboard/scripts/test-all.mjs
vendored
Normal file
409
vendor/ruvector/crates/rvlite/examples/dashboard/scripts/test-all.mjs
vendored
Normal file
@@ -0,0 +1,409 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Comprehensive RvLite WASM Test Suite
|
||||
* Tests ALL features: Vector API, SQL, SPARQL, Cypher
|
||||
*
|
||||
* Run with: node scripts/test-all.mjs
|
||||
*/
|
||||
|
||||
import { readFile } from 'fs/promises';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, join } from 'path';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// Test results tracking
|
||||
const results = {
|
||||
total: 0,
|
||||
passed: 0,
|
||||
failed: 0,
|
||||
sections: [],
|
||||
errors: []
|
||||
};
|
||||
|
||||
// Colors for terminal output
|
||||
const colors = {
|
||||
reset: '\x1b[0m',
|
||||
green: '\x1b[32m',
|
||||
red: '\x1b[31m',
|
||||
yellow: '\x1b[33m',
|
||||
cyan: '\x1b[36m',
|
||||
dim: '\x1b[2m',
|
||||
bold: '\x1b[1m'
|
||||
};
|
||||
|
||||
function log(color, text) {
|
||||
console.log(`${color}${text}${colors.reset}`);
|
||||
}
|
||||
|
||||
// Test runner
|
||||
async function test(name, fn) {
|
||||
results.total++;
|
||||
try {
|
||||
await fn();
|
||||
results.passed++;
|
||||
console.log(` ${colors.green}✓${colors.reset} ${name}`);
|
||||
return true;
|
||||
} catch (e) {
|
||||
results.failed++;
|
||||
results.errors.push({ name, error: e.message });
|
||||
console.log(` ${colors.red}✗${colors.reset} ${name}`);
|
||||
console.log(` ${colors.dim}Error: ${e.message}${colors.reset}`);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function assert(condition, message) {
|
||||
if (!condition) throw new Error(message || 'Assertion failed');
|
||||
}
|
||||
|
||||
function assertEqual(actual, expected, message) {
|
||||
if (actual !== expected) {
|
||||
throw new Error(message || `Expected ${JSON.stringify(expected)}, got ${JSON.stringify(actual)}`);
|
||||
}
|
||||
}
|
||||
|
||||
function assertDeepIncludes(obj, key) {
|
||||
if (typeof obj !== 'object' || obj === null || !(key in obj)) {
|
||||
throw new Error(`Object should have key '${key}', got: ${JSON.stringify(obj)}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Section header
|
||||
function section(name) {
|
||||
console.log(`\n${colors.cyan}═══════════════════════════════════════════════════════════════${colors.reset}`);
|
||||
console.log(`${colors.bold}${name}${colors.reset}`);
|
||||
console.log(`${colors.cyan}═══════════════════════════════════════════════════════════════${colors.reset}`);
|
||||
results.sections.push({ name, startIndex: results.total });
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
console.log(`${colors.cyan}╔════════════════════════════════════════════════════════════╗${colors.reset}`);
|
||||
console.log(`${colors.cyan}║${colors.reset} ${colors.bold}RvLite WASM Comprehensive Test Suite${colors.reset} ${colors.cyan}║${colors.reset}`);
|
||||
console.log(`${colors.cyan}║${colors.reset} Tests: Vector API • SQL • SPARQL • Cypher ${colors.cyan}║${colors.reset}`);
|
||||
console.log(`${colors.cyan}╚════════════════════════════════════════════════════════════╝${colors.reset}\n`);
|
||||
|
||||
// Load WASM
|
||||
console.log('Loading WASM module...');
|
||||
const wasmPath = join(__dirname, '../public/pkg/rvlite_bg.wasm');
|
||||
const wasmBytes = await readFile(wasmPath);
|
||||
const rvliteModule = await import('../public/pkg/rvlite.js');
|
||||
const { default: initRvLite, RvLite, RvLiteConfig } = rvliteModule;
|
||||
await initRvLite(wasmBytes);
|
||||
|
||||
const version = 'RvLite loaded';
|
||||
console.log(`${colors.green}✓${colors.reset} ${version}\n`);
|
||||
|
||||
const config = new RvLiteConfig(128);
|
||||
const db = new RvLite(config);
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 1: INITIALIZATION & CONFIG
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
section('SECTION 1: Initialization & Configuration');
|
||||
|
||||
await test('RvLite instance created', () => {
|
||||
assert(db !== null && db !== undefined, 'DB should be created');
|
||||
});
|
||||
|
||||
await test('is_ready returns true', () => {
|
||||
assert(db.is_ready() === true, 'Should be ready');
|
||||
});
|
||||
|
||||
await test('get_version returns string', () => {
|
||||
const version = db.get_version();
|
||||
assert(typeof version === 'string' && version.length > 0, 'Should have version');
|
||||
});
|
||||
|
||||
await test('get_features returns array', () => {
|
||||
const features = db.get_features();
|
||||
assert(Array.isArray(features), 'Features should be array');
|
||||
});
|
||||
|
||||
await test('get_config returns valid config', () => {
|
||||
const cfg = db.get_config();
|
||||
assert(cfg !== null, 'Config should exist');
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 2: VECTOR API
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
section('SECTION 2: Vector API');
|
||||
|
||||
let vectorId;
|
||||
await test('Insert vector returns ID', () => {
|
||||
const vector = new Float32Array(128).fill(0.5);
|
||||
vectorId = db.insert(vector, { label: 'test-1' });
|
||||
assert(vectorId !== null && vectorId !== undefined, 'Should return ID');
|
||||
});
|
||||
|
||||
await test('Insert multiple vectors', () => {
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const vector = new Float32Array(128).map(() => Math.random());
|
||||
db.insert(vector, { index: i });
|
||||
}
|
||||
assert(db.len() >= 6, 'Should have at least 6 vectors');
|
||||
});
|
||||
|
||||
await test('Search returns results', () => {
|
||||
const query = new Float32Array(128).fill(0.5);
|
||||
const results = db.search(query, 3);
|
||||
assert(Array.isArray(results), 'Should return array');
|
||||
assert(results.length > 0, 'Should find results');
|
||||
});
|
||||
|
||||
await test('Search results have id and score', () => {
|
||||
const query = new Float32Array(128).fill(0.5);
|
||||
const results = db.search(query, 1);
|
||||
assert(results[0].id !== undefined, 'Should have id');
|
||||
assert(results[0].score !== undefined, 'Should have score');
|
||||
});
|
||||
|
||||
await test('len() returns correct count', () => {
|
||||
const count = db.len();
|
||||
assert(typeof count === 'number' && count >= 6, 'Should have count >= 6');
|
||||
});
|
||||
|
||||
await test('is_empty() returns false after inserts', () => {
|
||||
assert(db.is_empty() === false, 'Should not be empty');
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 3: SQL (Vector Search)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
section('SECTION 3: SQL (Vector Search)');
|
||||
|
||||
await test('SQL: DROP TABLE (cleanup)', () => {
|
||||
try {
|
||||
const result = db.sql('DROP TABLE test_docs');
|
||||
assert(result !== undefined, 'Should return result');
|
||||
} catch {
|
||||
// Table might not exist - that's OK
|
||||
}
|
||||
});
|
||||
|
||||
await test('SQL: CREATE TABLE with VECTOR', () => {
|
||||
const result = db.sql('CREATE TABLE test_docs (id TEXT, title TEXT, embedding VECTOR(3))');
|
||||
assert(result !== undefined, 'Should return result');
|
||||
assertDeepIncludes(result, 'rows');
|
||||
});
|
||||
|
||||
await test('SQL: INSERT vector data', () => {
|
||||
const result = db.sql("INSERT INTO test_docs (id, title, embedding) VALUES ('d1', 'First Doc', [1.0, 2.0, 3.0])");
|
||||
assert(result !== undefined, 'Should return result');
|
||||
});
|
||||
|
||||
await test('SQL: INSERT multiple rows', () => {
|
||||
db.sql("INSERT INTO test_docs (id, title, embedding) VALUES ('d2', 'Second Doc', [4.0, 5.0, 6.0])");
|
||||
db.sql("INSERT INTO test_docs (id, title, embedding) VALUES ('d3', 'Third Doc', [7.0, 8.0, 9.0])");
|
||||
});
|
||||
|
||||
await test('SQL: Vector search with L2 distance (<->)', () => {
|
||||
const result = db.sql('SELECT * FROM test_docs ORDER BY embedding <-> [1.0, 2.0, 3.0] LIMIT 5');
|
||||
assert(result.rows !== undefined, 'Should have rows');
|
||||
assert(result.rows.length > 0, 'Should return results');
|
||||
});
|
||||
|
||||
await test('SQL: Vector search with cosine distance (<=>)', () => {
|
||||
const result = db.sql('SELECT * FROM test_docs ORDER BY embedding <=> [0.5, 0.5, 0.5] LIMIT 3');
|
||||
assert(result.rows !== undefined, 'Should have rows');
|
||||
});
|
||||
|
||||
await test('SQL: Vector search with WHERE filter', () => {
|
||||
const result = db.sql("SELECT * FROM test_docs WHERE id = 'd1' ORDER BY embedding <-> [1.0, 2.0, 3.0] LIMIT 5");
|
||||
assert(result.rows !== undefined, 'Should have rows');
|
||||
});
|
||||
|
||||
await test('SQL: Non-vector SELECT (table scan)', () => {
|
||||
const result = db.sql('SELECT * FROM test_docs');
|
||||
assert(result.rows !== undefined, 'Should have rows');
|
||||
assert(result.rows.length >= 3, 'Should return all 3 inserted rows');
|
||||
});
|
||||
|
||||
await test('SQL: Results contain actual data (not empty objects)', () => {
|
||||
const result = db.sql('SELECT * FROM test_docs ORDER BY embedding <-> [1.0, 2.0, 3.0] LIMIT 1');
|
||||
assert(result.rows.length > 0, 'Should have rows');
|
||||
const row = result.rows[0];
|
||||
// Check that row is not empty {}
|
||||
const keys = Object.keys(row);
|
||||
assert(keys.length > 0, 'Row should have properties, not be empty {}');
|
||||
});
|
||||
|
||||
await test('SQL: DROP TABLE', () => {
|
||||
const result = db.sql('DROP TABLE test_docs');
|
||||
assert(result !== undefined, 'Should return result');
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 4: SPARQL (RDF Triple Store)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
section('SECTION 4: SPARQL (RDF Triple Store)');
|
||||
|
||||
await test('SPARQL: Add triple', () => {
|
||||
db.add_triple('<http://example.org/Alice>', '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>', '<http://example.org/Person>');
|
||||
db.add_triple('<http://example.org/Alice>', '<http://example.org/name>', '"Alice Smith"');
|
||||
db.add_triple('<http://example.org/Bob>', '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type>', '<http://example.org/Person>');
|
||||
db.add_triple('<http://example.org/Alice>', '<http://example.org/knows>', '<http://example.org/Bob>');
|
||||
});
|
||||
|
||||
await test('SPARQL: triple_count() > 0', () => {
|
||||
const count = db.triple_count();
|
||||
assert(count >= 4, `Should have at least 4 triples, got ${count}`);
|
||||
});
|
||||
|
||||
await test('SPARQL: SELECT query', () => {
|
||||
const result = db.sparql('SELECT ?person WHERE { ?person <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://example.org/Person> }');
|
||||
assertEqual(result.type, 'select', 'Should be SELECT result');
|
||||
assert(result.bindings.length >= 2, 'Should find at least 2 people');
|
||||
});
|
||||
|
||||
await test('SPARQL: SELECT with "a" keyword (rdf:type shortcut)', () => {
|
||||
const result = db.sparql('SELECT ?s WHERE { ?s a <http://example.org/Person> }');
|
||||
assertEqual(result.type, 'select', 'Should be SELECT result');
|
||||
assert(result.bindings.length >= 2, 'Should find people');
|
||||
});
|
||||
|
||||
await test('SPARQL: ASK query (true case)', () => {
|
||||
const result = db.sparql('ASK { <http://example.org/Alice> <http://example.org/knows> <http://example.org/Bob> }');
|
||||
assertEqual(result.type, 'ask', 'Should be ASK result');
|
||||
assertEqual(result.result, true, 'Alice should know Bob');
|
||||
});
|
||||
|
||||
await test('SPARQL: ASK query (false case)', () => {
|
||||
const result = db.sparql('ASK { <http://example.org/Bob> <http://example.org/knows> <http://example.org/Alice> }');
|
||||
assertEqual(result.type, 'ask', 'Should be ASK result');
|
||||
assertEqual(result.result, false, 'Bob does not know Alice');
|
||||
});
|
||||
|
||||
await test('SPARQL: SELECT with LIMIT', () => {
|
||||
const result = db.sparql('SELECT ?s WHERE { ?s a <http://example.org/Person> } LIMIT 1');
|
||||
assertEqual(result.bindings.length, 1, 'Should return exactly 1 result');
|
||||
});
|
||||
|
||||
await test('SPARQL: Result binding has type and value', () => {
|
||||
const result = db.sparql('SELECT ?s WHERE { ?s a <http://example.org/Person> } LIMIT 1');
|
||||
const binding = result.bindings[0];
|
||||
assert(binding.s !== undefined, 'Should have binding');
|
||||
assert(binding.s.type !== undefined, 'Should have type');
|
||||
assert(binding.s.value !== undefined, 'Should have value');
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 5: CYPHER (Graph Database)
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
section('SECTION 5: Cypher (Graph Database)');
|
||||
|
||||
await test('Cypher: CREATE node', () => {
|
||||
const result = db.cypher("CREATE (n:TestPerson {name: 'Charlie', age: 35})");
|
||||
assert(result !== undefined, 'Should return result');
|
||||
});
|
||||
|
||||
await test('Cypher: CREATE multiple nodes', () => {
|
||||
db.cypher("CREATE (n:TestPerson {name: 'Diana', age: 28})");
|
||||
db.cypher("CREATE (c:TestCompany {name: 'Acme Inc', founded: 2010})");
|
||||
});
|
||||
|
||||
await test('Cypher: MATCH query', () => {
|
||||
const result = db.cypher('MATCH (n:TestPerson) RETURN n');
|
||||
assert(result !== undefined, 'Should return result');
|
||||
});
|
||||
|
||||
await test('Cypher: cypher_stats returns counts', () => {
|
||||
const stats = db.cypher_stats();
|
||||
assert(stats !== undefined, 'Should return stats');
|
||||
// Stats might have node_count or nodes depending on version
|
||||
});
|
||||
|
||||
await test('Cypher: CREATE relationship', () => {
|
||||
const result = db.cypher("MATCH (a:TestPerson {name: 'Charlie'}), (b:TestCompany {name: 'Acme Inc'}) CREATE (a)-[r:WORKS_AT]->(b) RETURN r");
|
||||
assert(result !== undefined, 'Should return result');
|
||||
});
|
||||
|
||||
await test('Cypher: MATCH with relationship', () => {
|
||||
const result = db.cypher('MATCH (a:TestPerson)-[r:WORKS_AT]->(b:TestCompany) RETURN a, b');
|
||||
assert(result !== undefined, 'Should return result');
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 6: EDGE CASES & ERROR HANDLING
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
section('SECTION 6: Edge Cases & Error Handling');
|
||||
|
||||
await test('SPARQL: Empty result for non-existent data', () => {
|
||||
const result = db.sparql('SELECT ?s WHERE { ?s a <http://example.org/NonExistent> }');
|
||||
assertEqual(result.bindings.length, 0, 'Should return empty bindings');
|
||||
});
|
||||
|
||||
await test('SQL: Error on non-existent table', () => {
|
||||
try {
|
||||
db.sql('SELECT * FROM nonexistent_table ORDER BY col <-> [1,2,3] LIMIT 1');
|
||||
throw new Error('Should have thrown');
|
||||
} catch (e) {
|
||||
assert(e.message.includes('not found') || e.message.includes('does not exist') || e.message !== 'Should have thrown',
|
||||
'Should throw table not found error');
|
||||
}
|
||||
});
|
||||
|
||||
await test('Fresh instance search returns empty', () => {
|
||||
const freshConfig = new RvLiteConfig(32);
|
||||
const freshDb = new RvLite(freshConfig);
|
||||
const query = new Float32Array(32).fill(0.5);
|
||||
const searchResults = freshDb.search(query, 5);
|
||||
assertEqual(searchResults.length, 0, 'Should return empty array');
|
||||
freshDb.free();
|
||||
});
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SECTION 7: DATA PERSISTENCE METHODS
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
section('SECTION 7: Data Persistence Methods');
|
||||
|
||||
await test('export_json returns object', () => {
|
||||
const exported = db.export_json();
|
||||
assert(typeof exported === 'object', 'Should return object');
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
db.free();
|
||||
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
// SUMMARY
|
||||
// ═══════════════════════════════════════════════════════════════════════════
|
||||
console.log(`\n${colors.cyan}╔════════════════════════════════════════════════════════════╗${colors.reset}`);
|
||||
console.log(`${colors.cyan}║${colors.reset} ${colors.bold}TEST SUMMARY${colors.reset} ${colors.cyan}║${colors.reset}`);
|
||||
console.log(`${colors.cyan}╚════════════════════════════════════════════════════════════╝${colors.reset}`);
|
||||
|
||||
console.log(`\n Total: ${results.total} tests`);
|
||||
console.log(` ${colors.green}Passed: ${results.passed}${colors.reset}`);
|
||||
console.log(` ${colors.red}Failed: ${results.failed}${colors.reset}`);
|
||||
|
||||
if (results.failed > 0) {
|
||||
console.log(`\n${colors.red}${colors.bold}Failed Tests:${colors.reset}`);
|
||||
results.errors.forEach(({ name, error }) => {
|
||||
console.log(` ${colors.red}✗${colors.reset} ${name}`);
|
||||
console.log(` ${colors.dim}${error}${colors.reset}`);
|
||||
});
|
||||
}
|
||||
|
||||
// Per-section summary
|
||||
console.log(`\n${colors.bold}Section Results:${colors.reset}`);
|
||||
results.sections.forEach((sec, i) => {
|
||||
const nextStart = results.sections[i + 1]?.startIndex || results.total;
|
||||
const sectionTests = nextStart - sec.startIndex;
|
||||
const icon = results.errors.some(e => {
|
||||
const idx = results.errors.indexOf(e);
|
||||
return idx >= sec.startIndex && idx < nextStart;
|
||||
}) ? colors.yellow + '⚠' : colors.green + '✓';
|
||||
console.log(` ${icon}${colors.reset} ${sec.name}`);
|
||||
});
|
||||
|
||||
console.log('\n');
|
||||
|
||||
// Exit with appropriate code
|
||||
process.exit(results.failed > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
runTests().catch(err => {
|
||||
console.error(`${colors.red}Test suite crashed:${colors.reset}`, err);
|
||||
process.exit(1);
|
||||
});
|
||||
113
vendor/ruvector/crates/rvlite/examples/dashboard/scripts/test-sql.mjs
vendored
Normal file
113
vendor/ruvector/crates/rvlite/examples/dashboard/scripts/test-sql.mjs
vendored
Normal file
@@ -0,0 +1,113 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* SQL Test Script - Test CREATE TABLE and SQL operations in WASM
|
||||
*/
|
||||
|
||||
import { readFile } from 'fs/promises';
|
||||
import { fileURLToPath } from 'url';
|
||||
import { dirname, join } from 'path';
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url);
|
||||
const __dirname = dirname(__filename);
|
||||
|
||||
async function main() {
|
||||
console.log('╔════════════════════════════════════════════════════════════╗');
|
||||
console.log('║ RvLite WASM SQL Test Suite ║');
|
||||
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
||||
|
||||
// Load WASM
|
||||
const wasmPath = join(__dirname, '../public/pkg/rvlite_bg.wasm');
|
||||
const wasmBytes = await readFile(wasmPath);
|
||||
|
||||
const { default: initRvLite, RvLite, RvLiteConfig } = await import('../public/pkg/rvlite.js');
|
||||
await initRvLite(wasmBytes);
|
||||
console.log('WASM loaded successfully!\n');
|
||||
|
||||
const config = new RvLiteConfig(384);
|
||||
const db = new RvLite(config);
|
||||
|
||||
const tests = [];
|
||||
|
||||
function test(name, fn) {
|
||||
try {
|
||||
fn();
|
||||
tests.push({ name, passed: true });
|
||||
console.log(` \x1b[32m✓\x1b[0m ${name}`);
|
||||
} catch (error) {
|
||||
tests.push({ name, passed: false, error: error.message });
|
||||
console.log(` \x1b[31m✗\x1b[0m ${name}`);
|
||||
console.log(` Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('═══════════════════════════════════════════════════════════════');
|
||||
console.log('SQL Parser Tests');
|
||||
console.log('═══════════════════════════════════════════════════════════════');
|
||||
|
||||
// Test 1: CREATE TABLE with VECTOR column (3-dimensional for test)
|
||||
test('CREATE TABLE with VECTOR', () => {
|
||||
const result = db.sql("CREATE TABLE docs (id TEXT, content TEXT, embedding VECTOR(3))");
|
||||
console.log(' Result:', JSON.stringify(result));
|
||||
});
|
||||
|
||||
// Test 2: INSERT with correct vector dimensions
|
||||
test('INSERT INTO table', () => {
|
||||
const result = db.sql("INSERT INTO docs (id, content, embedding) VALUES ('doc1', 'hello world', [1.0, 2.0, 3.0])");
|
||||
console.log(' Result:', JSON.stringify(result));
|
||||
});
|
||||
|
||||
// Test 3: INSERT another vector
|
||||
test('INSERT second vector', () => {
|
||||
const result = db.sql("INSERT INTO docs (id, content, embedding) VALUES ('doc2', 'test content', [4.0, 5.0, 6.0])");
|
||||
console.log(' Result:', JSON.stringify(result));
|
||||
});
|
||||
|
||||
// Test 4: Vector search with L2 distance
|
||||
test('Vector search with L2 distance', () => {
|
||||
const result = db.sql("SELECT * FROM docs ORDER BY embedding <-> [1.0, 2.0, 3.0] LIMIT 5");
|
||||
console.log(' Result:', JSON.stringify(result));
|
||||
});
|
||||
|
||||
// Test 5: Vector search with cosine distance
|
||||
test('Vector search with cosine distance', () => {
|
||||
const result = db.sql("SELECT * FROM docs ORDER BY embedding <=> [0.5, 0.5, 0.5] LIMIT 5");
|
||||
console.log(' Result:', JSON.stringify(result));
|
||||
});
|
||||
|
||||
// Test 6: Vector search with filter
|
||||
test('Vector search with filter', () => {
|
||||
const result = db.sql("SELECT * FROM docs WHERE id = 'doc1' ORDER BY embedding <-> [1.0, 2.0, 3.0] LIMIT 5");
|
||||
console.log(' Result:', JSON.stringify(result));
|
||||
});
|
||||
|
||||
// Test 7: DROP TABLE
|
||||
test('DROP TABLE', () => {
|
||||
const result = db.sql("DROP TABLE docs");
|
||||
console.log(' Result:', JSON.stringify(result));
|
||||
});
|
||||
|
||||
// Cleanup
|
||||
db.free();
|
||||
|
||||
// Summary
|
||||
console.log('\n╔════════════════════════════════════════════════════════════╗');
|
||||
console.log('║ TEST SUMMARY ║');
|
||||
console.log('╚════════════════════════════════════════════════════════════╝\n');
|
||||
|
||||
const passed = tests.filter(t => t.passed).length;
|
||||
const failed = tests.filter(t => !t.passed).length;
|
||||
|
||||
console.log(` Total: ${tests.length} tests`);
|
||||
console.log(` \x1b[32mPassed: ${passed}\x1b[0m`);
|
||||
console.log(` \x1b[31mFailed: ${failed}\x1b[0m`);
|
||||
|
||||
if (failed > 0) {
|
||||
console.log('\n Failed tests:');
|
||||
tests.filter(t => !t.passed).forEach(t => {
|
||||
console.log(` - ${t.name}: ${t.error}`);
|
||||
});
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
Reference in New Issue
Block a user