Skip to content

Commit

Permalink
test: new middleware spec
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed Jan 14, 2020
1 parent d33781c commit 3e8b3e5
Showing 1 changed file with 80 additions and 113 deletions.
193 changes: 80 additions & 113 deletions packages/koishi-core/tests/middleware.spec.ts
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)
})
})

0 comments on commit 3e8b3e5

Please sign in to comment.