From 7f9756cf0996440c743464dd359cffc91ae6442e Mon Sep 17 00:00:00 2001 From: Shigma <1700011071@pku.edu.cn> Date: Tue, 31 Dec 2019 21:28:54 +0800 Subject: [PATCH] test: level tests --- packages/database-level/src/database.ts | 23 +++++++++++------ packages/database-level/src/group.ts | 16 ++++++------ packages/database-level/src/user.ts | 22 ++++++++-------- packages/database-level/tests/level.spec.ts | 6 +++-- packages/database-mysql/src/group.ts | 4 +-- packages/koishi-utils/src/observe.ts | 28 +++++++++++++-------- packages/test-utils/src/database.ts | 23 ++++++++++++++++- 7 files changed, 79 insertions(+), 43 deletions(-) diff --git a/packages/database-level/src/database.ts b/packages/database-level/src/database.ts index f58221a6ab..f75a7aff71 100644 --- a/packages/database-level/src/database.ts +++ b/packages/database-level/src/database.ts @@ -76,14 +76,15 @@ export class LevelDatabase { } async create (table: K, data: Partial) { - const id = 1 + await new Promise((resolve, reject) => { - this.tables[table].createKeyStream({ reverse: true, limit: 1 }) - .on('data', key => resolve(key)) - .on('error', error => reject(error)) - .on('end', () => resolve(0)) - }) - if (!data.id) data.id = id - await (this.tables[table] as any).put(id, data) + if (typeof data.id !== 'number') { + data.id = 1 + await new Promise((resolve, reject) => { + this.tables[table].createKeyStream({ reverse: true, limit: 1 }) + .on('data', key => resolve(key)) + .on('error', error => reject(error)) + .on('end', () => resolve(0)) + }) + } + await (this.tables[table] as any).put(data.id, data) return data as TableData[K] } @@ -91,6 +92,12 @@ export class LevelDatabase { return this.tables[table].del(id) } + async update (table: K, id: number, data: Partial) { + const sub = this.tables[table] as LevelUp + const originalData = await sub.get(id) + return sub.put(id, { ...originalData, ...data }) + } + count (table: TableType) { return new Promise((resolve, reject) => { let userNum = 0 diff --git a/packages/database-level/src/group.ts b/packages/database-level/src/group.ts index 759d12c8a8..e263a6c265 100644 --- a/packages/database-level/src/group.ts +++ b/packages/database-level/src/group.ts @@ -1,4 +1,4 @@ -import { getSelfIds, injectMethods, GroupData, createGroup } from 'koishi-core' +import { getSelfIds, injectMethods, GroupData, createGroup, Group } from 'koishi-core' import { noop, observe } from 'koishi-utils' import {} from './database' @@ -35,20 +35,20 @@ injectMethods('level', 'group', { }, async setGroup (groupId, data) { - const originalData = await this.tables.group.get(groupId) - const newData: GroupData = { ...originalData, ...data } - await this.tables.group.put(groupId, newData) + return this.update('group', groupId, data) }, async observeGroup (group, selfId) { if (typeof group === 'number') { - selfId = typeof selfId === 'number' ? selfId : 0 const data = await this.getGroup(group, selfId) return data && observe(data, diff => this.setGroup(group, diff), `group ${group}`) - } else if ('_diff' in group) { - return group + } + + const data = await this.getGroup(group.id, selfId) + if ('_diff' in group) { + return (group as Group)._merge(data) } else { - return observe(group, diff => this.setGroup(group.id, diff), `group ${group.id}`) + return observe(Object.assign(group, data), diff => this.setGroup(group.id, diff), `group ${group.id}`) } }, diff --git a/packages/database-level/src/user.ts b/packages/database-level/src/user.ts index c50395bc24..451220c17e 100644 --- a/packages/database-level/src/user.ts +++ b/packages/database-level/src/user.ts @@ -1,4 +1,4 @@ -import { injectMethods, UserData, createUser } from 'koishi-core' +import { injectMethods, UserData, createUser, User } from 'koishi-core' import { observe, noop } from 'koishi-utils' import {} from './database' @@ -21,7 +21,7 @@ injectMethods('level', 'user', { }, async getUsers (...args) { - if (args.length > 1 || args.length && typeof args[0][0] === 'number') { + if (args.length > 1 || args.length && typeof args[0][0] !== 'string') { if (!args[0].length) return [] const users = await Promise.all(args[0].map(id => this.tables.user.get(id).catch(noop))) return users.filter(Boolean) as UserData[] @@ -36,20 +36,20 @@ injectMethods('level', 'user', { }, async setUser (userId, data) { - const originalData = await this.getUser(userId) - const newData: UserData = { ...originalData, ...data } - await this.tables.user.put(userId, newData) + return this.update('user', userId, data) }, async observeUser (user, authority) { if (typeof user === 'number') { - authority = typeof authority === 'number' ? authority : 0 - const dasDatum = await this.getUser(user, authority) - return dasDatum && observe(dasDatum, diff => this.setUser(user, diff), `user ${user}`) - } else if ('_diff' in user) { - return user + const data = await this.getUser(user, authority) + return data && observe(data, diff => this.setUser(user, diff), `user ${user}`) + } + + const data = await this.getUser(user.id, authority) + if ('_diff' in user) { + return (user as User)._merge(data) } else { - return observe(user, diff => this.setUser(user.id, diff), `user ${user}`) + return observe(Object.assign(user, data), diff => this.setUser(user.id, diff), `user ${user.id}`) } }, diff --git a/packages/database-level/tests/level.spec.ts b/packages/database-level/tests/level.spec.ts index 1179c887cd..b2d5cf339a 100644 --- a/packages/database-level/tests/level.spec.ts +++ b/packages/database-level/tests/level.spec.ts @@ -71,9 +71,11 @@ describe('other methods', () => { await expect(db.getFooCount()).resolves.toBe(1) await expect(db.createFoo()).resolves.toMatchObject({ id: 3 }) await expect(db.getFooCount()).resolves.toBe(2) + await expect(db.createFoo({ id: 100 })).resolves.toMatchObject({ id: 100 }) + await expect(db.getFooCount()).resolves.toBe(3) await expect(db.removeFoo(3)).resolves.toBeUndefined() - await expect(db.getFooCount()).resolves.toBe(1) - await expect(db.createFoo()).resolves.toMatchObject({ id: 3 }) await expect(db.getFooCount()).resolves.toBe(2) + await expect(db.createFoo()).resolves.toMatchObject({ id: 3 }) + await expect(db.getFooCount()).resolves.toBe(3) }) }) diff --git a/packages/database-mysql/src/group.ts b/packages/database-mysql/src/group.ts index fb84cea926..5ce3a709a9 100644 --- a/packages/database-mysql/src/group.ts +++ b/packages/database-mysql/src/group.ts @@ -1,5 +1,5 @@ import { contain, observe, difference, Observed } from 'koishi-utils' -import { getSelfIds, injectMethods, GroupData, createGroup, groupFields, GroupField } from 'koishi-core' +import { getSelfIds, injectMethods, GroupData, createGroup, groupFields, GroupField, Group } from 'koishi-core' declare module './database' { interface MysqlDatabaseConfig { @@ -79,7 +79,7 @@ injectMethods('mysql', 'group', { ? await this.getGroup(group.id, selfId, difference(fields, Object.keys(group))) : {} as Partial if ('_diff' in group) { - return (group as Observed)._merge(additionalData) + return (group as Group)._merge(additionalData) } else { return observe(Object.assign(group, additionalData), diff => this.setGroup(group.id, diff), `group ${group.id}`) } diff --git a/packages/koishi-utils/src/observe.ts b/packages/koishi-utils/src/observe.ts index 5aef6dbcfe..8507ca51b7 100644 --- a/packages/koishi-utils/src/observe.ts +++ b/packages/koishi-utils/src/observe.ts @@ -8,7 +8,7 @@ const builtinClasses = ['Date', 'RegExp', 'Set', 'Map', 'WeakSet', 'WeakMap', 'A const refs: Record = {} function observeObject (target: T, label: string, update?: () => void): T { - Object.defineProperty(target, '_proxy', { value: {} }) + Object.defineProperty(target, '__proxyGetters__', { value: {} }) if (!update) { Object.defineProperty(target, '_diff', { @@ -19,20 +19,20 @@ function observeObject (target: T, label: string, update?: () return new Proxy(target as Observed, { get (target, key) { - if (key in target._proxy) return target._proxy[key] + if (key in target.__proxyGetters__) return target.__proxyGetters__[key] const value = target[key] if (!value || staticTypes.includes(typeof value) || typeof key === 'string' && key.startsWith('_')) return value const _update = update || (() => { const hasKey = key in target._diff - target._diff[key] = target._proxy[key] + target._diff[key] = target.__proxyGetters__[key] if (!hasKey && label) { showObserverLog(`[diff] ${label}: ${String(key)} (deep)`) } }) if (Array.isArray(value)) { - return target._proxy[key] = observeArray(value, label, _update) + return target.__proxyGetters__[key] = observeArray(value, label, _update) } else { - return target._proxy[key] = observeObject(value, label, _update) + return target.__proxyGetters__[key] = observeObject(value, label, _update) } }, set (target, key, value) { @@ -42,7 +42,7 @@ function observeObject (target: T, label: string, update?: () } else if (typeof key !== 'string' || !key.startsWith('_')) { const hasKey = key in target._diff target._diff[key] = value - delete target._proxy[key] + delete target.__proxyGetters__[key] if (!hasKey && label) { showObserverLog(`[diff] ${label}: ${String(key)}`) } @@ -95,9 +95,10 @@ function observeArray (target: T[], label: string, update: () => void) { export type Observed = T & { _diff: Partial - _proxy: Partial _update: () => R - _merge: (value: Partial) => Observed + _merge: (value: Partial) => Observed + __proxyGetters__: Partial + __updateCallback__: UpdateFunction } type UpdateFunction = (diff: Partial) => R @@ -120,7 +121,12 @@ export function observe (target: T, ...args: [(string | nu if (typeof args[0] === 'function') update = args.shift() as UpdateFunction if (typeof args[0] === 'string') label = args[0] - if (label && label in refs) return refs[label]._merge(target) + if (label && label in refs) { + refs[label].__updateCallback__ = update + return refs[label]._merge(target) + } + + Object.defineProperty(target, '__updateCallback__', { value: update, writable: true }) Object.defineProperty(target, '_update', { value (this: Observed) { @@ -129,7 +135,7 @@ export function observe (target: T, ...args: [(string | nu if (fields.length) { if (label) showObserverLog(`[update] ${label}: ${fields.join(', ')}`) this._diff = {} - return update(diff) + return this.__updateCallback__(diff) } }, }) @@ -139,7 +145,7 @@ export function observe (target: T, ...args: [(string | nu for (const key in value) { if (!(key in this._diff)) { target[key] = value[key] - delete this._proxy[key] + delete this.__proxyGetters__[key] } } return this diff --git a/packages/test-utils/src/database.ts b/packages/test-utils/src/database.ts index 9a54a3a536..5dae6f2afa 100644 --- a/packages/test-utils/src/database.ts +++ b/packages/test-utils/src/database.ts @@ -1,4 +1,4 @@ -import { App, DatabaseConfig, createUser, createGroup } from 'koishi-core' +import { App, DatabaseConfig, createUser, createGroup, GroupData, UserData } from 'koishi-core' import { createArray } from './utils' type TestHook = (app: App) => any @@ -95,6 +95,7 @@ export function testDatabase (config: DatabaseConfig, options: TestDatabaseOptio await expect(db.getUsers([48], ['id'])).resolves.toHaveLength(0) await expect(db.getUsers([49], ['id'])).resolves.toHaveLength(1) await expect(db.getUsers([1, 2, 3, 4])).resolves.toHaveLength(3) + await expect(db.getUsers([])).resolves.toHaveLength(0) }) test('observeUser update', async () => { @@ -108,6 +109,16 @@ export function testDatabase (config: DatabaseConfig, options: TestDatabaseOptio expect(user.id).toBe(id) expect(user.flag).toBe(flag) }) + + test('observeUser merge', async () => { + const user: UserData = { id: 1000, flag: 3, name: '', authority: 1, usage: {} } + const observedUser = await db.observeUser(user, 1) + expect(observedUser).toMatchObject(user) + observedUser.flag = 5 + await observedUser._update() + await expect(db.observeUser(observedUser)).resolves.toBe(observedUser) + await expect(db.getUser(user.id)).resolves.toMatchObject({ flag: 5 }) + }) }) describe('group operations', () => { @@ -184,6 +195,16 @@ export function testDatabase (config: DatabaseConfig, options: TestDatabaseOptio expect(group.id).toBe(id) expect(group.flag).toBe(flag) }) + + test('observeGroup merge', async () => { + const group: GroupData = { id: 5, flag: 3, assignee: 2 } + const observedGroup = await db.observeGroup(group, 1) + expect(observedGroup).toMatchObject(group) + observedGroup.flag = 5 + await observedGroup._update() + await expect(db.observeGroup(observedGroup)).resolves.toBe(observedGroup) + await expect(db.getGroup(group.id)).resolves.toMatchObject({ flag: 5 }) + }) }) return app