Files
MetonaSqlark/tests/v071-hardening.test.ts
T
thzxx cbe407eb49
CI / test (22.x) (push) Successful in 17m24s
CI / e2e (push) Successful in 10m39s
CI / test (18.x) (push) Successful in 19m5s
CI / test (20.x) (push) Successful in 18m4s
CI / test (24.x) (push) Successful in 23m19s
fix: v0.7.1 API 修复与防御统一 — static create / 未 open 防护 / 原型污染 / lint 清零
- MetonaSqlark.create 静态工厂(README 示例在 ESM/Node 下此前 TypeError),
  独立 create 函数委托静态实现
- close 未初始化防御(engine undefined 不再崩溃)
- AriaEngine hasTable/getTableNames/getTableSchema 统一 ensureOpen
- 事务回滚失败不掩盖原始错误
- __proto__ 列名防护:Executor 列映射 Object.create(null) + schema 校验拒绝
- lint 清零(移除 5 处未使用导入)

测试 1147 → 1155(73 套件);行覆盖率 89.8%;版本 0.7.1
2026-08-13 11:14:42 +08:00

103 lines
4.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* v0.7.1 硬化回归测试
*
* 覆盖:
* - MetonaSqlark.create 静态工厂(README 示例 API 修复)
* - close 未初始化防护(不再 TypeError)
* - AriaEngine 未 open 防护统一(hasTable/getTableNames/getTableSchema
* - 事务回滚失败不掩盖原始错误
* - '__proto__' 列名拒绝(原型污染防护)
*/
import { MetonaSqlark } from '../src/core';
import { create } from '../src/index';
import { AriaEngine } from '../src/engine/aria/index';
import { MemoryEngine } from '../src/engine/memory';
import { TransactionManager } from '../src/transaction/index';
import { createSchema } from '../src/table/schema';
function uniqueDB(): string {
return `v071-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
}
describe('v0.7.1 — MetonaSqlark.create 静态工厂', () => {
it('类静态 create 可用(与 api create 行为一致)', async () => {
const db = await MetonaSqlark.create({ name: uniqueDB(), mode: 'memory' });
expect(db.isReady()).toBe(true);
await db.defineTable('t', { id: { type: 'string', primaryKey: true } });
await db.query('INSERT INTO t VALUES (\'1\')');
const rows = await db.query('SELECT * FROM t') as Record<string, unknown>[];
expect(rows).toHaveLength(1);
await db.close();
});
it('独立 create 函数仍可用', async () => {
const db = await create({ name: uniqueDB(), mode: 'memory' });
expect(db.isReady()).toBe(true);
await db.close();
});
it('init 前 close 不崩溃', async () => {
const db = new MetonaSqlark({ name: uniqueDB(), mode: 'memory' });
await expect(db.close()).resolves.toBeUndefined();
expect(db.isReady()).toBe(false);
});
});
describe('v0.7.1 — AriaEngine 未 open 防护统一', () => {
it('close 后 getTableNames/hasTable/getTableSchema 抛 DB_NOT_OPEN', async () => {
const eng = new AriaEngine({ storageBackend: 'memory' });
await eng.open(uniqueDB(), 1);
await eng.close();
await expect(eng.getTableNames()).rejects.toMatchObject({ code: 'DB_NOT_OPEN' });
await expect(eng.hasTable('t')).rejects.toMatchObject({ code: 'DB_NOT_OPEN' });
await expect(eng.getTableSchema('t')).rejects.toMatchObject({ code: 'DB_NOT_OPEN' });
});
it('open 后正常返回(行为不变)', async () => {
const eng = new AriaEngine({ storageBackend: 'memory' });
await eng.open(uniqueDB(), 1);
expect(await eng.getTableNames()).toEqual([]);
expect(await eng.hasTable('t')).toBe(false);
expect(await eng.getTableSchema('t')).toBeNull();
await eng.close();
});
});
describe('v0.7.1 — 事务回滚失败不掩盖原始错误', () => {
class RollbackFailsEngine extends MemoryEngine {
async rollbackTransaction(): Promise<void> {
throw new Error('rollback boom');
}
async beginTransaction(): Promise<void> { /* ok */ }
async commitTransaction(): Promise<void> { /* ok */ }
}
it('事务体抛错 + 回滚失败 → 抛原始事务错误', async () => {
const eng = new RollbackFailsEngine();
const mgr = new TransactionManager(eng);
await expect(mgr.execute(async () => {
throw new Error('original tx failure');
})).rejects.toThrow('original tx failure');
});
});
describe('v0.7.1 — __proto__ 列名拒绝', () => {
it('动态构造列映射(null 原型)含 __proto__ 列名被拒绝', () => {
// 对象字面量的 `__proto__:` 在字面量层就触发原型 setter(键不存在);
// 真实攻击面是动态构造的列映射(SQL 路径 / Object.create(null))——键可被
// Object.entries 看到,校验层必须拒绝
const cols: Record<string, unknown> = Object.create(null) as Record<string, unknown>;
cols.id = { type: 'string', primaryKey: true };
cols.__proto__ = { type: 'string' };
expect(() => createSchema('t', cols as never)).toThrow('"__proto__"');
});
it('SQL CREATE TABLE 拒绝 __proto__ 列名(列不静默丢失)', async () => {
const db = await MetonaSqlark.create({ name: uniqueDB(), mode: 'memory' });
await expect(db.query('CREATE TABLE t (id STRING PRIMARY KEY, __proto__ STRING)'))
.rejects.toMatchObject({ code: 'SCHEMA_ERROR' });
await db.close();
});
});