Skip to content

Commit

Permalink
v0.3.9: add test (#118)
Browse files Browse the repository at this point in the history
  • Loading branch information
taichunmin authored Jun 11, 2024
2 parents 1eaaaa7 + 8cbb1d9 commit a667e7e
Show file tree
Hide file tree
Showing 9 changed files with 228 additions and 89 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
"module": "./dist/index.mjs",
"name": "chameleon-ultra.js",
"type": "commonjs",
"version": "0.3.8",
"version": "0.3.9",
"bugs": {
"url": "https://github.com/taichunmin/chameleon-ultra.js/issues"
},
Expand Down
7 changes: 3 additions & 4 deletions src/ChameleonUltra.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _ from 'lodash'
import { Buffer } from '@taichunmin/buffer'
import { errToJson, middlewareCompose, sleep, type MiddlewareComposeFn, versionCompare } from './helper'
import { middlewareCompose, sleep, type MiddlewareComposeFn, versionCompare } from './helper'
import { EventAsyncGenerator } from './EventAsyncGenerator'
import { EventEmitter } from './EventEmitter'
import { type ReadableStream, type UnderlyingSink, type WritableStreamDefaultController, WritableStream } from 'node:stream/web'
Expand Down Expand Up @@ -212,7 +212,6 @@ export class ChameleonUltra {
} catch (err) {
err.message = `Failed to connect: ${err.message}`
this.emitter.emit('error', err)
this.#debug('error', `${err.message}\n${err.stack}`)
if (this.isConnected()) await this.disconnect(err)
throw err
}
Expand All @@ -227,7 +226,7 @@ export class ChameleonUltra {
try {
if (this.#isDisconnecting) return
this.#isDisconnecting = true // 避免重複執行
this.#debug('core', '%s %O', err.message, errToJson(err))
this.emitter.emit('error', err)
this.#debug('core', 'disconnecting...')
await this.invokeHook('disconnect', { err }, async (ctx, next) => {
try {
Expand Down Expand Up @@ -345,7 +344,7 @@ export class ChameleonUltra {
return resp
}
} catch (err) {
this.#debug('error', `${err.message}\n${err.stack}`)
this.emitter.emit('error', err)
throw err
}
}
Expand Down
76 changes: 76 additions & 0 deletions src/ResponseDecoder.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import * as sut from './ResponseDecoder'
import { Buffer } from '@taichunmin/buffer'

describe('is not buffer', () => {
test.each([
{ name: 'SlotInfo.fromCmd1019', fn: sut.SlotInfo.fromCmd1019 },
{ name: 'SlotFreqIsEnable.fromCmd1023', fn: sut.SlotFreqIsEnable.fromCmd1023 },
{ name: 'BatteryInfo.fromCmd1025', fn: sut.BatteryInfo.fromCmd1025 },
{ name: 'DeviceSettings.fromCmd1034', fn: sut.DeviceSettings.fromCmd1034 },
{ name: 'Hf14aAntiColl.fromCmd2000', fn: sut.Hf14aAntiColl.fromCmd2000 },
{ name: 'Mf1AcquireStaticNestedRes.fromCmd2003', fn: sut.Mf1AcquireStaticNestedRes.fromCmd2003 },
{ name: 'Mf1DarksideRes.fromCmd2004', fn: sut.Mf1DarksideRes.fromCmd2004 },
{ name: 'Mf1NtDistanceRes.fromCmd2005', fn: sut.Mf1NtDistanceRes.fromCmd2005 },
{ name: 'Mf1NestedRes.fromCmd2006', fn: sut.Mf1NestedRes.fromCmd2006 },
{ name: 'Mf1CheckKeysOfSectorsRes.fromCmd2012', fn: sut.Mf1CheckKeysOfSectorsRes.fromCmd2012 },
{ name: 'Mf1DetectionLog.fromBuffer', fn: sut.Mf1DetectionLog.fromBuffer },
{ name: 'Mf1DetectionLog.fromCmd4006', fn: sut.Mf1DetectionLog.fromCmd4006 },
{ name: 'Mf1EmuSettings.fromCmd4009', fn: sut.Mf1EmuSettings.fromCmd4009 },
])('$name should throw error', ({ name, fn }) => {
expect.hasAssertions()
try {
fn('not a buffer' as any)
} catch (err) {
expect(err).toBeInstanceOf(TypeError)
expect(err.message).toMatch(/should be a Buffer/)
}
})

test.each([
{ name: 'SlotInfo.fromCmd1019', fn: sut.SlotInfo.fromCmd1019 },
{ name: 'SlotFreqIsEnable.fromCmd1023', fn: sut.SlotFreqIsEnable.fromCmd1023 },
{ name: 'BatteryInfo.fromCmd1025', fn: sut.BatteryInfo.fromCmd1025 },
{ name: 'DeviceSettings.fromCmd1034', fn: sut.DeviceSettings.fromCmd1034 },
{ name: 'Mf1DarksideRes.fromCmd2004', fn: sut.Mf1DarksideRes.fromCmd2004 },
{ name: 'Mf1NtDistanceRes.fromCmd2005', fn: sut.Mf1NtDistanceRes.fromCmd2005 },
{ name: 'Mf1CheckKeysOfSectorsRes.fromCmd2012', fn: sut.Mf1CheckKeysOfSectorsRes.fromCmd2012 },
{ name: 'Mf1DetectionLog.fromBuffer', fn: sut.Mf1DetectionLog.fromBuffer },
])('$name should throw error', ({ name, fn }) => {
expect.hasAssertions()
try {
fn(new Buffer(0))
} catch (err) {
expect(err).toBeInstanceOf(TypeError)
expect(err.message).toMatch(/should be a Buffer/)
}
})
})

describe('Hf14aAntiColl', () => {
test('fromBuffer should throw invalid length error', () => {
expect.hasAssertions()
try {
sut.Hf14aAntiColl.fromBuffer(Buffer.from('01', 'hex'))
} catch (err) {
expect(err).toBeInstanceOf(Error)
expect(err.message).toMatch(/invalid length of/)
}
})

test('fromBuffer should throw invalid length error', () => {
expect.hasAssertions()
try {
sut.Hf14aAntiColl.fromBuffer(Buffer.from('040102030404000801', 'hex'))
} catch (err) {
expect(err).toBeInstanceOf(Error)
expect(err.message).toMatch(/invalid length of/)
}
})
})

describe('Mf1DarksideRes', () => {
test('fromBuffer should throw invalid length error', () => {
const actual = sut.Mf1DarksideRes.fromCmd2004(Buffer.from('01', 'hex'))
expect(actual).toMatchObject({ status: 1 })
})
})
2 changes: 1 addition & 1 deletion src/ResponseDecoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export class Hf14aAntiColl {
const uidLen = buf[0]
if (buf.length < uidLen + 4) throw new Error('invalid length of uid')
const atsLen = buf[uidLen + 4]
if (buf.length < uidLen + atsLen + 5) throw new Error('invalid invalid length of ats')
if (buf.length < uidLen + atsLen + 5) throw new Error('invalid length of ats')
return bufUnpackToClass(buf, `!${uidLen + 1}p2ss${atsLen + 1}p`, Hf14aAntiColl)
}

Expand Down
56 changes: 28 additions & 28 deletions src/helper.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import _ from 'lodash'
import * as sut from './helper'

test('sleep', async () => {
Expand All @@ -9,33 +8,6 @@ test('sleep', async () => {
expect(end - start).toBeGreaterThanOrEqual(90)
})

test('errToJson', async () => {
const err = _.merge(new Error('test'), { originalError: new Error('test') })
const actual = sut.errToJson(err)
expect(actual).toMatchObject({
name: 'Error',
message: 'test',
originalError: {
name: 'Error',
message: 'test',
},
})
})

test('jsonStringify', async () => {
const circular = { b: 1 }
const obj = {
circular1: circular,
circular2: circular,
map: new Map([['a', 1]]),
number: 1,
set: new Set([1, 2, 3]),
string: 'abc',
}
const actual = sut.jsonStringify(obj)
expect(actual).toBe('{"circular1":{"b":1},"circular2":"[Circular]","map":{"a":1},"number":1,"set":[1,2,3],"string":"abc"}')
})

describe('middlewareCompose', () => {
test('should have correct order', async () => {
const actual: number[] = []
Expand Down Expand Up @@ -326,6 +298,18 @@ describe('middlewareCompose', () => {

expect(actual).toBe(1)
})

test('should set default ctx', async () => {
const actual: number[] = []
await sut.middlewareCompose([
async (ctx, next) => {
actual.push(1)
await next()
},
])()

expect(actual).toEqual([1])
})
})

describe('versionCompare', () => {
Expand Down Expand Up @@ -353,4 +337,20 @@ describe('versionCompare', () => {
])('versionCompare(\'$str1\', \'$str2\') = $expected', ({ str1, str2, expected }) => {
expect(sut.versionCompare(str1, str2)).toBe(expected)
})

test('should throw error when version is invalid', () => {
expect.hasAssertions()
try {
sut.versionCompare('test', '1.0.0')
} catch (err) {
expect(err).toBeInstanceOf(Error)
expect(err.message).toMatch(/invalid version/)
}
try {
sut.versionCompare('1.0.0', 'test')
} catch (err) {
expect(err).toBeInstanceOf(Error)
expect(err.message).toMatch(/invalid version/)
}
})
})
55 changes: 2 additions & 53 deletions src/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ enum MiddlewareStatus {
ERROR,
}

export function middlewareCompose (middlewares: MiddlewareComposeFn[]): (ctx: Record<string, any>, next?: MiddlewareComposeFn) => Promise<unknown> {
export function middlewareCompose (middlewares: MiddlewareComposeFn[]): (ctx?: Record<string, any>, next?: MiddlewareComposeFn) => Promise<unknown> {
// 型態檢查
if (!_.isArray(middlewares)) throw new TypeError('Middleware stack must be an array!')
if (_.some(middlewares, fn => !_.isFunction(fn))) throw new TypeError('Middleware must be composed of functions!')
Expand Down Expand Up @@ -44,60 +44,9 @@ export async function sleep (ms: number): Promise<void> {
return new Promise(resolve => setTimeout(resolve, ms))
}

const ERROR_KEYS = [
'address',
'args',
'code',
'data',
'dest',
'errno',
'info',
'message',
'name',
'path',
'port',
'positions',
'reason',
'response.data',
'response.headers',
'response.status',
'source',
'stack',
'status',
'statusCode',
'statusMessage',
'syscall',
] as const

export function errToJson<T extends Error & { originalError?: any, stack?: any }> (err: T): Partial<T> {
const tmp: any = {
..._.pick(err, ERROR_KEYS),
...(_.isNil(err.originalError) ? {} : { originalError: errToJson(err.originalError) }),
}
return tmp
}

export function jsonStringify (obj: object, space?: number): string {
try {
const preventCircular = new Set()
return JSON.stringify(obj, function (this: any, key: string, value: any) {
if (Buffer.isBuffer(this[key])) return { type: 'Buffer', hex: this[key].toString('hex') }
if (value instanceof Map) return _.fromPairs([...value.entries()])
if (value instanceof Set) return [...value.values()]
if (_.isObject(value) && !_.isEmpty(value)) {
if (preventCircular.has(value)) return '[Circular]'
preventCircular.add(value)
}
return value
}, space)
} catch (err) {
return `[UnexpectedJSONParseError]: ${err.message as string}`
}
}

export function versionCompare (str1: string, str2: string): number {
const tmp = _.map([str1, str2], (str, idx) => {
const matched = _.trim(str).match(/^(?:(\d+)[.]?)?(?:(\d+)[.]?)?(?:(\d+)[.]?)?/)
const matched = _.trim(str).match(/^(?:(\d+)[.]?)(?:(\d+)[.]?)?(?:(\d+)[.]?)?/)
if (_.isNil(matched)) throw new Error(`invalid version: str${idx + 1} = ${str}`)
return _.map(matched.slice(1), v => _.isNil(v) ? 0 : _.toSafeInteger(v))
})
Expand Down
45 changes: 45 additions & 0 deletions src/plugin/Debug.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import _ from 'lodash'
import * as sut from './Debug'

test('errToJson', async () => {
const err = _.merge(new Error('test'), { originalError: new Error('test') })
const actual = sut.errToJson(err)
expect(actual).toMatchObject({
name: 'Error',
message: 'test',
originalError: {
name: 'Error',
message: 'test',
},
})
})

test('jsonStringify', async () => {
const circular = { b: 1 }
const obj = {
_censored: ['censored', 'censored1'],
bigint: 1n,
censored: 'test',
circular1: circular,
circular2: circular,
date: new Date('2000-01-01T00:00:00Z'),
error: new Error('test'),
map: new Map([['a', 1]]),
number: 1,
set: new Set([1, 2, 3]),
string: 'abc',
}
const actual = JSON.parse(sut.jsonStringify(obj))
expect(actual).toMatchObject({
bigint: '1',
censored: '[Censored]',
circular1: { b: 1 },
circular2: '[Circular]',
date: '2000-01-01T00:00:00.000Z',
error: { message: 'test', name: 'Error' },
map: { a: 1 },
number: 1,
set: [1, 2, 3],
string: 'abc',
})
})
Loading

0 comments on commit a667e7e

Please sign in to comment.