From e76eb1edac1dd97964a5fd4f43aa24b4554c414a Mon Sep 17 00:00:00 2001 From: ChengLei Shao Date: Sun, 6 Mar 2022 19:12:44 +0800 Subject: [PATCH] add: test (#224) * add: test * feat: updateAssociation Skip reverseAssociationPair * feat: reverseAssociationPair with HasMany && HasOne --- .../{respsitory => repository}/count.test.ts | 0 .../src/__tests__/repository/create.test.ts | 163 ++++++++++++++++++ .../destroy.test.ts | 0 .../{respsitory => repository}/find.test.ts | 0 .../src/__tests__/respsitory/create.test.ts | 43 ----- packages/database/src/update-associations.ts | 66 ++++++- 6 files changed, 224 insertions(+), 48 deletions(-) rename packages/database/src/__tests__/{respsitory => repository}/count.test.ts (100%) create mode 100644 packages/database/src/__tests__/repository/create.test.ts rename packages/database/src/__tests__/{respsitory => repository}/destroy.test.ts (100%) rename packages/database/src/__tests__/{respsitory => repository}/find.test.ts (100%) delete mode 100644 packages/database/src/__tests__/respsitory/create.test.ts diff --git a/packages/database/src/__tests__/respsitory/count.test.ts b/packages/database/src/__tests__/repository/count.test.ts similarity index 100% rename from packages/database/src/__tests__/respsitory/count.test.ts rename to packages/database/src/__tests__/repository/count.test.ts diff --git a/packages/database/src/__tests__/repository/create.test.ts b/packages/database/src/__tests__/repository/create.test.ts new file mode 100644 index 0000000000..a1072e4054 --- /dev/null +++ b/packages/database/src/__tests__/repository/create.test.ts @@ -0,0 +1,163 @@ +import { mockDatabase } from '../index'; +import Database from '../../database'; + +describe('create with hasMany', () => { + let db: Database; + let Post; + let User; + + afterEach(async () => { + await db.close(); + }); + + beforeEach(async () => { + db = mockDatabase(); + + await db.clean({ drop: true }); + User = db.collection({ + name: 'users', + fields: [ + { + type: 'string', + name: 'name', + }, + { + type: 'hasMany', + name: 'posts', + }, + ], + }); + + Post = db.collection({ + name: 'posts', + fields: [ + { + type: 'string', + name: 'title', + }, + { + type: 'belongsTo', + name: 'user', + }, + ], + }); + + await db.sync(); + }); + + it('should save associations with reverseField value', async () => { + const u1 = await db.getRepository('users').create({ + values: { + name: 'u1', + posts: [{ title: 't1', user: null }], + }, + }); + + const p1 = await db.getRepository('posts').findOne(); + // @ts-ignore + expect(await p1.getUser()).not.toBeNull(); + }); +}); + +describe('create with belongsToMany', () => { + let db: Database; + let Post; + let Tag; + + afterEach(async () => { + await db.close(); + }); + + beforeEach(async () => { + db = mockDatabase(); + Post = db.collection({ + name: 'posts', + fields: [ + { + type: 'string', + name: 'title', + }, + { + type: 'belongsToMany', + name: 'tags', + }, + ], + }); + + Tag = db.collection({ + name: 'tags', + fields: [ + { + type: 'string', + name: 'name', + }, + { + type: 'belongsToMany', + name: 'posts', + }, + ], + }); + + await db.sync(); + }); + + it('should save associations with reverseField value', async () => { + const t1 = await db.getRepository('tags').create({ + values: { + name: 't1', + }, + }); + + const p1 = await db.getRepository('posts').create({ + values: { + title: 'p1', + tags: [{ id: t1.get('id'), name: 't1', posts: [] }], + }, + }); + + // @ts-ignore + expect(await p1.countTags()).toEqual(1); + }); +}); + +describe('create', () => { + let db: Database; + let User; + let Post; + + beforeEach(async () => { + db = mockDatabase(); + User = db.collection({ + name: 'users', + fields: [ + { type: 'string', name: 'name' }, + { type: 'hasMany', name: 'posts' }, + ], + }); + + Post = db.collection({ + name: 'posts', + fields: [ + { type: 'string', name: 'title' }, + { type: 'belongsTo', name: 'user' }, + ], + }); + await db.sync(); + }); + + afterEach(async () => { + await db.close(); + }); + + test('create with association', async () => { + const u1 = await User.repository.create({ + values: { + name: 'u1', + posts: [{ title: 'u1p1' }], + }, + }); + + expect(u1.name).toEqual('u1'); + expect(await u1.countPosts()).toEqual(1); + }); +}); diff --git a/packages/database/src/__tests__/respsitory/destroy.test.ts b/packages/database/src/__tests__/repository/destroy.test.ts similarity index 100% rename from packages/database/src/__tests__/respsitory/destroy.test.ts rename to packages/database/src/__tests__/repository/destroy.test.ts diff --git a/packages/database/src/__tests__/respsitory/find.test.ts b/packages/database/src/__tests__/repository/find.test.ts similarity index 100% rename from packages/database/src/__tests__/respsitory/find.test.ts rename to packages/database/src/__tests__/repository/find.test.ts diff --git a/packages/database/src/__tests__/respsitory/create.test.ts b/packages/database/src/__tests__/respsitory/create.test.ts deleted file mode 100644 index a7da75d0f5..0000000000 --- a/packages/database/src/__tests__/respsitory/create.test.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { mockDatabase } from '../index'; -import Database from '../../database'; - -describe('create', () => { - let db: Database; - let User; - let Post; - - beforeEach(async () => { - db = mockDatabase(); - User = db.collection({ - name: 'users', - fields: [ - { type: 'string', name: 'name' }, - { type: 'hasMany', name: 'posts' }, - ], - }); - - Post = db.collection({ - name: 'posts', - fields: [ - { type: 'string', name: 'title' }, - { type: 'belongsTo', name: 'user' }, - ], - }); - await db.sync(); - }); - - afterEach(async () => { - await db.close(); - }); - test('create with association', async () => { - const u1 = await User.repository.create({ - values: { - name: 'u1', - posts: [{ title: 'u1p1' }], - }, - }); - - expect(u1.name).toEqual('u1'); - expect(await u1.countPosts()).toEqual(1); - }); -}); diff --git a/packages/database/src/update-associations.ts b/packages/database/src/update-associations.ts index a03c7b0444..4567c437c9 100644 --- a/packages/database/src/update-associations.ts +++ b/packages/database/src/update-associations.ts @@ -6,7 +6,7 @@ import { HasOne, Hookable, ModelCtor, - Transactionable + Transactionable, } from 'sequelize'; import { Model } from './model'; import { TransactionAble } from './repository'; @@ -64,6 +64,7 @@ interface UpdateAssociationOptions extends Transactionable, Hookable { updateAssociationValues?: string[]; sourceModel?: Model; context?: any; + associationContext?: any; } export async function updateModelByValues(instance: Model, values: UpdateValue, options?: UpdateOptions) { @@ -164,6 +165,37 @@ export async function updateAssociations(instance: Model, values: any, options: } } +function isReverseAssociationPair(a: any, b: any) { + const typeSet = new Set(); + typeSet.add(a.associationType); + typeSet.add(b.associationType); + + if (typeSet.size == 1 && typeSet.has('BelongsToMany')) { + return ( + a.through.tableName === b.through.tableName && + a.target.name === b.source.name && + b.target.name === a.source.name && + a.foreignKey === b.otherKey && + a.sourceKey === b.targetKey && + a.otherKey === b.foreignKey && + a.targetKey === b.sourceKey + ); + } + + if ((typeSet.has('HasOne') && typeSet.has('BelongsTo')) || (typeSet.has('HasMany') && typeSet.has('BelongsTo'))) { + const sourceAssoc = a.associationType == 'BelongsTo' ? b : a; + const targetAssoc = sourceAssoc == a ? b : a; + + return ( + sourceAssoc.source.name === targetAssoc.target.name && + sourceAssoc.foreignKey === targetAssoc.foreignKey && + sourceAssoc.sourceKey === targetAssoc.targetKey + ); + } + + return false; +} + /** * update model association by key * @param instance @@ -183,6 +215,10 @@ export async function updateAssociation( return false; } + if (options.associationContext && isReverseAssociationPair(association, options.associationContext)) { + return false; + } + switch (association.associationType) { case 'HasOne': case 'BelongsTo': @@ -280,7 +316,12 @@ export async function updateSingleAssociation( await instance.update(value, { ...options, transaction }); } - await updateAssociations(instance, value, { ...options, transaction, updateAssociationValues: keys }); + await updateAssociations(instance, value, { + ...options, + transaction, + associationContext: association, + updateAssociationValues: keys, + }); model.setDataValue(key, instance); if (!options.transaction) { await transaction.commit(); @@ -290,7 +331,12 @@ export async function updateSingleAssociation( } const instance = await model[createAccessor](value, { context, transaction }); - await updateAssociations(instance, value, { ...options, transaction, updateAssociationValues: keys }); + await updateAssociations(instance, value, { + ...options, + transaction, + associationContext: association, + updateAssociationValues: keys, + }); model.setDataValue(key, instance); // @ts-ignore if (association.targetKey) { @@ -392,7 +438,12 @@ export async function updateMultipleAssociation( if (isUndefinedOrNull(item[pk])) { // create new record const instance = await model[createAccessor](item, accessorOptions); - await updateAssociations(instance, item, { ...options, transaction, updateAssociationValues: keys }); + await updateAssociations(instance, item, { + ...options, + transaction, + associationContext: association, + updateAssociationValues: keys, + }); list3.push(instance); } else { // set & update record @@ -405,7 +456,12 @@ export async function updateMultipleAssociation( if (updateAssociationValues.includes(key)) { await instance.update(item, { ...options, transaction }); } - await updateAssociations(instance, item, { ...options, transaction, updateAssociationValues: keys }); + await updateAssociations(instance, item, { + ...options, + transaction, + associationContext: association, + updateAssociationValues: keys, + }); list3.push(instance); } }