Files
MetonaSqlark/tests/table/schema.test.ts
T
thzxx d33f4ab3b0
CI / test (18.x) (push) Successful in 9m58s
CI / test (20.x) (push) Successful in 9m58s
CI / test (22.x) (push) Successful in 10m18s
CI / test (24.x) (push) Successful in 10m2s
test: OPFSEngine 12项mock测试 + Schema约束5项测试 → 543 passed
2026-07-27 21:34:12 +08:00

190 lines
6.4 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.
/**
* 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');
});
});
// ---- 约束校验(v0.2.3 ----
describe('约束校验', () => {
it('string 超出 maxLength 抛出错误', () => {
expect(() => checkFieldType('t', 'c', 'string', 'hello', { type: 'string', maxLength: 3 }))
.toThrow('exceeds max length');
});
it('string 未超 maxLength 不报错', () => {
expect(() => checkFieldType('t', 'c', 'string', 'hi', { type: 'string', maxLength: 5 }))
.not.toThrow();
});
it('number 低于 min 抛出错误', () => {
expect(() => checkFieldType('t', 'c', 'number', -5, { type: 'number', min: 0 }))
.toThrow('below minimum');
});
it('number 高于 max 抛出错误', () => {
expect(() => checkFieldType('t', 'c', 'number', 200, { type: 'number', max: 100 }))
.toThrow('above maximum');
});
it('number 在范围内不报错', () => {
expect(() => checkFieldType('t', 'c', 'number', 50, { type: 'number', min: 0, max: 100 }))
.not.toThrow();
});
});
// ---- 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);
});
});
});