-
-
Notifications
You must be signed in to change notification settings - Fork 255
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
80 additions
and
113 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,150 +1,117 @@ | ||
import { MockedApp } from 'koishi-test-utils' | ||
import { errors, MessageMeta } from 'koishi-core' | ||
import { MockedApp, createArray } from 'koishi-test-utils' | ||
import { errors, Middleware, NextFunction } from 'koishi-core' | ||
import { sleep, noop } from 'koishi-utils' | ||
import { format } from 'util' | ||
|
||
let callSequence: jest.Mock[] | ||
const app = new MockedApp() | ||
|
||
const shared: MessageMeta = { | ||
postType: 'message', | ||
userId: 10000, | ||
selfId: 514, | ||
function wrap <T extends (...args: any[]) => any> (callback: T) { | ||
const wrapper = jest.fn(((...args: Parameters<T>) => { | ||
callSequence.push(wrapper) | ||
return callback(...args) as ReturnType<T> | ||
})) | ||
return wrapper | ||
} | ||
|
||
describe('Middleware API', () => { | ||
let flag: number | ||
|
||
beforeEach(() => flag = 0) | ||
|
||
beforeAll(() => { | ||
app.users.middleware((_, next) => { | ||
flag |= 1 << 1 | ||
return next() | ||
}) | ||
|
||
app.groups.middleware(({ message }, next) => { | ||
flag |= 1 << 2 | ||
if (message === 'foo') return | ||
if (message === 'bar') return next() | ||
return next(() => (flag |= 1 << 4, undefined)) | ||
}) | ||
beforeEach(() => { | ||
app._middlewares = [] | ||
callSequence = [] | ||
}) | ||
|
||
app.middleware(({ message }, next) => { | ||
flag |= 1 << 3 | ||
if (message === 'foo') return next() | ||
if (message === 'bar') return | ||
return next(next => (flag |= 1 << 5, next(() => (flag |= 1 << 6, undefined)))) | ||
}) | ||
describe('Middleware API', () => { | ||
const extraCalls = 7 | ||
|
||
app.prependMiddleware((_, next) => { | ||
flag |= 1 << 0 | ||
return next() | ||
}) | ||
test('max middlewares', async () => { | ||
const warnCallback = jest.fn() | ||
app.receiver.on('logger/warn', warnCallback) | ||
createArray(64 + extraCalls, () => app.middleware(noop)) | ||
expect(app._middlewares.length).toBe(64) | ||
expect(warnCallback).toBeCalledTimes(extraCalls) | ||
}) | ||
|
||
test('middleware-1', async () => { | ||
await app.receiveMessage('user', 'foo', 10000) | ||
expect(flag.toString(2).split('').reverse().join('')).toBe('1101') | ||
test('max prepended middlewares', () => { | ||
const warnCallback = jest.fn() | ||
app.receiver.on('logger/warn', warnCallback) | ||
createArray(64 + extraCalls, () => app.prependMiddleware(noop)) | ||
expect(app._middlewares.length).toBe(64) | ||
expect(warnCallback).toBeCalledTimes(extraCalls) | ||
}) | ||
|
||
test('middleware-2', async () => { | ||
await app.receiveMessage('group', 'bar', 10000, 20000) | ||
expect(flag.toString(2).split('').reverse().join('')).toBe('1011') | ||
test('remove middlewares', () => { | ||
app.middleware(noop) | ||
expect(app._middlewares.length).toBe(1) | ||
expect(app.removeMiddleware(noop)).toBeTruthy() | ||
expect(app._middlewares.length).toBe(0) | ||
expect(app.removeMiddleware(noop)).toBeFalsy() | ||
expect(app._middlewares.length).toBe(0) | ||
}) | ||
}) | ||
|
||
test('middleware-3', async () => { | ||
await app.receiveMessage('user', 'baz', 10000) | ||
expect(flag.toString(2).split('').reverse().join('')).toBe('1101011') | ||
describe('Middleware Runtime', () => { | ||
test('run asynchronously', async () => { | ||
const mid1 = wrap<Middleware>((_, next) => sleep(0).then(() => next())) | ||
const mid2 = wrap<Middleware>((_, next) => next()) | ||
app.middleware(mid1) | ||
app.middleware(mid2) | ||
await app.receiveMessage('user', 'foo', 123) | ||
expect(callSequence).toEqual([mid1, mid2]) | ||
}) | ||
|
||
test('middleware-4', async () => { | ||
await app.receiveMessage('group', 'baz', 10000, 20000) | ||
expect(flag.toString(2).split('').reverse().join('')).toBe('10111') | ||
test('stop when no next is called', async () => { | ||
const mid1 = wrap<Middleware>(noop) | ||
const mid2 = wrap<Middleware>((_, next) => next()) | ||
app.middleware(mid1) | ||
app.middleware(mid2) | ||
expect(callSequence).toEqual([]) | ||
await app.receiveMessage('user', 'foo', 123) | ||
expect(callSequence).toEqual([mid1]) | ||
}) | ||
}) | ||
|
||
describe('runtime checks', () => { | ||
beforeEach(() => { | ||
// @ts-ignore remove all middlewares | ||
app._middlewares = [[app, app._preprocess]] | ||
test('prepend middleware', async () => { | ||
const mid1 = wrap<Middleware>((_, next) => next()) | ||
const mid2 = wrap<Middleware>((_, next) => next()) | ||
const mid3 = wrap<Middleware>((_, next) => next()) | ||
app.middleware(mid1) | ||
app.prependMiddleware(mid2) | ||
app.prependMiddleware(mid3) | ||
await app.receiveMessage('user', 'foo', 123) | ||
expect(callSequence).toEqual([mid3, mid2, mid1]) | ||
}) | ||
|
||
test('isolated next function', async () => { | ||
const errorCallback = jest.fn() | ||
const middlewareErrorCallback = jest.fn() | ||
app.receiver.on('error', error => errorCallback(error.message)) | ||
app.receiver.on('error/middleware', error => middlewareErrorCallback(error.message)) | ||
|
||
app.middleware(async (_, next) => { | ||
next() | ||
}) | ||
|
||
app.middleware(async (_, next) => { | ||
await sleep(0) | ||
next() | ||
}) | ||
|
||
await app.receiveMessage('group', 'bar', 10000, 20000) | ||
|
||
expect(errorCallback).toBeCalledTimes(1) | ||
expect(errorCallback).toBeCalledWith(errors.ISOLATED_NEXT) | ||
expect(middlewareErrorCallback).toBeCalledTimes(0) | ||
test('temporary middleware', async () => { | ||
const mid1 = wrap<Middleware>((_, next) => next(mid3)) | ||
const mid2 = wrap<Middleware>((_, next) => next(mid4)) | ||
const mid3 = wrap<NextFunction>((next) => next(mid5)) | ||
const mid4 = wrap<NextFunction>((next) => next()) | ||
const mid5 = wrap<NextFunction>((next) => next()) | ||
app.middleware(mid1) | ||
app.middleware(mid2) | ||
await app.receiveMessage('user', 'foo', 123) | ||
expect(callSequence).toEqual([mid1, mid2, mid3, mid4, mid5]) | ||
}) | ||
|
||
test('middleware error', async () => { | ||
const errorCallback = jest.fn() | ||
const middlewareErrorCallback = jest.fn() | ||
app.receiver.on('error', error => errorCallback(error.message)) | ||
app.receiver.on('error/middleware', error => middlewareErrorCallback(error.message)) | ||
|
||
const errorMessage = 'error message' | ||
app.middleware(() => { | ||
throw new Error(errorMessage) | ||
}) | ||
|
||
await app.receiveMessage('group', 'bar', 10000, 20000) | ||
|
||
app.middleware(() => { throw new Error(errorMessage) }) | ||
await app.receiveMessage('user', 'foo', 123) | ||
expect(errorCallback).toBeCalledTimes(1) | ||
expect(errorCallback).toBeCalledWith(errorMessage) | ||
expect(middlewareErrorCallback).toBeCalledTimes(1) | ||
expect(middlewareErrorCallback).toBeCalledWith(errorMessage) | ||
}) | ||
|
||
test('max middlewares', () => { | ||
const mock = jest.fn() | ||
app.receiver.on('error', mock) | ||
|
||
const extraCalls = 7 | ||
for (let index = 0; index < 63 + extraCalls; ++index) { | ||
app.middleware(noop) | ||
} | ||
|
||
expect(app._middlewares.length).toBe(64) | ||
expect(mock).toBeCalledTimes(extraCalls) | ||
expect(mock.mock.calls[0][0]).toHaveProperty('message', format(errors.MAX_MIDDLEWARES, 64)) | ||
}) | ||
|
||
test('max prepended middlewares', () => { | ||
const mock = jest.fn() | ||
app.receiver.on('error', mock) | ||
|
||
const extraCalls = 7 | ||
for (let index = 0; index < 63 + extraCalls; ++index) { | ||
app.prependMiddleware(noop) | ||
} | ||
|
||
expect(app._middlewares.length).toBe(64) | ||
expect(mock).toBeCalledTimes(extraCalls) | ||
expect(mock.mock.calls[0][0]).toHaveProperty('message', format(errors.MAX_MIDDLEWARES, 64)) | ||
}) | ||
|
||
test('remove middlewares', () => { | ||
const fn = () => {} | ||
app.middleware(fn) | ||
expect(app._middlewares.length).toBe(2) | ||
expect(app.removeMiddleware(fn)).toBeTruthy() | ||
expect(app._middlewares.length).toBe(1) | ||
expect(app.removeMiddleware(fn)).toBeFalsy() | ||
expect(app._middlewares.length).toBe(1) | ||
test('isolated next function', async () => { | ||
const warnCallback = jest.fn() | ||
app.receiver.on('logger/warn', warnCallback) | ||
app.middleware((_, next) => (next(), undefined)) | ||
app.middleware((_, next) => sleep(0).then(() => next())) | ||
await app.receiveMessage('user', 'foo', 123) | ||
await sleep(0) | ||
expect(warnCallback).toBeCalledTimes(1) | ||
}) | ||
}) |