/** * Executor + Builder + Memory 边缘覆盖测试 */ import { MetonaSqlark } from '../src/core'; import { MemoryEngine } from '../src/engine/memory'; import { SelectQueryBuilder } from '../src/query/builder'; import { createSchema } from '../src/table/schema'; describe('边缘覆盖', () => { let db: MetonaSqlark; beforeEach(async () => { db = new MetonaSqlark({ name: 'test-edge', mode: 'memory' }); await db.init(); await db.defineTable('users', { id: { type: 'string', primaryKey: true }, name: { type: 'string', required: true }, age: { type: 'number' }, active: { type: 'boolean', default: true }, }); await db.table('users').insertMany([ { id: '1', name: 'Alice', age: 30, active: true }, { id: '2', name: 'Bob', age: 25, active: true }, { id: '3', name: 'Charlie', age: 35, active: false }, { id: '4', name: 'Diana', age: 30, active: true }, ]); }); afterEach(async () => { await db.close(); }); // ---- DISTINCT ---- describe('DISTINCT', () => { it('SELECT DISTINCT 去重', async () => { const result = await db.query('SELECT DISTINCT age FROM users ORDER BY age') as Record[]; expect(result).toHaveLength(3); // 30,25,35 }); it('DISTINCT with WHERE', async () => { const result = await db.query("SELECT DISTINCT age FROM users WHERE active = TRUE") as Record[]; expect(result).toHaveLength(2); // 30,25 }); }); // ---- $and / $or / $not ---- describe('复杂 Where 条件', () => { it('$and 条件 (MemoryEngine 直接)', async () => { // $and 在 MemoryEngine 的 matchWhere 中解析 const engine = new MemoryEngine(); await engine.open('test', 1); await engine.createTable(createSchema('users', { id: { type: 'string', primaryKey: true }, age: { type: 'number' }, active: { type: 'boolean' }, })); await engine.insert('users', [ { id: '1', age: 30, active: true }, { id: '2', age: 25, active: true }, { id: '3', age: 30, active: false }, ]); const rows = await engine.find('users', { table: 'users', where: { $and: [{ age: 30 }, { active: true }] } as any, }); expect(rows).toHaveLength(1); await engine.close(); }); it('$or 条件 (MemoryEngine 直接)', async () => { const engine = new MemoryEngine(); await engine.open('test2', 1); await engine.createTable(createSchema('users', { id: { type: 'string', primaryKey: true }, name: { type: 'string' }, })); await engine.insert('users', [ { id: '1', name: 'Alice' }, { id: '2', name: 'Bob' }, { id: '3', name: 'Charlie' }, ]); const rows = await engine.find('users', { table: 'users', where: { $or: [{ name: 'Alice' }, { name: 'Bob' }] } as any, }); expect(rows).toHaveLength(2); await engine.close(); }); it('$not 条件', async () => { const rows = await db.table('users').select().where({ name: { $not: { $eq: 'Alice' } } as any, }).execute(); expect(rows).toHaveLength(3); }); it('$in + $nin', async () => { const r1 = await db.table('users').select().where({ age: { $in: [25, 35] } }).execute(); expect(r1).toHaveLength(2); const r2 = await db.table('users').select().where({ age: { $nin: [30] } }).execute(); expect(r2).toHaveLength(2); }); it('$lt + $lte', async () => { const r = await db.table('users').select().where({ age: { $lt: 30 } }).execute(); expect(r).toHaveLength(1); // Bob(25) const r2 = await db.table('users').select().where({ age: { $lte: 30 } }).execute(); expect(r2).toHaveLength(3); // Alice+Diana(30)+Bob(25) }); }); // ---- QueryBuilder 全覆盖 ---- describe('QueryBuilder 全覆盖', () => { let engine: MemoryEngine; beforeEach(async () => { engine = new MemoryEngine(); await engine.open('test', 1); await engine.createTable(createSchema('users', { id: { type: 'string', primaryKey: true }, name: { type: 'string', required: true }, })); await engine.insert('users', [{ id: '1', name: 'Alice' }, { id: '2', name: 'Bob' }]); }); afterEach(async () => { await engine.close(); }); it('SelectQueryBuilder.as 别名', () => { const qb = new SelectQueryBuilder(engine, 'users').as('u'); expect(qb.toAST().alias).toBe('u'); }); it('rightJoin', () => { const qb = new SelectQueryBuilder(engine, 'users'); qb.rightJoin('orders', { 'users.id': { $col: 'orders.user_id' } }, 'o'); expect(qb.toAST().joins![0].type).toBe('RIGHT'); }); it('join 默认为 INNER', () => { const qb = new SelectQueryBuilder(engine, 'users'); qb.join('orders', { 'users.id': { $col: 'orders.user_id' } }); expect(qb.toAST().joins![0].type).toBe('INNER'); }); }); // ---- RIGHT JOIN SQL ---- describe('RIGHT JOIN', () => { beforeEach(async () => { await db.defineTable('departments', { id: { type: 'number', primaryKey: true }, name: { type: 'string' }, }); await db.table('departments').insertMany([ { id: 1, name: 'Eng' }, { id: 2, name: 'Sales' }, ]); await db.defineTable('emp', { id: { type: 'string', primaryKey: true }, name: { type: 'string' }, dept_id: { type: 'number' }, }); await db.table('emp').insert({ id: '1', name: 'Alice', dept_id: 1 }); }); it('RIGHT JOIN 保留右表无匹配行', async () => { const result = await db.query( 'SELECT emp.name, departments.name FROM emp RIGHT JOIN departments ON emp.dept_id = departments.id', ) as Record[]; expect(result.length).toBeGreaterThanOrEqual(2); }); }); // ---- MemoryEngine 索引 ---- describe('MemoryEngine 索引查找', () => { it('索引列 $eq 查询', async () => { // age 列标记为 index await db.defineTable('indexed_users', { id: { type: 'string', primaryKey: true }, name: { type: 'string' }, score: { type: 'number', index: true }, }); await db.table('indexed_users').insertMany([ { id: '1', name: 'A', score: 100 }, { id: '2', name: 'B', score: 200 }, { id: '3', name: 'C', score: 100 }, ]); // $eq 查询应使用索引 const rows = await db.table('indexed_users').select().where({ score: 100 }).execute(); expect(rows).toHaveLength(2); }); it('唯一索引列查询', async () => { await db.defineTable('unique_users', { id: { type: 'string', primaryKey: true }, email: { type: 'string', unique: true }, }); await db.table('unique_users').insertMany([ { id: '1', email: 'a@t.com' }, { id: '2', email: 'b@t.com' }, ]); const rows = await db.table('unique_users').select().where({ email: 'a@t.com' }).execute(); expect(rows).toHaveLength(1); }); }); // ---- GROUP BY 边缘 ---- describe('GROUP BY 边缘', () => { it('单个 GROUP BY 列', async () => { const result = await db.query('SELECT active, COUNT(*) FROM users GROUP BY active') as Record[]; expect(result).toHaveLength(2); }); it('AVG 空值处理 (GROUP BY)', async () => { await db.defineTable('scores', { id: { type: 'string', primaryKey: true }, dept: { type: 'string' }, val: { type: 'number' } }); await db.table('scores').insertMany([{ id: '1', dept: 'A', val: 100 }, { id: '2', dept: 'A', val: 200 }]); const result = await db.query('SELECT dept, AVG(val) FROM scores GROUP BY dept') as Record[]; expect(result[0]['AVG(val)']).toBe(150); }); }); });