From 7bd3bd161d235be77546ca49bec5d0cd125839e7 Mon Sep 17 00:00:00 2001 From: v1rtl Date: Mon, 7 Nov 2022 21:26:38 +0200 Subject: [PATCH] add basic tests and simplify some code, make some api private --- .gitignore | 1 + egg.json | 6 +++++- request.ts | 6 ++---- server.ts | 17 ++++++++++++----- utils.ts | 12 ++++-------- utils_test.ts | 23 +++++++++++++++++++++++ 6 files changed, 47 insertions(+), 18 deletions(-) create mode 100644 .gitignore create mode 100644 utils_test.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2b4c3ab --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +coverage* \ No newline at end of file diff --git a/egg.json b/egg.json index b840232..84d07d9 100644 --- a/egg.json +++ b/egg.json @@ -8,7 +8,11 @@ "releaseType": null, "unstable": false, "unlisted": false, - "files": ["README.md", "*.ts", "LICENSE"], + "files": [ + "README.md", + "*.ts", + "LICENSE" + ], "ignore": [], "checkFormat": false, "checkTests": false, diff --git a/request.ts b/request.ts index 6efb7a3..c08951c 100644 --- a/request.ts +++ b/request.ts @@ -17,10 +17,8 @@ export function parseRequest(json: string): (JsonRpcRequest | 'invalid')[] | 'pa const res: (JsonRpcRequest | 'invalid')[] = [] for (const obj of arr) { - if (typeof obj !== 'object') res.push('invalid') - else if (!obj) res.push('invalid') - else if (obj.jsonrpc !== '2.0') res.push('invalid') - else if (typeof obj.method !== 'string') res.push('invalid') + if (typeof obj !== 'object' || !obj || obj.jsonrpc !== '2.0' || typeof obj.method !== 'string') + res.push('invalid') else res.push(obj) } diff --git a/server.ts b/server.ts index bc6c74c..34dd347 100644 --- a/server.ts +++ b/server.ts @@ -9,13 +9,13 @@ export class App { socks: Map methods: Map Promise> emitters: Map void, clientId: string) => void> - timeout: number + #timeout: number constructor(options: RPCOptions = { path: '/' }) { this.options = options this.socks = new Map() this.methods = new Map() this.emitters = new Map() - this.timeout = options.timeout || 1000 * 60 * 60 * 24 + this.#timeout = options.timeout || 1000 * 60 * 60 * 24 } /** * Upgrade a request to WebSocket and handle it @@ -42,11 +42,11 @@ export class App { this.socks.set(clientId, socket) // Close the socket after timeout - setTimeout(() => socket.close(), this.timeout) + setTimeout(() => socket.close(), this.#timeout) socket.onmessage = ({ data }) => { if (typeof data === 'string') { - send(socket, this.handleRPCMethod(clientId as string, data)) + send(socket, this.#handleRPCMethod(clientId as string, data)) } else if (data instanceof Uint8Array) { console.warn('Warn: an invalid jsonrpc message was sent. Skipping.') } @@ -80,7 +80,7 @@ export class App { * @param client client ID * @param data Received data */ - async handleRPCMethod(client: string, data: string) { + async #handleRPCMethod(client: string, data: string) { const sock = this.socks.get(client) if (!sock) return console.warn(`Warn: recieved a request from and undefined connection`) @@ -151,4 +151,11 @@ export class App { e.respondWith(this.handle(e.request)) } } + /** + * Close the server + */ + close() { + this.httpConn?.close() + this.listener?.close() + } } diff --git a/utils.ts b/utils.ts index 6ede730..69ce070 100644 --- a/utils.ts +++ b/utils.ts @@ -2,7 +2,7 @@ export const makeArray = (val: T | T[]) => (Array.isArray(val) ? val : [val]) export function makeEncryptor(key: string) { const textToChars = (text: string) => text.split('').map((c) => c.charCodeAt(0)) - const byteHex = (n: number) => ('0' + Number(n).toString(16)).substr(-2) + const byteHex = (n: number) => ('0' + Number(n).toString(16)).substring(-2) const applyKeyToChar = (code: number) => textToChars(key).reduce((a, b) => a ^ b, code) function decrypt(encoded: string) { @@ -29,14 +29,10 @@ export function lazyJSONParse(json: string): any { } export function delay(time: number) { - return new Promise((resolve) => { - setTimeout(() => resolve(), time) - }) + return new Promise((resolve) => void setTimeout(() => resolve(), time)) } -export function pathsAreEqual(actual: string, expected: string | undefined) { - if (expected === '*') return true - return actual === (expected || '/') -} +export const pathsAreEqual = (actual: string, expected?: string) => + expected === '*' ? true : actual === (expected || '/') export const paramsEncoder = makeEncryptor('nothing-secret') diff --git a/utils_test.ts b/utils_test.ts new file mode 100644 index 0000000..d03bdcf --- /dev/null +++ b/utils_test.ts @@ -0,0 +1,23 @@ +import { lazyJSONParse, pathsAreEqual } from './utils.ts' +import { assertEquals } from 'https://deno.land/std@0.162.0/testing/asserts.ts' + +Deno.test('lazyJSONParse', async (it) => { + await it.step('should parse JSON like JSON.parse', () => { + assertEquals(lazyJSONParse('{ "a": "b" }'), JSON.parse('{ "a": "b" }')) + }) + await it.step('should return an empty object on failed parse', () => { + assertEquals(lazyJSONParse('{ "a": "b"'), {}) + }) +}) + +Deno.test('pathsAreEqual', async (it) => { + await it.step('if expected path is asterisk, return true', () => { + assertEquals(pathsAreEqual('/hello', '*'), true) + }) + await it.step('should assert equal paths', () => { + assertEquals(pathsAreEqual('/hello', '/hello'), true) + }) + await it.step('if nothing is expected, default to "/"', () => { + assertEquals(pathsAreEqual('/'), true) + }) +})