Files
MetonaSqlark/tests/table/schema.test.ts
T
thzxx e2a590c5b1 feat: metona-sqlark v0.1.12 — 前端TypeScript关系型数据库
- 4种存储引擎:Memory / IndexedDB / OPFS / Hybrid
- 完整SQL支持:SELECT/INSERT/UPDATE/DELETE/JOIN/GROUP BY/HAVING/DISTINCT
- Query Builder链式API + TypeScript泛型支持
- 聚合函数:COUNT/SUM/AVG/MIN/MAX
- 事务、插件系统(14 hooks)、发布订阅、数据迁移、导入导出
- React/Vue框架集成
- 264个测试用例,93.46%覆盖率
- 零运行时依赖
2026-07-26 15:00:01 +08:00

166 lines
5.5 KiB
TypeScript

/**
* Schema 边缘场景测试
*/
import { createSchema, validateRow, getPrimaryKey, checkFieldType, astColumnToColumnDef } from '../../src/table/schema';
describe('Schema 边缘场景', () => {
// ---- createSchema ----
describe('createSchema', () => {
it('空列表抛出错误', () => {
expect(() => createSchema('empty', {})).toThrow('at least one column');
});
it('多主键定义(取第一个标记的)', () => {
const schema = createSchema('multi', {
id1: { type: 'string', primaryKey: true },
id2: { type: 'string' },
});
expect(getPrimaryKey(schema)).toBe('id1');
});
it('大表定义', () => {
const cols: Record<string, any> = {};
for (let i = 0; i < 50; i++) {
cols[`col${i}`] = { type: 'string' };
}
cols.id = { type: 'string', primaryKey: true };
const schema = createSchema('big', cols);
expect(Object.keys(schema.columns)).toHaveLength(51);
});
});
// ---- getPrimaryKey ----
describe('getPrimaryKey', () => {
it('没有标记主键时返回第一列', () => {
const schema = createSchema('test', {
uuid: { type: 'string', primaryKey: true },
name: { type: 'string' },
});
// 重新构造不带主键标记的 schema
const noPkSchema = { name: 'test', columns: {
first: { type: 'string' as const },
second: { type: 'string' as const },
}};
expect(getPrimaryKey(noPkSchema)).toBe('first');
});
});
// ---- validateRow ----
describe('validateRow', () => {
const schema = createSchema('users', {
id: { type: 'string', primaryKey: true },
name: { type: 'string', required: true },
bio: { type: 'string', maxLength: 200 },
score: { type: 'number', min: 0, max: 100, default: 50 },
active: { type: 'boolean', default: true },
birthday: { type: 'date' },
tags: { type: 'json', default: [] },
});
it('跳过 undefined 字段(使用默认值)', () => {
const row = validateRow(schema, { id: '1', name: 'Alice' });
expect(row.id).toBe('1');
expect(row.name).toBe('Alice');
expect(row.score).toBe(50);
expect(row.active).toBe(true);
expect(row.tags).toEqual([]);
});
it('null 值被视为不满足必填', () => {
expect(() => validateRow(schema, { id: '1', name: null })).toThrow('required');
});
it('json 类型接受对象', () => {
const row = validateRow(schema, { id: '1', name: 'Alice', tags: { key: 'value' } });
expect(row.tags).toEqual({ key: 'value' });
});
it('json 类型接受数组', () => {
const row = validateRow(schema, { id: '1', name: 'Alice', tags: [1, 2, 3] });
expect(row.tags).toEqual([1, 2, 3]);
});
it('date 类型接受有效日期字符串', () => {
const row = validateRow(schema, { id: '1', name: 'Alice', birthday: '2024-01-15' });
expect(row.birthday).toBe('2024-01-15');
});
it('date 类型拒绝无效格式', () => {
expect(() => validateRow(schema, { id: '1', name: 'Alice', birthday: 'not-a-date' }))
.toThrow('expects valid date');
expect(() => validateRow(schema, { id: '1', name: 'Alice', birthday: 123 }))
.toThrow('expects valid date');
});
it('boolean 类型', () => {
const row = validateRow(schema, { id: '1', name: 'Alice', active: false });
expect(row.active).toBe(false);
});
it('boolean 拒绝非布尔值', () => {
expect(() => validateRow(schema, { id: '1', name: 'Alice', active: 'yes' }))
.toThrow('expects boolean');
});
it('number 拒绝字符串', () => {
expect(() => validateRow(schema, { id: '1', name: 'Alice', score: 'high' }))
.toThrow('expects number');
});
it('string 拒绝数字', () => {
expect(() => validateRow(schema, { id: 123, name: 'Alice' }))
.toThrow('expects string');
});
});
// ---- checkFieldType ----
describe('checkFieldType', () => {
it('string 类型通过', () => {
expect(() => checkFieldType('t', 'c', 'string', 'hello')).not.toThrow();
});
it('string 类型拒绝', () => {
expect(() => checkFieldType('t', 'c', 'string', 123)).toThrow('expects string');
});
it('number 类型通过', () => {
expect(() => checkFieldType('t', 'c', 'number', 42)).not.toThrow();
expect(() => checkFieldType('t', 'c', 'number', 3.14)).not.toThrow();
expect(() => checkFieldType('t', 'c', 'number', 0)).not.toThrow();
});
it('boolean 类型通过', () => {
expect(() => checkFieldType('t', 'c', 'boolean', true)).not.toThrow();
expect(() => checkFieldType('t', 'c', 'boolean', false)).not.toThrow();
});
it('json 拒绝非对象', () => {
expect(() => checkFieldType('t', 'c', 'json', 'not-object')).toThrow('expects object');
expect(() => checkFieldType('t', 'c', 'json', 123)).toThrow('expects object');
});
});
// ---- astColumnToColumnDef ----
describe('astColumnToColumnDef', () => {
it('转换基础列', () => {
const result = astColumnToColumnDef({ name: 'id', type: 'string' });
expect(result.type).toBe('string');
});
it('转换带修饰符的列', () => {
const result = astColumnToColumnDef({
name: 'id', type: 'string', primaryKey: true, unique: true, required: true,
});
expect(result.primaryKey).toBe(true);
expect(result.unique).toBe(true);
expect(result.required).toBe(true);
});
});
});