Junyi e3e352ffeb
refactor(sequence-field): move to plugin and use table to record (#1209)
* 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
2022-12-16 01:04:22 -08:00

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');
});
});
});