mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 05:29:26 +08:00
* refactor(sequence-field): move to plugin and use table to record * fix(database): skip test case which not in core * fix(plugin-sequence): add migration * fix(plugin-sequence): fix types * test(plugin-sequence): fix test cases * fix(plugin-sequence): fix configuration ui * fix(plugin-sequence): fix merge * fix(plugin-sequence): fix schema and error message
758 lines
19 KiB
TypeScript
758 lines
19 KiB
TypeScript
import moment from 'moment';
|
|
|
|
import { Application } from '@nocobase/server';
|
|
import { Database } from '@nocobase/database';
|
|
import { mockServer, mockDatabase } from '@nocobase/test';
|
|
|
|
import Plugin, { SequenceField, SequenceFieldOptions } from '..';
|
|
|
|
describe('sequence field', () => {
|
|
let app: Application;
|
|
let db: Database;
|
|
|
|
beforeEach(async () => {
|
|
app = mockServer();
|
|
app.plugin(Plugin);
|
|
db = app.db;
|
|
await db.sync({
|
|
force: true,
|
|
alter: {
|
|
drop: true
|
|
}
|
|
});
|
|
await app.load();
|
|
await app.start();
|
|
});
|
|
|
|
afterEach(async () => {
|
|
await db.close();
|
|
});
|
|
|
|
describe('define', () => {
|
|
it('without any pattern will throw error', async () => {
|
|
expect(() => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [{ type: 'sequence', name: 'name' }],
|
|
});
|
|
}).toThrow();
|
|
});
|
|
|
|
it('with empty pattern will throw error', async () => {
|
|
expect(() => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [{ type: 'sequence', name: 'name', patterns: [] }],
|
|
});
|
|
}).toThrow();
|
|
});
|
|
});
|
|
|
|
describe('string pattern', () => {
|
|
it('no options', async () => {
|
|
expect(() => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [{ type: 'sequence', name: 'name', patterns: [{ type: 'string' }] }],
|
|
});
|
|
}).toThrow();
|
|
});
|
|
|
|
it('constant', async () => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{ type: 'string', options: { value: 'abc' } }
|
|
]
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create();
|
|
expect(item1.get('name')).toBe('abc');
|
|
|
|
const item2 = await TestModel.create();
|
|
expect(item2.get('name')).toBe('abc');
|
|
});
|
|
});
|
|
|
|
describe('integer pattern', () => {
|
|
it.skip('no key', async () => {
|
|
expect(() => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [{ type: 'sequence', name: 'name', patterns: [{ type: 'integer' }] }],
|
|
});
|
|
}).toThrow();
|
|
});
|
|
|
|
it('default start from 0, digits as 1, no cycle', async () => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{
|
|
type: 'integer',
|
|
options: { key: 1 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create();
|
|
expect(item1.get('name')).toBe('0');
|
|
|
|
const item2 = await TestModel.create();
|
|
expect(item2.get('name')).toBe('1');
|
|
});
|
|
|
|
it('start from 9', async () => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{
|
|
type: 'integer',
|
|
options: {
|
|
start: 9,
|
|
key: 1
|
|
}
|
|
}
|
|
]
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create();
|
|
expect(item1.get('name')).toBe('9');
|
|
|
|
const item2 = await TestModel.create();
|
|
expect(item2.get('name')).toBe('9');
|
|
});
|
|
|
|
it('start from 0, current set to 9', async () => {
|
|
const collection = db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{
|
|
type: 'integer',
|
|
options: { key: 1 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
const field = collection.getField('name');
|
|
const SeqRepo = db.getRepository('sequences');
|
|
await SeqRepo.create({
|
|
|
|
});
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create();
|
|
expect(item1.get('name')).toBe('0');
|
|
});
|
|
|
|
it('digits more than 1, start from 9', async () => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{
|
|
type: 'integer',
|
|
options: {
|
|
digits: 2,
|
|
start: 9,
|
|
key: 1
|
|
}
|
|
}
|
|
]
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create();
|
|
expect(item1.get('name')).toBe('09');
|
|
|
|
const item2 = await TestModel.create();
|
|
expect(item2.get('name')).toBe('10');
|
|
});
|
|
|
|
it('cycle by day', async () => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{
|
|
type: 'integer',
|
|
options: {
|
|
cycle: '0 0 0 * * *',
|
|
key: 1
|
|
}
|
|
}
|
|
]
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const now = new Date();
|
|
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create({
|
|
id: 1,
|
|
createdAt: yesterday
|
|
});
|
|
expect(item1.get('name')).toBe('0');
|
|
|
|
const item2 = await TestModel.create({
|
|
id: 2,
|
|
createdAt: yesterday
|
|
});
|
|
expect(item2.get('name')).toBe('1');
|
|
|
|
const item3 = await TestModel.create({ id: 3 });
|
|
expect(item3.get('name')).toBe('0');
|
|
|
|
const item4 = await TestModel.create({ id: 4 });
|
|
expect(item4.get('name')).toBe('1');
|
|
});
|
|
|
|
it('last record has no value of this field', async () => {
|
|
const testCollection = db.collection({
|
|
name: 'tests',
|
|
fields: [],
|
|
});
|
|
await db.sync();
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create();
|
|
expect(item1.get('name')).toBeUndefined();
|
|
|
|
testCollection.addField('name', {
|
|
type: 'sequence',
|
|
patterns: [
|
|
{
|
|
type: 'integer',
|
|
options: { key: 1 }
|
|
}
|
|
]
|
|
});
|
|
await db.sync();
|
|
|
|
const item2 = await TestModel.create();
|
|
expect(item2.get('name')).toBe('0');
|
|
});
|
|
|
|
it('deleted sequence should be skipped', async () => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{
|
|
type: 'integer',
|
|
options: { key: 1 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create();
|
|
expect(item1.get('name')).toBe('0');
|
|
|
|
await item1.destroy();
|
|
|
|
const item2 = await TestModel.create();
|
|
expect(item2.get('name')).toBe('1');
|
|
});
|
|
|
|
it('multiple integer in same field', async () => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{
|
|
type: 'integer',
|
|
options: { key: 1 }
|
|
},
|
|
{
|
|
type: 'integer',
|
|
options: { key: 2 }
|
|
}
|
|
]
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create();
|
|
expect(item1.get('name')).toBe('00');
|
|
|
|
const item2 = await TestModel.create();
|
|
expect(item2.get('name')).toBe('11');
|
|
});
|
|
});
|
|
|
|
describe('date pattern', () => {
|
|
it('default to current createdAt as YYYYMMDD', async () => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{ type: 'date' }
|
|
]
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const now = new Date();
|
|
const YYYYMMDD = moment(now).format('YYYYMMDD');
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create();
|
|
expect(item1.get('name')).toBe(YYYYMMDD);
|
|
});
|
|
|
|
it('field option', async () => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{ type: 'date', options: { field: 'date' } }
|
|
]
|
|
},
|
|
{
|
|
type: 'date',
|
|
name: 'date'
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const date = new Date(2022, 7, 1);
|
|
const YYYYMMDD = moment(date).format('YYYYMMDD');
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create({
|
|
date
|
|
});
|
|
expect(item1.get('name')).toBe(YYYYMMDD);
|
|
});
|
|
|
|
it('format option', async () => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{ type: 'date', options: { format: 'YYYY-MM-DD' } }
|
|
]
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const now = new Date();
|
|
const YYYYMMDD = moment(now).format('YYYY-MM-DD');
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create();
|
|
expect(item1.get('name')).toBe(YYYYMMDD);
|
|
});
|
|
});
|
|
|
|
describe('mixed pattern', () => {
|
|
it('all patterns', async () => {
|
|
db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{ type: 'string', options: { value: 'A' } },
|
|
{ type: 'date' },
|
|
{ type: 'integer', options: { key: 1 } }
|
|
]
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const now = new Date();
|
|
const YYYYMMDD = moment(now).format('YYYYMMDD');
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create();
|
|
expect(item1.get('name')).toBe(`A${YYYYMMDD}0`);
|
|
|
|
const item2 = await TestModel.create();
|
|
expect(item2.get('name')).toBe(`A${YYYYMMDD}1`);
|
|
});
|
|
|
|
it('changed after generated', async () => {
|
|
const testsCollection = db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{ type: 'string', options: { value: 'A' } },
|
|
{ type: 'date' },
|
|
{ type: 'integer', options: { key: 1 } }
|
|
]
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const now = new Date();
|
|
const YYYYMMDD = moment(now).format('YYYYMMDD');
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create();
|
|
expect(item1.get('name')).toBe(`A${YYYYMMDD}0`);
|
|
|
|
const f2 = testsCollection.setField('name', {
|
|
type: 'sequence',
|
|
patterns: [
|
|
{ type: 'string', options: { value: 'A' } },
|
|
{ type: 'date' },
|
|
// change options but no difference with default with new key
|
|
{ type: 'integer', options: { digits: 1, key: 2 } }
|
|
]
|
|
}) as SequenceField;
|
|
|
|
const item2 = await TestModel.create();
|
|
expect(item2.get('name')).toBe(`A${YYYYMMDD}0`);
|
|
|
|
const f3 = testsCollection.setField('name', {
|
|
type: 'sequence',
|
|
patterns: [
|
|
{ type: 'string', options: { value: 'A' } },
|
|
{ type: 'date' },
|
|
// change options but no difference with default with key
|
|
{ type: 'integer', options: { digits: 1, key: f2.options.patterns[2].options.key } }
|
|
]
|
|
});
|
|
|
|
const item3 = await TestModel.create();
|
|
expect(item3.get('name')).toBe(`A${YYYYMMDD}1`);
|
|
|
|
const f4 = testsCollection.setField('name', {
|
|
type: 'sequence',
|
|
patterns: [
|
|
{ type: 'string', options: { value: 'A' } },
|
|
{ type: 'date' },
|
|
{ type: 'integer', options: { digits: 2, key: 3 } }
|
|
]
|
|
});
|
|
|
|
const item4 = await TestModel.create();
|
|
expect(item4.get('name')).toBe(`A${YYYYMMDD}00`);
|
|
|
|
testsCollection.setField('name', {
|
|
type: 'sequence',
|
|
patterns: [
|
|
{ type: 'string', options: { value: 'a' } },
|
|
{ type: 'date' },
|
|
{ type: 'integer', options: { digits: 2, key: f4.options.patterns[2].options.key } }
|
|
]
|
|
});
|
|
|
|
const item5 = await TestModel.create();
|
|
expect(item5.get('name')).toBe(`a${YYYYMMDD}01`);
|
|
|
|
testsCollection.setField('name', {
|
|
type: 'sequence',
|
|
patterns: [
|
|
{ type: 'date' },
|
|
{ type: 'integer', options: { digits: 2, key: 4 } }
|
|
]
|
|
});
|
|
|
|
const item6 = await TestModel.create();
|
|
expect(item6.get('name')).toBe(`${YYYYMMDD}00`);
|
|
});
|
|
});
|
|
|
|
describe('multiple serial fields', () => {
|
|
it('2 fields', async () => {
|
|
const testsCollection = db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{ type: 'string', options: { value: 'A' } },
|
|
{ type: 'date' },
|
|
{ type: 'integer', options: { digits: 2, cycle: '0 0 * * *', key: 1 } }
|
|
]
|
|
},
|
|
{
|
|
type: 'sequence',
|
|
name: 'code',
|
|
patterns: [
|
|
{ type: 'string', options: { value: 'C' } },
|
|
{ type: 'integer', options: { digits: 4, key: 1 }}
|
|
]
|
|
}
|
|
]
|
|
});
|
|
await db.sync();
|
|
|
|
const now = new Date();
|
|
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
|
const NOW = moment(now).format('YYYYMMDD');
|
|
const YESTERDAY = moment(yesterday).format('YYYYMMDD');
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create({ createdAt: yesterday });
|
|
expect(item1.get('name')).toBe(`A${YESTERDAY}00`);
|
|
expect(item1.get('code')).toBe(`C0000`);
|
|
|
|
const item2 = await TestModel.create();
|
|
expect(item2.get('name')).toBe(`A${NOW}00`);
|
|
expect(item2.get('code')).toBe(`C0001`);
|
|
|
|
testsCollection.setField('name', {
|
|
type: 'sequence',
|
|
patterns: [
|
|
{ type: 'date' },
|
|
{ type: 'integer', options: { digits: 1, key: 1 } },
|
|
{ type: 'string', options: { value: 'a' } },
|
|
]
|
|
});
|
|
|
|
const item3 = await TestModel.create();
|
|
expect(item3.get('name')).toBe(`${NOW}1a`);
|
|
expect(item3.get('code')).toBe(`C0002`);
|
|
});
|
|
});
|
|
|
|
describe('inputable', () => {
|
|
it('not inputable', async () => {
|
|
const testsCollection = db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{ type: 'string', options: { value: 'A' } },
|
|
{ type: 'date' },
|
|
{ type: 'integer', options: { key: 1 } }
|
|
],
|
|
inputable: false
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const now = new Date();
|
|
const YYYYMMDD = moment(now).format('YYYYMMDD');
|
|
const name = `BB${YYYYMMDD}11`;
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const result = await TestModel.create({ name });
|
|
expect(result.name).toBe(`A${YYYYMMDD}0`);
|
|
});
|
|
|
|
it('inputable without match', async () => {
|
|
const testsCollection = db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{ type: 'string', options: { value: 'A' } },
|
|
{ type: 'date' },
|
|
{ type: 'integer', options: { key: 1 } }
|
|
],
|
|
inputable: true,
|
|
match: false
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const now = new Date();
|
|
const YYYYMMDD = moment(now).format('YYYYMMDD');
|
|
const name = `BB${YYYYMMDD}11`;
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const result = await TestModel.create({ name });
|
|
expect(result.name).toBe(name);
|
|
});
|
|
|
|
it('inputable with match', async () => {
|
|
const testsCollection = db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{ type: 'string', options: { value: 'A' } },
|
|
{ type: 'date' },
|
|
{ type: 'integer', options: { key: 1 } }
|
|
],
|
|
inputable: true,
|
|
match: true
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const now = new Date();
|
|
const YYYYMMDD = moment(now).format('YYYYMMDD');
|
|
|
|
const TestModel = db.getModel('tests');
|
|
await expect(TestModel.create({ name: `BB${YYYYMMDD}11` })).rejects.toThrow();
|
|
});
|
|
|
|
it('input value within generated sequence', async () => {
|
|
const testsCollection = db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{ type: 'integer', options: { key: 1 } }
|
|
],
|
|
inputable: true,
|
|
match: true
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create();
|
|
expect(item1.name).toBe('0');
|
|
|
|
const item2 = await TestModel.create({ name: '0' });
|
|
expect(item2.name).toBe('0');
|
|
|
|
const item3 = await TestModel.create();
|
|
expect(item3.name).toBe('1');
|
|
});
|
|
|
|
it('input value beyond generated sequence', async () => {
|
|
const testsCollection = db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{ type: 'integer', options: { key: 1 } }
|
|
],
|
|
inputable: true,
|
|
match: true
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create();
|
|
expect(item1.name).toBe('0');
|
|
|
|
const item2 = await TestModel.create({ name: '2' });
|
|
expect(item2.name).toBe('2');
|
|
|
|
const item3 = await TestModel.create();
|
|
expect(item3.name).toBe('3');
|
|
});
|
|
|
|
it('input value with cycle', async () => {
|
|
const testsCollection = db.collection({
|
|
name: 'tests',
|
|
fields: [
|
|
{
|
|
type: 'sequence',
|
|
name: 'name',
|
|
patterns: [
|
|
{ type: 'integer', options: { key: 1, cycle: '0 0 0 * * *' } }
|
|
],
|
|
inputable: true,
|
|
match: true
|
|
}
|
|
],
|
|
});
|
|
await db.sync();
|
|
|
|
const now = new Date();
|
|
const yesterday = new Date(now.getTime() - 24 * 60 * 60 * 1000);
|
|
|
|
const TestModel = db.getModel('tests');
|
|
const item1 = await TestModel.create({ createdAt: yesterday });
|
|
expect(item1.name).toBe('0');
|
|
|
|
const item2 = await TestModel.create({ name: '0', createdAt: yesterday });
|
|
expect(item2.name).toBe('0');
|
|
|
|
const item3 = await TestModel.create({ createdAt: yesterday });
|
|
expect(item3.name).toBe('1');
|
|
|
|
const item4 = await TestModel.create();
|
|
expect(item4.name).toBe('0');
|
|
});
|
|
});
|
|
});
|