diff --git a/packages/core/src/__tests__/client.test.ts b/packages/core/src/__tests__/client.test.ts index 4d351e47c7..a7cfc66dcc 100644 --- a/packages/core/src/__tests__/client.test.ts +++ b/packages/core/src/__tests__/client.test.ts @@ -1,6 +1,6 @@ // // Copyright © 2020, 2021 Anticrm Platform Contributors. -// Copyright © 2021 Hardcore Engineering, Inc. +// Copyright © 2021, 2022 Hardcore Engineering, Inc. // // Licensed under the Eclipse Public License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. You may @@ -13,15 +13,21 @@ // See the License for the specific language governing permissions and // limitations under the License. // - -import { Space } from '../classes' -import { createClient } from '../client' +import { Plugin, IntlString } from '@hcengineering/platform' +import type { Class, Doc, Domain, Ref } from '../classes' +import { Space, ClassifierKind, DOMAIN_MODEL } from '../classes' +import { createClient, ClientConnection } from '../client' import core from '../component' +import { Hierarchy } from '../hierarchy' +import { ModelDb, TxDb } from '../memdb' import { TxOperations } from '../operations' +import type { DocumentQuery, FindResult, TxResult } from '../storage' +import { Tx, TxFactory, TxProcessor } from '../tx' import { connect } from './connection' +import { genMinModel } from './minmodel' describe('client', () => { - it('client', async () => { + it('should create client and spaces', async () => { const klass = core.class.Space const client = new TxOperations(await createClient(connect), core.account.System) const result = await client.findAll(klass, {}) @@ -46,5 +52,127 @@ describe('client', () => { }) const result3 = await client.findAll(klass, {}) expect(result3).toHaveLength(4) + + const result4 = await client.findOne(klass, {}) + expect(result4).toEqual(result3[0]) + }) + + it('should create client with plugins', async () => { + const txFactory = new TxFactory(core.account.System) + const txes = genMinModel() + + txes.push( + txFactory.createTxCreateDoc( + core.class.Class, + core.space.Model, + { + label: 'PluginConfiguration' as IntlString, + extends: core.class.Doc, + kind: ClassifierKind.CLASS, + domain: DOMAIN_MODEL + }, + core.class.PluginConfiguration + ) + ) + + async function connectPlugin (handler: (tx: Tx) => void): Promise { + const hierarchy = new Hierarchy() + + for (const tx of txes) hierarchy.tx(tx) + + const transactions = new TxDb(hierarchy) + const model = new ModelDb(hierarchy) + for (const tx of txes) { + await transactions.tx(tx) + await model.tx(tx) + } + + async function findAll (_class: Ref>, query: DocumentQuery): Promise> { + return await transactions.findAll(_class, query) + } + + return { + findAll, + tx: async (tx: Tx): Promise => { + if (tx.objectSpace === core.space.Model) { + hierarchy.tx(tx) + } + const result = await Promise.all([transactions.tx(tx)]) + return result[0] + }, + close: async () => {}, + + loadChunk: async (domain: Domain, idx?: number) => ({ + idx: -1, + index: -1, + docs: {}, + finished: true, + digest: '' + }), + closeChunk: async (idx: number) => {}, + loadDocs: async (domain: Domain, docs: Ref[]) => [], + upload: async (domain: Domain, docs: Doc[]) => {}, + clean: async (domain: Domain, docs: Ref[]) => {} + } + } + const spyCreate = jest.spyOn(TxProcessor, 'createDoc2Doc') + const spyUpdate = jest.spyOn(TxProcessor, 'updateDoc2Doc') + + const pluginData1 = { + pluginId: 'testPlugin1', + transactions: [] + } + const txCreateDoc1 = txFactory.createTxCreateDoc(core.class.PluginConfiguration, core.space.Model, pluginData1) + txes.push(txCreateDoc1) + const client1 = new TxOperations(await createClient(connectPlugin, ['testPlugin1' as Plugin]), core.account.System) + const result1 = await client1.findAll(core.class.PluginConfiguration, {}) + + expect(result1).toHaveLength(1) + expect(result1[0]._id).toStrictEqual(txCreateDoc1.objectId) + expect(spyCreate).toHaveBeenLastCalledWith(txCreateDoc1) + expect(spyUpdate).toBeCalledTimes(0) + await client1.close() + + const pluginData2 = { + pluginId: 'testPlugin2', + transactions: [] + } + const txCreateDoc2 = txFactory.createTxCreateDoc(core.class.PluginConfiguration, core.space.Model, pluginData2) + txes.push(txCreateDoc2) + const client2 = new TxOperations(await createClient(connectPlugin, ['testPlugin1' as Plugin]), core.account.System) + const result2 = await client2.findAll(core.class.PluginConfiguration, {}) + + expect(result2).toHaveLength(2) + expect(result2[0]._id).toStrictEqual(txCreateDoc1.objectId) + expect(result2[1]._id).toStrictEqual(txCreateDoc2.objectId) + expect(spyCreate).toHaveBeenLastCalledWith(txCreateDoc2) + expect(spyUpdate).toBeCalledTimes(0) + await client2.close() + + const pluginData3 = { + pluginId: 'testPlugin3', + transactions: [txCreateDoc1._id] + } + const txUpdateDoc = txFactory.createTxUpdateDoc( + core.class.PluginConfiguration, + core.space.Model, + txCreateDoc1.objectId, + pluginData3 + ) + txes.push(txUpdateDoc) + const client3 = new TxOperations(await createClient(connectPlugin, ['testPlugin2' as Plugin]), core.account.System) + const result3 = await client3.findAll(core.class.PluginConfiguration, {}) + + expect(result3).toHaveLength(1) + expect(result3[0]._id).toStrictEqual(txCreateDoc2.objectId) + expect(spyCreate).toHaveBeenLastCalledWith(txCreateDoc2) + expect(spyUpdate.mock.calls[1][1]).toStrictEqual(txUpdateDoc) + expect(spyUpdate).toBeCalledTimes(2) + await client3.close() + + spyCreate.mockReset() + spyCreate.mockRestore() + spyUpdate.mockReset() + spyUpdate.mockRestore() }) }) diff --git a/packages/core/src/__tests__/hierarchy.test.ts b/packages/core/src/__tests__/hierarchy.test.ts index 9fbad6bf03..babc79a352 100644 --- a/packages/core/src/__tests__/hierarchy.test.ts +++ b/packages/core/src/__tests__/hierarchy.test.ts @@ -17,6 +17,7 @@ import type { Class, Doc, Obj, Ref } from '../classes' import type { TxCreateDoc } from '../tx' import core from '../component' import { Hierarchy } from '../hierarchy' +import * as Proxy from '../proxy' import { genMinModel, test } from './minmodel' const txes = genMinModel() @@ -69,4 +70,40 @@ describe('hierarchy', () => { const modelDomain = hierarchy.getDomain(core.class.Class) expect(modelDomain).toBe('model') }) + + it('should create Mixin proxy', async () => { + const spyProxy = jest.spyOn(Proxy, '_createMixinProxy') + const hierarchy = prepare() + + hierarchy.as(txes[0], test.mixin.TestMixin) + expect(spyProxy).toBeCalledTimes(1) + + hierarchy.as(txes[0], test.mixin.TestMixin) + expect(spyProxy).toBeCalledTimes(1) + + spyProxy.mockReset() + spyProxy.mockRestore() + }) + + it('should call static methods', async () => { + const spyToDoc = jest.spyOn(Proxy, '_toDoc') + Hierarchy.toDoc(txes[0]) + expect(spyToDoc).toBeCalledTimes(1) + spyToDoc.mockReset() + spyToDoc.mockRestore() + + const spyMixinClass = jest.spyOn(Proxy, '_mixinClass') + Hierarchy.mixinClass(txes[0]) + expect(spyMixinClass).toBeCalledTimes(1) + + spyMixinClass.mockImplementationOnce(() => undefined).mockImplementationOnce(() => test.mixin.TestMixin) + let result = Hierarchy.mixinOrClass(txes[0]) + expect(result).toStrictEqual(txes[0]._class) + result = Hierarchy.mixinOrClass(txes[0]) + expect(result).toStrictEqual(test.mixin.TestMixin) + expect(spyMixinClass).toBeCalledTimes(3) + + spyMixinClass.mockReset() + spyMixinClass.mockRestore() + }) })