From 9290e53b32bf355eaac7d5243391db321bdf7642 Mon Sep 17 00:00:00 2001 From: vasyl Date: Tue, 5 Aug 2025 17:20:33 +1000 Subject: [PATCH 01/18] implement the client side timeout handling, upgrade some deps --- package.json | 10 ++--- src/client/AllserverClient.js | 38 ++++++++++++++++--- test/client/AllserverClient.test.js | 12 ++++++ .../allserver_integration_test.proto | 1 + test/integration/integration.test.js | 28 ++++++++------ 5 files changed, 67 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 8a0a282..3203574 100644 --- a/package.json +++ b/package.json @@ -62,17 +62,17 @@ } }, "devDependencies": { - "@grpc/grpc-js": "^1.1.7", + "@grpc/grpc-js": "^1.13.4", "@grpc/proto-loader": "^0.7.5", - "bullmq": "^3.10.1", + "bullmq": "^3.16.2", "cls-hooked": "^4.2.2", "eslint": "^8.55.0", - "express": "^4.18.2", + "express": "^4.21.2", "lambda-local": "^1.7.3", "micro": "^10.0.1", - "mocha": "^10.2.0", + "mocha": "^11.7.1", "node-fetch": "^2.6.9", - "nyc": "^15.1.0", + "nyc": "^17.1.0", "prettier": "^2.1.1" }, "dependencies": { diff --git a/src/client/AllserverClient.js b/src/client/AllserverClient.js index b0d767d..59fcf62 100644 --- a/src/client/AllserverClient.js +++ b/src/client/AllserverClient.js @@ -141,6 +141,8 @@ module.exports = require("stampit")({ [p]: { // The protocol implementation strategy. transport: null, + // The maximum time to wait until returning the ALLSERVER_CLIENT_TIMEOUT error. 0 means - no timeout. + timeout: 0, // Disable any exception throwing when calling any methods. Otherwise, throws network and server errors. neverThrow: true, // Automatically find (introspect) and call corresponding remote procedures. Use only the methods defined in client side. @@ -173,6 +175,7 @@ module.exports = require("stampit")({ { uri, transport, + timeout, neverThrow, dynamicMethods, autoIntrospect, @@ -183,6 +186,7 @@ module.exports = require("stampit")({ }, { stamp } ) { + this[p].timeout = timeout != null ? timeout : this[p].timeout; this[p].neverThrow = neverThrow != null ? neverThrow : this[p].neverThrow; this[p].dynamicMethods = dynamicMethods != null ? dynamicMethods : this[p].dynamicMethods; this[p].autoIntrospect = autoIntrospect != null ? autoIntrospect : this[p].autoIntrospect; @@ -218,10 +222,10 @@ module.exports = require("stampit")({ try { // This is supposed to be executed only once (per uri) unless it throws. // There are only 3 situations when this throws: - // * the "introspect" method not found on server, - // * the network request is malformed, + // * the "introspect" method not found on server, (GRPC) + // * the network request is malformed, (HTTP-like) // * couldn't connect to the remote host. - ctx.result = await transport.introspect(ctx); + ctx.result = await this._callTransport(ctx); } catch (err) { ctx.result = { success: false, @@ -281,19 +285,41 @@ module.exports = require("stampit")({ return await runMiddlewares(middlewares); }, + _callTransport(ctx) { + const transportMethod = ctx.isIntrospection ? "introspect" : "call"; + // In JavaScript if the `timeout` is null or undefined or some other object this condition will return `false` + if (this[p].timeout > 0) { + return Promise.race([ + this[p].transport[transportMethod](ctx), + new Promise((resolve) => + setTimeout( + () => + resolve({ + success: false, + code: "ALLSERVER_CLIENT_TIMEOUT", + message: `The remote procedure ${ctx.procedureName} timed out in ${this[p].timeout} ms`, + }), + this[p].timeout + ) + ), + ]); + } + + return this[p].transport[transportMethod](ctx); + }, + async call(procedureName, arg) { if (!arg) arg = {}; if (!arg._) arg._ = {}; arg._.procedureName = procedureName; - const transport = this[p].transport; const defaultCtx = { procedureName, arg, client: this }; - const ctx = transport.createCallContext(defaultCtx); + const ctx = this[p].transport.createCallContext(defaultCtx); await this._callMiddlewares(ctx, "before", async () => { if (!ctx.result) { try { - ctx.result = await transport.call(ctx); + ctx.result = await this._callTransport(ctx); } catch (err) { if (!this[p].neverThrow) throw err; diff --git a/test/client/AllserverClient.test.js b/test/client/AllserverClient.test.js index 359f8fa..af35b00 100644 --- a/test/client/AllserverClient.test.js +++ b/test/client/AllserverClient.test.js @@ -211,6 +211,18 @@ describe("AllserverClient", () => { assert.strictEqual(result.message, "Couldn't reach remote procedure foo due to: Shit happens too"); assert.strictEqual(result.error.message, "Shit happens too"); }); + + it("should return ALLSERVER_CLIENT_TIMEOUT code on timeouts", async () => { + const MockedTransport = VoidClientTransport.methods({ + call: () => new Promise(() => {}), // never resolving or rejecting promise + }); + + const client = AllserverClient({ transport: MockedTransport(), timeout: 1 }); // 1 ms timeout for fast unit tests + const result = await client.call("doesnt_matter"); + assert.strictEqual(result.success, false); + assert.strictEqual(result.code, "ALLSERVER_CLIENT_TIMEOUT"); + assert.strictEqual(result.message, "The remote procedure doesnt_matter timed out in 1 ms"); + }); }); describe("dynamicMethods", () => { diff --git a/test/integration/allserver_integration_test.proto b/test/integration/allserver_integration_test.proto index 7f46386..644519c 100644 --- a/test/integration/allserver_integration_test.proto +++ b/test/integration/allserver_integration_test.proto @@ -21,6 +21,7 @@ service MyService { rpc gate(GateRequest) returns (GateReply) {} rpc throws(Empty) returns (Reply) {} rpc throwsBadArgs(Empty) returns (Reply) {} + rpc forcedTimeout(Empty) returns (Reply) {} } message HelloRequest { diff --git a/test/integration/integration.test.js b/test/integration/integration.test.js index 2e35416..312b0db 100644 --- a/test/integration/integration.test.js +++ b/test/integration/integration.test.js @@ -27,6 +27,9 @@ const procedures = { // call database ... return { success: true, code: "CREATED", user: { id: String(Math.random()).substr(2), firstName, lastName } }; }, + forcedTimeout() { + return new Promise((resolve) => setTimeout(resolve, 2)); // resolve in 2ms, which is longer than the timeout of 1ms + }, }; async function callClientMethods(client) { @@ -77,6 +80,13 @@ async function callClientMethods(client) { assert.strictEqual(response.success, false); assert.strictEqual(response.code, "ALLSERVER_CLIENT_PROCEDURE_NOT_FOUND"); assert.strictEqual(response.message, "Procedure 'unexist' not found via introspection"); + + client[Symbol.for("AllserverClient")].timeout = 1; + response = await client.forcedTimeout({}); + assert.strictEqual(response.success, false); + assert.strictEqual(response.code, "ALLSERVER_CLIENT_TIMEOUT"); + assert.strictEqual(response.message, "The remote procedure forcedTimeout timed out in 1 ms"); + client[Symbol.for("AllserverClient")].timeout = 0; } let { @@ -92,7 +102,7 @@ let { GrpcClientTransport, LambdaClientTransport, } = require("../.."); -Allserver = Allserver.props({ logger: { error() {} } }); // silence the servers +Allserver = Allserver.props({ logger: { error() {} } }); // silence the servers console because we test error cases here describe("integration", function () { this.timeout(5000); // Needed for CI on Windows. @@ -132,7 +142,7 @@ describe("integration", function () { assert.strictEqual(response.code, "ALLSERVER_PROCEDURE_ERROR"); assert.strictEqual(response.error.status, 500); - await httpServer.stop(); + httpServer.stop(); }); it("should behave with node-fetch", async () => { @@ -168,7 +178,7 @@ describe("integration", function () { const httpClient = AllserverClient({ uri: "http://localhost:40001" }); await callClientMethods(httpClient); - await httpServer.stop(); + httpServer.stop(); }); }); @@ -211,10 +221,8 @@ describe("integration", function () { assert.strictEqual(response.code, "ALLSERVER_PROCEDURE_ERROR"); assert.strictEqual(response.error.status, 500); - await new Promise((r) => { - if (server.closeIdleConnections) server.closeIdleConnections(); - server.close(r); - }); + if (server.closeIdleConnections) server.closeIdleConnections(); + server.close(); }); it("should behave with node-fetch", async () => { @@ -256,10 +264,8 @@ describe("integration", function () { const httpClient = AllserverClient({ uri: "http://localhost:40003/express/allsever" }); await callClientMethods(httpClient); - await new Promise((r) => { - if (server.closeIdleConnections) server.closeIdleConnections(); - server.close(r); - }); + if (server.closeIdleConnections) server.closeIdleConnections(); + server.close(); }); }); From 715d050a043f2fdbc0647784d8550963eb49ed66 Mon Sep 17 00:00:00 2001 From: vasyl Date: Tue, 5 Aug 2025 17:33:31 +1000 Subject: [PATCH 02/18] upgrade a few more dependencies --- package.json | 12 ++++++------ src/server/GrpcTransport.js | 2 -- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 3203574..043f3b5 100644 --- a/package.json +++ b/package.json @@ -34,9 +34,9 @@ "author": "Vasyl Boroviak", "license": "MIT", "peerDependencies": { - "@grpc/grpc-js": "^1.1.7", - "@grpc/proto-loader": "^0.7.5", - "bullmq": "^3.10.1", + "@grpc/grpc-js": "^1.13.4", + "@grpc/proto-loader": "^0.8.0", + "bullmq": "^5.56.9", "express": "^4.18.2", "micro": "^10.0.1", "node-fetch": "^2.6.9" @@ -63,10 +63,10 @@ }, "devDependencies": { "@grpc/grpc-js": "^1.13.4", - "@grpc/proto-loader": "^0.7.5", - "bullmq": "^3.16.2", + "@grpc/proto-loader": "^0.8.0", + "bullmq": "^5.56.9", "cls-hooked": "^4.2.2", - "eslint": "^8.55.0", + "eslint": "^8.57.1", "express": "^4.21.2", "lambda-local": "^1.7.3", "micro": "^10.0.1", diff --git a/src/server/GrpcTransport.js b/src/server/GrpcTransport.js index 04335c3..b71943a 100644 --- a/src/server/GrpcTransport.js +++ b/src/server/GrpcTransport.js @@ -81,8 +81,6 @@ module.exports = require("./Transport").compose({ err ? reject(err) : resolve(result) ); }); - - return this.server.start(); }, stopServer() { return new Promise((r) => this.server.tryShutdown(r)); From 12789582ca717176ac308bc293694e8498046c64 Mon Sep 17 00:00:00 2001 From: vasyl Date: Tue, 5 Aug 2025 17:39:15 +1000 Subject: [PATCH 03/18] more relaxed peerDeps --- package.json | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/package.json b/package.json index 043f3b5..6674010 100644 --- a/package.json +++ b/package.json @@ -34,12 +34,12 @@ "author": "Vasyl Boroviak", "license": "MIT", "peerDependencies": { - "@grpc/grpc-js": "^1.13.4", - "@grpc/proto-loader": "^0.8.0", - "bullmq": "^5.56.9", - "express": "^4.18.2", - "micro": "^10.0.1", - "node-fetch": "^2.6.9" + "@grpc/grpc-js": "1", + "@grpc/proto-loader": "0", + "bullmq": "3 - 5", + "express": "4", + "micro": "10", + "node-fetch": "2" }, "peerDependenciesMeta": { "@grpc/grpc-js": { From 2c4e2326fcf11075670f1e2be36aa8bd088ba0be Mon Sep 17 00:00:00 2001 From: vasyl Date: Tue, 12 Aug 2025 19:56:01 +1000 Subject: [PATCH 04/18] Utilise new Node.js graceful shutdown server.closeAllConnections() for HTTP transport --- src/server/HttpTransport.js | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/HttpTransport.js b/src/server/HttpTransport.js index 5c4387f..aec5cfb 100644 --- a/src/server/HttpTransport.js +++ b/src/server/HttpTransport.js @@ -60,6 +60,7 @@ module.exports = require("./Transport").compose({ return new Promise((r) => { if (this.server.closeIdleConnections) this.server.closeIdleConnections(); this.server.close(r); + if (this.server.closeAllConnections) this.server.closeAllConnections(); }); }, From ad56a2e8f1c167a60ff38be685685028d55987f5 Mon Sep 17 00:00:00 2001 From: vasyl Date: Tue, 12 Aug 2025 19:56:53 +1000 Subject: [PATCH 05/18] 2.6.0 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 6674010..ec4b8f1 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "allserver", - "version": "2.5.0", + "version": "2.6.0", "description": "Multi-protocol simple RPC server and [optional] client. Boilerplate-less. Opinionated. Minimalistic. DX-first.", "main": "src/index.js", "scripts": { From f4d48046d18ddad2eb912a4e8465522a1874bf1d Mon Sep 17 00:00:00 2001 From: vasyl Date: Thu, 30 Oct 2025 16:15:36 +1100 Subject: [PATCH 06/18] relax Express.js dependency --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ec4b8f1..d025d4f 100644 --- a/package.json +++ b/package.json @@ -37,7 +37,7 @@ "@grpc/grpc-js": "1", "@grpc/proto-loader": "0", "bullmq": "3 - 5", - "express": "4", + "express": "4 - 6", "micro": "10", "node-fetch": "2" }, From 3b71221a5e285aa88d5c59380ca7d537fe5cb53a Mon Sep 17 00:00:00 2001 From: vasyl Date: Thu, 30 Oct 2025 16:16:05 +1100 Subject: [PATCH 07/18] Remove Lambda Transport callArg+callContext legacy properties --- src/client/LambdaClientTransport.js | 15 ++------------- src/server/LambdaTransport.js | 14 +++----------- 2 files changed, 5 insertions(+), 24 deletions(-) diff --git a/src/client/LambdaClientTransport.js b/src/client/LambdaClientTransport.js index 02e6fe3..bb98ba6 100644 --- a/src/client/LambdaClientTransport.js +++ b/src/client/LambdaClientTransport.js @@ -31,13 +31,7 @@ module.exports = require("./ClientTransport").compose({ async call(ctx) { let promise = this.awsSdkLambdaClient.invoke({ FunctionName: this.uri.substring("lambda://".length), - // TODO: change to this during the next major release: - // Payload: JSON.stringify(ctx.arg), - Payload: JSON.stringify({ - callContext: { ...ctx.lambda.callContext, procedureName: ctx.procedureName }, - callArg: ctx.lambda.callArg, - ...ctx.arg, - }), + Payload: JSON.stringify(ctx.arg), }); if (typeof promise.promise === "function") promise = promise.promise(); // AWS SDK v2 adoption const invocationResponse = await promise; @@ -47,12 +41,7 @@ module.exports = require("./ClientTransport").compose({ createCallContext(defaultCtx) { return { ...defaultCtx, - // TODO: change to this during the next major release: - // lambda: {} - lambda: { - callContext: {}, - callArg: defaultCtx.arg, - }, + lambda: {}, }; }, }, diff --git a/src/server/LambdaTransport.js b/src/server/LambdaTransport.js index fbd5790..fa1ee06 100644 --- a/src/server/LambdaTransport.js +++ b/src/server/LambdaTransport.js @@ -21,9 +21,7 @@ module.exports = require("./Transport").compose({ return false; } } else { - // TODO: change to this during the next major release: - // ctx.arg = ctx.lambda.invoke || {}; - ctx.arg = ctx.lambda.invoke.callArg || ctx.lambda.invoke || {}; + ctx.arg = ctx.lambda.invoke || {}; return true; } }, @@ -56,11 +54,7 @@ module.exports = require("./Transport").compose({ headers: event?.headers, }; } else { - // TODO: change to this during the next major release: - // lambda.invoke = event || {}; - lambda.invoke = event?.callArg - ? { callContext: event?.callContext, callArg: event?.callArg } - : event || {}; + lambda.invoke = event || {}; } this._handleRequest({ ...defaultCtx, lambda }); @@ -70,9 +64,7 @@ module.exports = require("./Transport").compose({ getProcedureName(ctx) { const { isHttp, http, invoke } = ctx.lambda; - // TODO: change to this during the next major release: - // return (isHttp ? http.path.substr(1) : invoke._?.procedureName) || ""; // if no procedureName then it's an introspection - return isHttp ? http.path.substr(1) : invoke.callContext?.procedureName || invoke._?.procedureName || ""; // if no procedureName then it's an introspection + return (isHttp ? http.path.substr(1) : invoke._?.procedureName) || ""; // if no procedureName then it's an introspection }, isIntrospection(ctx) { From ea49ee09fedb40c1b70d1370de5da137c9258133 Mon Sep 17 00:00:00 2001 From: vasyl Date: Thu, 30 Oct 2025 16:19:42 +1100 Subject: [PATCH 08/18] Drop Node 14, 16, 18 support --- .github/workflows/ci.yml | 4 ++-- .nvmrc | 2 +- src/client/HttpClientTransport.js | 2 +- src/server/HttpTransport.js | 2 +- src/server/LambdaTransport.js | 2 +- test/integration/LocalLambdaClientTransport.js | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4f28c87..a04b1d3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -7,7 +7,7 @@ jobs: strategy: matrix: os-version: [ubuntu-latest, macos-latest] - node-version: ["14", "16", "18"] + node-version: ["20", "22", "24"] redis-version: [6] steps: @@ -38,7 +38,7 @@ jobs: strategy: matrix: os-version: [windows-latest] - node-version: ["14", "16", "18"] + node-version: ["20", "22", "24"] steps: - name: Git checkout diff --git a/.nvmrc b/.nvmrc index 3c03207..209e3ef 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -18 +20 diff --git a/src/client/HttpClientTransport.js b/src/client/HttpClientTransport.js index 5c3c32d..dc62f36 100644 --- a/src/client/HttpClientTransport.js +++ b/src/client/HttpClientTransport.js @@ -51,7 +51,7 @@ module.exports = require("./ClientTransport").compose({ const json = JSON.parse(text); error = new Error((json && json.message) || text); if (json && json.code) error.code = json.code; - } catch (err) { + } catch { // ignoring. Not a JSON } } diff --git a/src/server/HttpTransport.js b/src/server/HttpTransport.js index aec5cfb..6d989e0 100644 --- a/src/server/HttpTransport.js +++ b/src/server/HttpTransport.js @@ -22,7 +22,7 @@ module.exports = require("./Transport").compose({ if (bodyBuffer.length !== 0) arg = await this.micro.json(ctx.http.req); ctx.arg = arg; return true; - } catch (err) { + } catch { return false; } }, diff --git a/src/server/LambdaTransport.js b/src/server/LambdaTransport.js index fa1ee06..ee7568e 100644 --- a/src/server/LambdaTransport.js +++ b/src/server/LambdaTransport.js @@ -17,7 +17,7 @@ module.exports = require("./Transport").compose({ } } return true; - } catch (err) { + } catch { return false; } } else { diff --git a/test/integration/LocalLambdaClientTransport.js b/test/integration/LocalLambdaClientTransport.js index 6eee133..70bb1b6 100644 --- a/test/integration/LocalLambdaClientTransport.js +++ b/test/integration/LocalLambdaClientTransport.js @@ -40,7 +40,7 @@ module.exports = require("../../ClientTransport").compose({ const message = (json && json.message) || text; error = new Error(message); if (json && json.code) error.code = json.code; - } catch (err) { + } catch { // ignoring. Not a JSON } } From 79c6fa70f5a7dec830a8f8be34ea0ba4ad0c5ab1 Mon Sep 17 00:00:00 2001 From: vasyl Date: Thu, 30 Oct 2025 16:25:38 +1100 Subject: [PATCH 09/18] Replace eslint with oxlint --- package.json | 15 ++------------- src/client/HttpClientTransport.js | 1 - 2 files changed, 2 insertions(+), 14 deletions(-) diff --git a/package.json b/package.json index d025d4f..5d00f28 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "Multi-protocol simple RPC server and [optional] client. Boilerplate-less. Opinionated. Minimalistic. DX-first.", "main": "src/index.js", "scripts": { - "lint": "eslint ./", + "lint": "oxlint ./", "test": "mocha", "cov": "nyc --reporter=html npm run test" }, @@ -66,29 +66,18 @@ "@grpc/proto-loader": "^0.8.0", "bullmq": "^5.56.9", "cls-hooked": "^4.2.2", - "eslint": "^8.57.1", "express": "^4.21.2", "lambda-local": "^1.7.3", "micro": "^10.0.1", "mocha": "^11.7.1", "node-fetch": "^2.6.9", "nyc": "^17.1.0", + "oxlint": "^1.25.0", "prettier": "^2.1.1" }, "dependencies": { "stampit": "^4.3.1" }, - "eslintConfig": { - "parserOptions": { - "ecmaVersion": 2022 - }, - "env": { - "es6": true, - "node": true, - "mocha": true - }, - "extends": "eslint:recommended" - }, "mocha": { "recursive": true, "exit": true diff --git a/src/client/HttpClientTransport.js b/src/client/HttpClientTransport.js index dc62f36..453c922 100644 --- a/src/client/HttpClientTransport.js +++ b/src/client/HttpClientTransport.js @@ -4,7 +4,6 @@ module.exports = require("./ClientTransport").compose({ name: "HttpClientTransport", props: { - // eslint-disable-next-line no-undef fetch: (typeof globalThis !== "undefined" && globalThis.fetch) || require("node-fetch"), headers: { "Content-Type": "application/json; charset=utf-8", From b261fe71bf9040c5cc377fdcf1b9a423b03ff396 Mon Sep 17 00:00:00 2001 From: vasyl Date: Thu, 30 Oct 2025 16:29:39 +1100 Subject: [PATCH 10/18] Fix tests after Node upgrade --- test/client/AllserverClient.test.js | 2 +- test/integration/integration.test.js | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/test/client/AllserverClient.test.js b/test/client/AllserverClient.test.js index af35b00..b3a1939 100644 --- a/test/client/AllserverClient.test.js +++ b/test/client/AllserverClient.test.js @@ -130,7 +130,7 @@ describe("AllserverClient", () => { assert.strictEqual(result.success, false); assert.strictEqual(result.code, "ALLSERVER_CLIENT_MALFORMED_INTROSPECTION"); assert.strictEqual(result.message, "Malformed introspection from void://localhost"); - assert.strictEqual(result.error.message, "Unexpected token b in JSON at position 0"); + assert(result.error.message.includes("Unexpected token")); }); it("should not throw if underlying transport returns introspection in a wrong format", async () => { diff --git a/test/integration/integration.test.js b/test/integration/integration.test.js index 312b0db..ff07362 100644 --- a/test/integration/integration.test.js +++ b/test/integration/integration.test.js @@ -118,7 +118,7 @@ describe("integration", function () { assert.strictEqual(response.success, false); assert.strictEqual(response.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); assert.strictEqual(response.message, "Couldn't reach remote procedure: sayHello"); - if (response.error.cause) assert(response.error.cause.message.includes("ECONNREFUSED")); + if (response.error.cause) assert.strictEqual(response.error.cause.code, "ECONNREFUSED"); else assert(response.error.message.includes("ECONNREFUSED")); const httpServer = Allserver({ procedures, transport: HttpTransport({ port: 40000 }) }); @@ -194,7 +194,7 @@ describe("integration", function () { assert.strictEqual(response.success, false); assert.strictEqual(response.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); assert.strictEqual(response.message, "Couldn't reach remote procedure: sayHello"); - if (response.error.cause) assert(response.error.cause.message.includes("ECONNREFUSED")); + if (response.error.cause) assert.strictEqual(response.error.cause.code, "ECONNREFUSED"); else assert(response.error.message.includes("ECONNREFUSED")); const app = express(); @@ -404,7 +404,7 @@ describe("integration", function () { assert.strictEqual(response.success, false); assert.strictEqual(response.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); assert.strictEqual(response.message, "Couldn't reach remote procedure: sayHello"); - assert(response.error.message.includes("ECONNREFUSED")); + assert.strictEqual(response.error.code, "ECONNREFUSED"); if (process.platform === "win32") return; From 8eba103d2f243c7647cded93c0b582b3da08364b Mon Sep 17 00:00:00 2001 From: vasyl Date: Thu, 30 Oct 2025 16:31:50 +1100 Subject: [PATCH 11/18] Use new node native module import --- src/client/GrpcClientTransport.js | 2 +- src/server/Allserver.js | 2 +- src/server/ExpressTransport.js | 2 +- src/server/GrpcTransport.js | 2 +- src/server/HttpTransport.js | 4 ++-- test/client/AllserverClient.test.js | 2 +- test/client/HttpClientTransport.js | 2 +- test/integration/integration.test.js | 4 ++-- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/client/GrpcClientTransport.js b/src/client/GrpcClientTransport.js index 4d7389e..ff47a8f 100644 --- a/src/client/GrpcClientTransport.js +++ b/src/client/GrpcClientTransport.js @@ -4,7 +4,7 @@ module.exports = require("./ClientTransport").compose({ name: "GrpcClientTransport", props: { - _fs: require("fs"), + _fs: require("node:fs"), _grpc: require("@grpc/grpc-js"), _protoLoader: require("@grpc/proto-loader"), _grpcClientForIntrospection: null, diff --git a/src/server/Allserver.js b/src/server/Allserver.js index 0541e3c..2eea087 100644 --- a/src/server/Allserver.js +++ b/src/server/Allserver.js @@ -1,4 +1,4 @@ -const assert = require("assert"); +const assert = require("node:assert"); const { isObject, isBoolean, isFunction, uniq } = require("../util"); diff --git a/src/server/ExpressTransport.js b/src/server/ExpressTransport.js index b24ff56..2ad000e 100644 --- a/src/server/ExpressTransport.js +++ b/src/server/ExpressTransport.js @@ -1,4 +1,4 @@ -const { parse: parseUrl } = require("url"); +const { parse: parseUrl } = require("node:url"); module.exports = require("./Transport").compose({ name: "ExpressTransport", diff --git a/src/server/GrpcTransport.js b/src/server/GrpcTransport.js index b71943a..1d7611a 100644 --- a/src/server/GrpcTransport.js +++ b/src/server/GrpcTransport.js @@ -4,7 +4,7 @@ module.exports = require("./Transport").compose({ name: "GrpcTransport", props: { - _fs: require("fs"), + _fs: require("node:fs"), _grpc: require("@grpc/grpc-js"), _protoLoader: require("@grpc/proto-loader"), port: process.env.PORT, diff --git a/src/server/HttpTransport.js b/src/server/HttpTransport.js index 6d989e0..4828f27 100644 --- a/src/server/HttpTransport.js +++ b/src/server/HttpTransport.js @@ -1,5 +1,5 @@ -const http = require("http"); -const { parse: parseUrl, URLSearchParams } = require("url"); +const http = require("node:http"); +const { parse: parseUrl, URLSearchParams } = require("node:url"); module.exports = require("./Transport").compose({ name: "HttpTransport", diff --git a/test/client/AllserverClient.test.js b/test/client/AllserverClient.test.js index b3a1939..a1296ef 100644 --- a/test/client/AllserverClient.test.js +++ b/test/client/AllserverClient.test.js @@ -1,4 +1,4 @@ -const assert = require("assert"); +const assert = require("node:assert"); const cls = require("cls-hooked"); const spaceName = "allserver"; diff --git a/test/client/HttpClientTransport.js b/test/client/HttpClientTransport.js index f8d3df1..09217b3 100644 --- a/test/client/HttpClientTransport.js +++ b/test/client/HttpClientTransport.js @@ -1,4 +1,4 @@ -const assert = require("assert"); +const assert = require("node:assert"); const { HttpClientTransport } = require("../.."); diff --git a/test/integration/integration.test.js b/test/integration/integration.test.js index ff07362..2d0f512 100644 --- a/test/integration/integration.test.js +++ b/test/integration/integration.test.js @@ -1,4 +1,4 @@ -const assert = require("assert"); +const assert = require("node:assert"); const procedures = { sayHello({ name }) { @@ -322,7 +322,7 @@ describe("integration", function () { ); const proto = grpc.loadPackageDefinition(packageDefinition); const client = new proto.MyService("localhost:50051", grpc.credentials.createInsecure()); - const { promisify } = require("util"); + const { promisify } = require("node:util"); for (const k in client) if (typeof client[k] === "function") client[k] = promisify(client[k].bind(client)); const response = await client.sayHello({ name: "world" }); From 881214400a1875e7c9f07d241d5983028d7e48ca Mon Sep 17 00:00:00 2001 From: vasyl Date: Thu, 30 Oct 2025 16:40:16 +1100 Subject: [PATCH 12/18] Replace node-fetch with global fetch --- README.md | 6 ++---- example/index.test.js | 1 - package.json | 7 +------ src/client/HttpClientTransport.js | 2 +- test/client/HttpClientTransport.js | 10 +++++----- test/integration/integration.test.js | 8 ++++---- 6 files changed, 13 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index 5f7f296..a8e45ad 100644 --- a/README.md +++ b/README.md @@ -162,7 +162,7 @@ npm i allserver express Optionally, you can use Allserver's built-in client: ```shell -npm i allserver node-fetch +npm i allserver ``` Or do HTTP requests using any module you like. @@ -390,10 +390,8 @@ exports.handler = Allserver({ #### Using built-in client -You'd need to install `node-fetch` optional dependency. - ```shell -npm i allserver node-fetch +npm i allserver ``` Note, that this code is **same** as the gRPC client code example below! diff --git a/example/index.test.js b/example/index.test.js index ebab2b1..3069b33 100644 --- a/example/index.test.js +++ b/example/index.test.js @@ -33,7 +33,6 @@ setTimeout(async () => { const httpServer = Allserver({ procedures, transport: HttpTransport({ port: 40000 }) }); httpServer.start(); - // const fetch = require("node-fetch"); // const response = await ( // await fetch("http://localhost:40000/sayHello", { method: "POST", body: JSON.stringify({ name: user }) }) // ).json(); diff --git a/package.json b/package.json index 5d00f28..145a576 100644 --- a/package.json +++ b/package.json @@ -38,8 +38,7 @@ "@grpc/proto-loader": "0", "bullmq": "3 - 5", "express": "4 - 6", - "micro": "10", - "node-fetch": "2" + "micro": "10" }, "peerDependenciesMeta": { "@grpc/grpc-js": { @@ -56,9 +55,6 @@ }, "micro": { "optional": true - }, - "node-fetch": { - "optional": true } }, "devDependencies": { @@ -70,7 +66,6 @@ "lambda-local": "^1.7.3", "micro": "^10.0.1", "mocha": "^11.7.1", - "node-fetch": "^2.6.9", "nyc": "^17.1.0", "oxlint": "^1.25.0", "prettier": "^2.1.1" diff --git a/src/client/HttpClientTransport.js b/src/client/HttpClientTransport.js index 453c922..92baf1c 100644 --- a/src/client/HttpClientTransport.js +++ b/src/client/HttpClientTransport.js @@ -4,7 +4,7 @@ module.exports = require("./ClientTransport").compose({ name: "HttpClientTransport", props: { - fetch: (typeof globalThis !== "undefined" && globalThis.fetch) || require("node-fetch"), + fetch: globalThis.fetch, headers: { "Content-Type": "application/json; charset=utf-8", }, diff --git a/test/client/HttpClientTransport.js b/test/client/HttpClientTransport.js index 09217b3..2897c37 100644 --- a/test/client/HttpClientTransport.js +++ b/test/client/HttpClientTransport.js @@ -21,14 +21,14 @@ describe("HttpClientTransport", () => { describe("#call", () => { it("should add response to context", async () => { - const { Response } = require("node-fetch"); const MockedTransport = HttpClientTransport.props({ async fetch(uri, options) { assert.strictEqual(uri, "http://localhost/"); assert.strictEqual(options.method, "POST"); - const response = new Response(); - response.json = () => ({ success: true, code: "OK", message: "called" }); - return response; + return new globalThis.Response(JSON.stringify({ success: true, code: "OK", message: "called" }), { + status: 200, + headers: { "Content-Type": "application/json" }, + }); }, }); @@ -37,7 +37,7 @@ describe("HttpClientTransport", () => { const ctx = transport.createCallContext({ procedureName: "" }); const result = await transport.call(ctx); - assert(ctx.http.response instanceof Response); + assert(ctx.http.response instanceof globalThis.Response); assert.deepStrictEqual(result, { success: true, code: "OK", message: "called" }); }); }); diff --git a/test/integration/integration.test.js b/test/integration/integration.test.js index 2d0f512..8bcac64 100644 --- a/test/integration/integration.test.js +++ b/test/integration/integration.test.js @@ -108,7 +108,7 @@ describe("integration", function () { this.timeout(5000); // Needed for CI on Windows. describe("http", () => { - const fetch = require("node-fetch"); + const fetch = globalThis.fetch; it("should behave with AllserverClient", async () => { const httpClient = AllserverClient({ uri: "http://localhost:40000" }); @@ -145,7 +145,7 @@ describe("integration", function () { httpServer.stop(); }); - it("should behave with node-fetch", async () => { + it("should behave with fetch", async () => { const httpServer = Allserver({ procedures, transport: HttpTransport({ port: 40001 }) }); await httpServer.start(); @@ -184,7 +184,7 @@ describe("integration", function () { describe("express", () => { const express = require("express"); - const fetch = require("node-fetch"); + const fetch = globalThis.fetch; it("should behave with AllserverClient", async () => { const httpClient = AllserverClient({ uri: "http://localhost:40002/express/allserver" }); @@ -225,7 +225,7 @@ describe("integration", function () { server.close(); }); - it("should behave with node-fetch", async () => { + it("should behave with fetch", async () => { const app = express(); const expressServer = Allserver({ procedures, transport: ExpressTransport() }); app.use("/express/allsever", expressServer.start()); From 8b5071f5aff32aaeee0750207065692acf3501a3 Mon Sep 17 00:00:00 2001 From: vasyl Date: Thu, 30 Oct 2025 16:44:29 +1100 Subject: [PATCH 13/18] upgrade stampit dependency --- package.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 145a576..e41696e 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "allserver", "version": "2.6.0", - "description": "Multi-protocol simple RPC server and [optional] client. Boilerplate-less. Opinionated. Minimalistic. DX-first.", + "description": "Multi-protocol RPC server and [optional] client. DX-first. Minimalistic. Boilerplate-less. Opinionated.", "main": "src/index.js", "scripts": { "lint": "oxlint ./", "test": "mocha", - "cov": "nyc --reporter=html npm run test" + "cov": "nyc --reporter=html node --run test" }, "keywords": [ "simple", @@ -71,7 +71,7 @@ "prettier": "^2.1.1" }, "dependencies": { - "stampit": "^4.3.1" + "stampit": "^5.0.1" }, "mocha": { "recursive": true, From 40b600be0326425f1c3192be1878038c601a875f Mon Sep 17 00:00:00 2001 From: vasyl Date: Thu, 30 Oct 2025 17:26:32 +1100 Subject: [PATCH 14/18] replace mocha with node --test --- .nvmrc | 2 +- package.json | 7 +------ test/client/AllserverClient.test.js | 1 + test/client/HttpClientTransport.js | 1 + test/integration/integration.test.js | 16 +++++++++------- test/server/Allserver.test.js | 1 + 6 files changed, 14 insertions(+), 14 deletions(-) diff --git a/.nvmrc b/.nvmrc index 209e3ef..a45fd52 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20 +24 diff --git a/package.json b/package.json index e41696e..3367336 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "main": "src/index.js", "scripts": { "lint": "oxlint ./", - "test": "mocha", + "test": "node --test --test-force-exit --trace-deprecation", "cov": "nyc --reporter=html node --run test" }, "keywords": [ @@ -65,16 +65,11 @@ "express": "^4.21.2", "lambda-local": "^1.7.3", "micro": "^10.0.1", - "mocha": "^11.7.1", "nyc": "^17.1.0", "oxlint": "^1.25.0", "prettier": "^2.1.1" }, "dependencies": { "stampit": "^5.0.1" - }, - "mocha": { - "recursive": true, - "exit": true } } diff --git a/test/client/AllserverClient.test.js b/test/client/AllserverClient.test.js index a1296ef..e1b9a15 100644 --- a/test/client/AllserverClient.test.js +++ b/test/client/AllserverClient.test.js @@ -1,4 +1,5 @@ const assert = require("node:assert"); +const { describe, it, afterEach } = require("node:test"); const cls = require("cls-hooked"); const spaceName = "allserver"; diff --git a/test/client/HttpClientTransport.js b/test/client/HttpClientTransport.js index 2897c37..639c165 100644 --- a/test/client/HttpClientTransport.js +++ b/test/client/HttpClientTransport.js @@ -1,4 +1,5 @@ const assert = require("node:assert"); +const { describe, it } = require("node:test"); const { HttpClientTransport } = require("../.."); diff --git a/test/integration/integration.test.js b/test/integration/integration.test.js index 8bcac64..78a5abe 100644 --- a/test/integration/integration.test.js +++ b/test/integration/integration.test.js @@ -1,4 +1,5 @@ const assert = require("node:assert"); +const { describe, it } = require("node:test"); const procedures = { sayHello({ name }) { @@ -28,7 +29,7 @@ const procedures = { return { success: true, code: "CREATED", user: { id: String(Math.random()).substr(2), firstName, lastName } }; }, forcedTimeout() { - return new Promise((resolve) => setTimeout(resolve, 2)); // resolve in 2ms, which is longer than the timeout of 1ms + return new Promise((resolve) => setTimeout(resolve, 10)); // resolve in 10ms, which is longer than the timeout of 1ms }, }; @@ -105,8 +106,6 @@ let { Allserver = Allserver.props({ logger: { error() {} } }); // silence the servers console because we test error cases here describe("integration", function () { - this.timeout(5000); // Needed for CI on Windows. - describe("http", () => { const fetch = globalThis.fetch; @@ -142,7 +141,7 @@ describe("integration", function () { assert.strictEqual(response.code, "ALLSERVER_PROCEDURE_ERROR"); assert.strictEqual(response.error.status, 500); - httpServer.stop(); + await httpServer.stop(); }); it("should behave with fetch", async () => { @@ -178,7 +177,7 @@ describe("integration", function () { const httpClient = AllserverClient({ uri: "http://localhost:40001" }); await callClientMethods(httpClient); - httpServer.stop(); + await httpServer.stop(); }); }); @@ -222,7 +221,7 @@ describe("integration", function () { assert.strictEqual(response.error.status, 500); if (server.closeIdleConnections) server.closeIdleConnections(); - server.close(); + await server.close(); }); it("should behave with fetch", async () => { @@ -265,7 +264,7 @@ describe("integration", function () { await callClientMethods(httpClient); if (server.closeIdleConnections) server.closeIdleConnections(); - server.close(); + await server.close(); }); }); @@ -448,4 +447,7 @@ describe("integration", function () { await bullmqServer.stop(); }); }); + { + timeout: 5000; + } }); diff --git a/test/server/Allserver.test.js b/test/server/Allserver.test.js index 7a40100..f042f0d 100644 --- a/test/server/Allserver.test.js +++ b/test/server/Allserver.test.js @@ -1,4 +1,5 @@ const assert = require("node:assert/strict"); +const { describe, it } = require("node:test"); const cls = require("cls-hooked"); const spaceName = "allserver"; From 21643a52582d87ca9207404f811cff1f47b4b7af Mon Sep 17 00:00:00 2001 From: vasyl Date: Fri, 31 Oct 2025 15:54:11 +1100 Subject: [PATCH 15/18] handle AWS ASK Lambda weirdest responses, drop aws-sdk v2 support --- README.md | 8 +- package.json | 9 ++- src/client/LambdaClientTransport.js | 48 ++++++++---- test/client/LambdaClientTransport.test.js | 93 +++++++++++++++++++++++ 4 files changed, 135 insertions(+), 23 deletions(-) create mode 100644 test/client/LambdaClientTransport.test.js diff --git a/README.md b/README.md index a8e45ad..9a9b0fa 100644 --- a/README.md +++ b/README.md @@ -616,13 +616,7 @@ assert(success === true); ### Bare AWS Lambda invocation -First you need to install any of the AWS SDK versions. - -```shell -npm i allserver aws-sdk -``` - -or +First you need to install the AWS SDK v3. ```shell npm i allserver @aws-sdk/client-lambda diff --git a/package.json b/package.json index 3367336..250de61 100644 --- a/package.json +++ b/package.json @@ -25,7 +25,7 @@ ], "repository": { "type": "git", - "url": "http://github.com/flash-oss/allserver" + "url": "https://github.com/flash-oss/allserver" }, "bugs": { "url": "https://github.com/flash-oss/allserver/issues" @@ -34,6 +34,7 @@ "author": "Vasyl Boroviak", "license": "MIT", "peerDependencies": { + "@aws-sdk/client-lambda": "3", "@grpc/grpc-js": "1", "@grpc/proto-loader": "0", "bullmq": "3 - 5", @@ -41,6 +42,9 @@ "micro": "10" }, "peerDependenciesMeta": { + "@aws-sdk/client-lambda": { + "optional": true + }, "@grpc/grpc-js": { "optional": true }, @@ -58,12 +62,13 @@ } }, "devDependencies": { + "@aws-sdk/client-lambda": "^3.921.0", "@grpc/grpc-js": "^1.13.4", "@grpc/proto-loader": "^0.8.0", "bullmq": "^5.56.9", "cls-hooked": "^4.2.2", "express": "^4.21.2", - "lambda-local": "^1.7.3", + "lambda-local": "^2.2.0", "micro": "^10.0.1", "nyc": "^17.1.0", "oxlint": "^1.25.0", diff --git a/src/client/LambdaClientTransport.js b/src/client/LambdaClientTransport.js index bb98ba6..499f44c 100644 --- a/src/client/LambdaClientTransport.js +++ b/src/client/LambdaClientTransport.js @@ -7,13 +7,7 @@ module.exports = require("./ClientTransport").compose({ init() { if (!this.awsSdkLambdaClient) { - let Lambda; - try { - Lambda = require("aws-sdk").Lambda; // AWS SDK v2 adoption - } catch { - Lambda = require("@aws-sdk/client-lambda").Lambda; - } - + const { Lambda } = require("@aws-sdk/client-lambda"); this.awsSdkLambdaClient = new Lambda(); } }, @@ -29,13 +23,39 @@ module.exports = require("./ClientTransport").compose({ }, async call(ctx) { - let promise = this.awsSdkLambdaClient.invoke({ - FunctionName: this.uri.substring("lambda://".length), - Payload: JSON.stringify(ctx.arg), - }); - if (typeof promise.promise === "function") promise = promise.promise(); // AWS SDK v2 adoption - const invocationResponse = await promise; - return JSON.parse(Buffer.from(invocationResponse.Payload)); + let invocationResponse; + try { + invocationResponse = await this.awsSdkLambdaClient.invoke({ + FunctionName: this.uri.substring("lambda://".length), + Payload: JSON.stringify(ctx.arg), + }); + ctx.lambda.response = invocationResponse; + } catch (e) { + if (e.name.includes("ProviderError") || e.name.includes("NotFound")) e.noNetToServer = true; + throw e; + } + + let json; + try { + json = JSON.parse(Buffer.from(invocationResponse.Payload)); + } catch (e) { + e.code = "ALLSERVER_RPC_RESPONSE_IS_NOT_JSON"; + throw e; + } + + const error = new Error("Bad response payload"); + if (json.constructor !== Object) { + error.code = "ALLSERVER_RPC_RESPONSE_IS_NOT_OBJECT"; + throw error; + } + + // Yes, the AWS Lambda sometimes returns a empty object when the Lambda runtime shuts down abruptly for no apparent reason. + if (Object.keys(json).length === 0) { + error.code = "ALLSERVER_RPC_RESPONSE_IS_EMPTY_OBJECT"; + throw error; + } + + return json; }, createCallContext(defaultCtx) { diff --git a/test/client/LambdaClientTransport.test.js b/test/client/LambdaClientTransport.test.js new file mode 100644 index 0000000..59b62bf --- /dev/null +++ b/test/client/LambdaClientTransport.test.js @@ -0,0 +1,93 @@ +const assert = require("node:assert"); +const { describe, it } = require("node:test"); + +const { LambdaClientTransport } = require("../.."); + +describe("LambdaClientTransport", () => { + describe("#call", () => { + it("should add response to context", async () => { + const MockedTransport = LambdaClientTransport.props({ + awsSdkLambdaClient: { + async invoke({ FunctionName, Payload }) { + assert.strictEqual(FunctionName, "my-function"); + assert.deepStrictEqual(JSON.parse(Payload), { _: { procedureName: "" } }); + return { + Payload: Uint8Array.from( + Buffer.from(JSON.stringify({ success: true, code: "OK", message: "called" })) + ), + }; + }, + }, + }); + const transport = MockedTransport({ uri: "lambda://my-function" }); + const ctx = transport.createCallContext({ arg: { _: { procedureName: "" } } }); + + const result = await transport.call(ctx); + + assert(ctx.lambda?.response?.Payload instanceof Uint8Array); + assert.deepStrictEqual(result, { success: true, code: "OK", message: "called" }); + }); + + it("should handle misconfiguration", async () => { + class ProviderError extends Error { + constructor(message) { + super(message); + this.name = "ProviderError"; + } + } + class NotFound extends Error { + constructor(message) { + super(message); + this.name = "NotFound"; + } + } + + for (const ErrorClass of [ProviderError, NotFound]) { + const MockedTransport = LambdaClientTransport.props({ + awsSdkLambdaClient: { + async invoke() { + throw new ErrorClass("ProviderError"); + }, + }, + }); + const transport = MockedTransport({ uri: "lambda://my-function" }); + const ctx = transport.createCallContext({ arg: { _: { procedureName: "" } } }); + + let error; + await transport.call(ctx).catch((err) => (error = err)); + + assert(error); + assert.strictEqual(error.noNetToServer, true); + } + }); + + it("should handle bad responses", async () => { + const codeToPayloadMap = { + ALLSERVER_RPC_RESPONSE_IS_NOT_JSON: "this is not a JSON", + ALLSERVER_RPC_RESPONSE_IS_NOT_OBJECT: JSON.stringify([]), + ALLSERVER_RPC_RESPONSE_IS_EMPTY_OBJECT: JSON.stringify({}), + }; + + for (const [expectedCode, payloadAsString] of Object.entries(codeToPayloadMap)) { + const MockedTransport = LambdaClientTransport.props({ + awsSdkLambdaClient: { + async invoke() { + return { + Payload: Uint8Array.from(Buffer.from(payloadAsString)), + }; + }, + }, + }); + const transport = MockedTransport({ uri: "lambda://my-function" }); + const ctx = transport.createCallContext({ arg: { _: { procedureName: "" } } }); + + let error; + await transport.call(ctx).catch((err) => (error = err)); + + assert(error); + assert(ctx.lambda?.response?.Payload instanceof Uint8Array); + assert.strictEqual(error.code, expectedCode); + } + }); + }); +}); From 6ff51bbe590ea5d073635a3d7b21813aec05d8c8 Mon Sep 17 00:00:00 2001 From: vasyl Date: Fri, 31 Oct 2025 16:07:04 +1100 Subject: [PATCH 16/18] use "node:assert/strict" instead of "node:assert" --- src/server/Allserver.js | 2 +- test/client/AllserverClient.test.js | 202 +++++++++--------- ...ansport.js => HttpClientTransport.test.js} | 12 +- test/client/LambdaClientTransport.test.js | 12 +- test/integration/integration.test.js | 116 +++++----- test/server/Allserver.test.js | 122 +++++------ 6 files changed, 233 insertions(+), 233 deletions(-) rename test/client/{HttpClientTransport.js => HttpClientTransport.test.js} (76%) diff --git a/src/server/Allserver.js b/src/server/Allserver.js index 2eea087..c75f490 100644 --- a/src/server/Allserver.js +++ b/src/server/Allserver.js @@ -1,4 +1,4 @@ -const assert = require("node:assert"); +const assert = require("node:assert/strict"); const { isObject, isBoolean, isFunction, uniq } = require("../util"); diff --git a/test/client/AllserverClient.test.js b/test/client/AllserverClient.test.js index e1b9a15..1e693ee 100644 --- a/test/client/AllserverClient.test.js +++ b/test/client/AllserverClient.test.js @@ -1,4 +1,4 @@ -const assert = require("node:assert"); +const assert = require("node:assert/strict"); const { describe, it, afterEach } = require("node:test"); const cls = require("cls-hooked"); @@ -84,12 +84,12 @@ describe("AllserverClient", () => { it("should work with third party added transports supported", () => { const client = AllserverClient({ uri: "void://bla" }); - assert.strictEqual(client[p].transport.uri, "void://bla"); + assert.equal(client[p].transport.uri, "void://bla"); }); it("should ignore case", () => { const client = AllserverClient({ uri: "VOID://bla" }); - assert.strictEqual(client[p].transport.uri, "VOID://bla"); + assert.equal(client[p].transport.uri, "VOID://bla"); }); it("should throw is schema is not supported", () => { @@ -111,10 +111,10 @@ describe("AllserverClient", () => { }); const result = await AllserverClient({ transport: MockedTransport() }).introspect(); - assert.strictEqual(result.success, false); - assert.strictEqual(result.code, "ALLSERVER_CLIENT_INTROSPECTION_FAILED"); - assert.strictEqual(result.message, "Couldn't introspect void://localhost due to: Cannot reach server"); - assert.strictEqual(result.error.message, "Cannot reach server"); + assert.equal(result.success, false); + assert.equal(result.code, "ALLSERVER_CLIENT_INTROSPECTION_FAILED"); + assert.equal(result.message, "Couldn't introspect void://localhost due to: Cannot reach server"); + assert.equal(result.error.message, "Cannot reach server"); }); it("should not throw if underlying transport returns malformed introspection", async () => { @@ -128,9 +128,9 @@ describe("AllserverClient", () => { }); const result = await AllserverClient({ transport: MockedTransport() }).testMethod(); - assert.strictEqual(result.success, false); - assert.strictEqual(result.code, "ALLSERVER_CLIENT_MALFORMED_INTROSPECTION"); - assert.strictEqual(result.message, "Malformed introspection from void://localhost"); + assert.equal(result.success, false); + assert.equal(result.code, "ALLSERVER_CLIENT_MALFORMED_INTROSPECTION"); + assert.equal(result.message, "Malformed introspection from void://localhost"); assert(result.error.message.includes("Unexpected token")); }); @@ -145,9 +145,9 @@ describe("AllserverClient", () => { }); const result = await AllserverClient({ transport: MockedTransport() }).testMethod(); - assert.strictEqual(result.success, false); - assert.strictEqual(result.code, "ALLSERVER_CLIENT_MALFORMED_INTROSPECTION"); - assert.strictEqual(result.message, "Malformed introspection from void://localhost"); + assert.equal(result.success, false); + assert.equal(result.code, "ALLSERVER_CLIENT_MALFORMED_INTROSPECTION"); + assert.equal(result.message, "Malformed introspection from void://localhost"); assert(!result.error); }); }); @@ -192,10 +192,10 @@ describe("AllserverClient", () => { }); const result = await AllserverClient({ transport: MockedTransport() }).call("foo", {}); - assert.strictEqual(result.success, false); - assert.strictEqual(result.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); - assert.strictEqual(result.message, "Couldn't reach remote procedure foo due to: Cannot reach server"); - assert.strictEqual(result.error.message, "Cannot reach server"); + assert.equal(result.success, false); + assert.equal(result.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); + assert.equal(result.message, "Couldn't reach remote procedure foo due to: Cannot reach server"); + assert.equal(result.error.message, "Cannot reach server"); }); it("should not throw if neverThrow enabled and the method is not present", async () => { @@ -204,13 +204,13 @@ describe("AllserverClient", () => { }); const client = AllserverClient({ transport: MockedTransport() }); - assert.strictEqual(Reflect.has(client, "foo"), false); // don't have it + assert.equal(Reflect.has(client, "foo"), false); // don't have it const result = await client.call("foo", {}); - assert.strictEqual(Reflect.has(client, "foo"), false); // still don't have it - assert.strictEqual(result.success, false); - assert.strictEqual(result.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); - assert.strictEqual(result.message, "Couldn't reach remote procedure foo due to: Shit happens too"); - assert.strictEqual(result.error.message, "Shit happens too"); + assert.equal(Reflect.has(client, "foo"), false); // still don't have it + assert.equal(result.success, false); + assert.equal(result.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); + assert.equal(result.message, "Couldn't reach remote procedure foo due to: Shit happens too"); + assert.equal(result.error.message, "Shit happens too"); }); it("should return ALLSERVER_CLIENT_TIMEOUT code on timeouts", async () => { @@ -220,9 +220,9 @@ describe("AllserverClient", () => { const client = AllserverClient({ transport: MockedTransport(), timeout: 1 }); // 1 ms timeout for fast unit tests const result = await client.call("doesnt_matter"); - assert.strictEqual(result.success, false); - assert.strictEqual(result.code, "ALLSERVER_CLIENT_TIMEOUT"); - assert.strictEqual(result.message, "The remote procedure doesnt_matter timed out in 1 ms"); + assert.equal(result.success, false); + assert.equal(result.code, "ALLSERVER_CLIENT_TIMEOUT"); + assert.equal(result.message, "The remote procedure doesnt_matter timed out in 1 ms"); }); }); @@ -250,18 +250,18 @@ describe("AllserverClient", () => { }; }, async call({ procedureName, arg }) { - assert.strictEqual(procedureName, "getRates"); - assert.deepStrictEqual(arg, { a: 1, _: { procedureName: "getRates" } }); + assert.equal(procedureName, "getRates"); + assert.deepEqual(arg, { a: 1, _: { procedureName: "getRates" } }); return { success: true, code: "CALLED", message: "A is good", b: 42 }; }, }); const nameMapper = (name) => name !== "hide-me" && name.replace(/(-\w)/g, (k) => k[1].toUpperCase()); const client = AllserverClient({ transport: MockedTransport(), nameMapper }); - assert.strictEqual(Reflect.has(client, "getRates"), false); // dont have it yet + assert.equal(Reflect.has(client, "getRates"), false); // dont have it yet const result = await client.getRates({ a: 1 }); - assert.strictEqual(Reflect.has(client, "getRates"), true); // have it now! - assert.deepStrictEqual(result, { success: true, code: "CALLED", message: "A is good", b: 42 }); + assert.equal(Reflect.has(client, "getRates"), true); // have it now! + assert.deepEqual(result, { success: true, code: "CALLED", message: "A is good", b: 42 }); }); }); @@ -281,28 +281,28 @@ describe("AllserverClient", () => { }; }, async call({ procedureName, arg }) { - assert.strictEqual(procedureName, "getRates"); - assert.deepStrictEqual(arg, { a: 1, _: { procedureName: "getRates" } }); + assert.equal(procedureName, "getRates"); + assert.deepEqual(arg, { a: 1, _: { procedureName: "getRates" } }); return { success: true, code: "CALLED", message: "A is good", b: 42 }; }, }); let beforeCalled = 0; function before(ctx) { - assert.strictEqual(this, client, "The `this` context must be the client itself"); + assert.equal(this, client, "The `this` context must be the client itself"); if (ctx.isIntrospection) { assert(!ctx.procedureName); } else { - assert.strictEqual(ctx.procedureName, "getRates"); - assert.deepStrictEqual(ctx.arg, { a: 1, _: { procedureName: "getRates" } }); + assert.equal(ctx.procedureName, "getRates"); + assert.deepEqual(ctx.arg, { a: 1, _: { procedureName: "getRates" } }); } beforeCalled += 1; } const client = AllserverClient({ transport: MockedTransport(), before }); const result = await client.getRates({ a: 1 }); - assert.deepStrictEqual(result, { success: true, code: "CALLED", message: "A is good", b: 42 }); - assert.strictEqual(beforeCalled, 2); + assert.deepEqual(result, { success: true, code: "CALLED", message: "A is good", b: 42 }); + assert.equal(beforeCalled, 2); assert(introspected); }); @@ -318,9 +318,9 @@ describe("AllserverClient", () => { return "Override result"; }; const client = DefaultedAllserverClient({ transport: VoidClientTransport(), before }); - assert.strictEqual(client[p].before.length, 2, "Default middleware should be present"); + assert.equal(client[p].before.length, 2, "Default middleware should be present"); const result = await client.foo(); - assert.deepStrictEqual(result, "Override result"); + assert.deepEqual(result, "Override result"); assert.ok(defaultMiddlewareCalled); }); @@ -335,7 +335,7 @@ describe("AllserverClient", () => { callIntrospectedProceduresOnly: false, }); const result = await client.foo(); - assert.deepStrictEqual(result, { + assert.deepEqual(result, { success: false, code: "ALLSERVER_CLIENT_MIDDLEWARE_ERROR", message: "The 'before' middleware error while calling 'foo' procedure: 'before' is throwing", @@ -355,7 +355,7 @@ describe("AllserverClient", () => { callIntrospectedProceduresOnly: false, }); const result = await client.foo(); - assert.deepStrictEqual(result, { + assert.deepEqual(result, { success: false, code: "OVERRIDE_CODE", message: "'before' is throwing", @@ -373,12 +373,12 @@ describe("AllserverClient", () => { setTraceIdAndRunFunction("my-random-trace-id", next); }, () => { - assert.strictEqual(getTraceId(), "my-random-trace-id"); + assert.equal(getTraceId(), "my-random-trace-id"); called.push(1); return undefined; }, () => { - assert.strictEqual(getTraceId(), "my-random-trace-id"); + assert.equal(getTraceId(), "my-random-trace-id"); called.push(2); return { success: false, code: "BAD_AUTH_OR_SOMETHING", message: "Bad auth or something" }; }, @@ -391,12 +391,12 @@ describe("AllserverClient", () => { const result = await client.foo(); - assert.deepStrictEqual(result, { + assert.deepEqual(result, { success: false, code: "BAD_AUTH_OR_SOMETHING", message: "Bad auth or something", }); - assert.deepStrictEqual(called, [1, 2]); + assert.deepEqual(called, [1, 2]); }); }); @@ -415,21 +415,21 @@ describe("AllserverClient", () => { }; }, async call({ procedureName, arg }) { - assert.strictEqual(procedureName, "getRates"); - assert.deepStrictEqual(arg, { a: 1, _: { procedureName: "getRates" } }); + assert.equal(procedureName, "getRates"); + assert.deepEqual(arg, { a: 1, _: { procedureName: "getRates" } }); return { success: true, code: "CALLED", message: "A is good", b: 42 }; }, }); let afterCalled = 0; function after(ctx) { - assert.strictEqual(this, client, "The `this` context must be the client itself"); + assert.equal(this, client, "The `this` context must be the client itself"); if (ctx.isIntrospection) { assert(!ctx.procedureName); } else { - assert.strictEqual(ctx.procedureName, "getRates"); - assert.deepStrictEqual(ctx.arg, { a: 1, _: { procedureName: "getRates" } }); - assert.deepStrictEqual(ctx.result, { + assert.equal(ctx.procedureName, "getRates"); + assert.deepEqual(ctx.arg, { a: 1, _: { procedureName: "getRates" } }); + assert.deepEqual(ctx.result, { success: true, code: "CALLED", message: "A is good", @@ -440,8 +440,8 @@ describe("AllserverClient", () => { } const client = AllserverClient({ transport: MockedTransport(), after }); const result = await client.getRates({ a: 1 }); - assert.deepStrictEqual(result, { success: true, code: "CALLED", message: "A is good", b: 42 }); - assert.strictEqual(afterCalled, 2); + assert.deepEqual(result, { success: true, code: "CALLED", message: "A is good", b: 42 }); + assert.equal(afterCalled, 2); assert(introspected); }); @@ -457,9 +457,9 @@ describe("AllserverClient", () => { return "Override result"; }; const client = DefaultedAllserverClient({ transport: VoidClientTransport(), after }); - assert.strictEqual(client[p].after.length, 2, "Default middleware should be present"); + assert.equal(client[p].after.length, 2, "Default middleware should be present"); const result = await client.foo(); - assert.deepStrictEqual(result, "Override result"); + assert.deepEqual(result, "Override result"); assert.ok(defaultMiddlewareCalled); }); @@ -474,7 +474,7 @@ describe("AllserverClient", () => { callIntrospectedProceduresOnly: false, }); const result = await client.foo(); - assert.deepStrictEqual(result, { + assert.deepEqual(result, { success: false, code: "ALLSERVER_CLIENT_MIDDLEWARE_ERROR", message: "The 'after' middleware error while calling 'foo' procedure: 'after' is throwing", @@ -494,7 +494,7 @@ describe("AllserverClient", () => { callIntrospectedProceduresOnly: false, }); const result = await client.foo(); - assert.deepStrictEqual(result, { + assert.deepEqual(result, { success: false, code: "OVERRIDE_CODE", message: "'after' is throwing", @@ -514,11 +514,11 @@ describe("AllserverClient", () => { ], after: [ () => { - assert.strictEqual(getTraceId(), "my-random-trace-id"); + assert.equal(getTraceId(), "my-random-trace-id"); called.push(1); }, () => { - assert.strictEqual(getTraceId(), "my-random-trace-id"); + assert.equal(getTraceId(), "my-random-trace-id"); called.push(2); }, ], @@ -526,7 +526,7 @@ describe("AllserverClient", () => { await client.foo(); - assert.deepStrictEqual(called, [1, 2]); + assert.deepEqual(called, [1, 2]); }); }); @@ -561,7 +561,7 @@ describe("AllserverClient", () => { assert(afterCalled); assert(!secondBeforeCalled); assert(!secondAfterCalled); - assert.deepStrictEqual(result, { + assert.deepEqual(result, { success: false, code: "ALLSERVER_CLIENT_MIDDLEWARE_ERROR", message: "The 'before' middleware error while calling 'foo' procedure: 'before' is throwing", @@ -583,17 +583,17 @@ describe("AllserverClient", () => { }; }, async call({ procedureName, arg }) { - assert.strictEqual(procedureName, "foo"); - assert.deepStrictEqual(arg, { a: 1, _: { procedureName: "foo" } }); + assert.equal(procedureName, "foo"); + assert.deepEqual(arg, { a: 1, _: { procedureName: "foo" } }); return { success: true, code: "CALLED_A", message: "A is good", b: 42 }; }, }); const client = AllserverClient({ transport: MockedTransport() }); - assert.strictEqual(Reflect.has(client, "foo"), false); // dont have it yet + assert.equal(Reflect.has(client, "foo"), false); // dont have it yet const result = await client.foo({ a: 1 }); - assert.strictEqual(Reflect.has(client, "foo"), true); // have it now! - assert.deepStrictEqual(result, { success: true, code: "CALLED_A", message: "A is good", b: 42 }); + assert.equal(Reflect.has(client, "foo"), true); // have it now! + assert.deepEqual(result, { success: true, code: "CALLED_A", message: "A is good", b: 42 }); }); it("should attempt calling if introspection fails", async () => { @@ -602,20 +602,20 @@ describe("AllserverClient", () => { return Promise.reject(new Error("Couldn't introspect")); }, call({ procedureName, arg }) { - assert.strictEqual(procedureName, "foo"); - assert.deepStrictEqual(arg, { a: 1, _: { procedureName: "foo" } }); + assert.equal(procedureName, "foo"); + assert.deepEqual(arg, { a: 1, _: { procedureName: "foo" } }); return { success: true, code: "CALLED_A", message: "A is good", b: 42 }; }, }); const client = AllserverClient({ transport: MockedTransport(), callIntrospectedProceduresOnly: false }); - assert.strictEqual(Reflect.has(client, "foo"), false); // don't have it + assert.equal(Reflect.has(client, "foo"), false); // don't have it const result = await AllserverClient({ transport: MockedTransport(), callIntrospectedProceduresOnly: false, }).foo({ a: 1 }); - assert.strictEqual(Reflect.has(client, "foo"), false); // still don't have it - assert.deepStrictEqual(result, { success: true, code: "CALLED_A", message: "A is good", b: 42 }); + assert.equal(Reflect.has(client, "foo"), false); // still don't have it + assert.deepEqual(result, { success: true, code: "CALLED_A", message: "A is good", b: 42 }); }); it("should not override existing methods", async () => { @@ -634,7 +634,7 @@ describe("AllserverClient", () => { }); function foo(arg) { - assert.strictEqual(arg && arg.a, 1); + assert.equal(arg && arg.a, 1); return { success: true, code: "CACHED", @@ -644,10 +644,10 @@ describe("AllserverClient", () => { } const client = AllserverClient.methods({ foo }).create({ transport: MockedTransport() }); - assert.strictEqual(client.foo, foo); + assert.equal(client.foo, foo); const result = await client.foo({ a: 1 }); - assert.strictEqual(client.foo, foo); - assert.deepStrictEqual(result, { + assert.equal(client.foo, foo); + assert.deepEqual(result, { success: true, code: "CACHED", message: "The reply mimics memory caching", @@ -674,13 +674,13 @@ describe("AllserverClient", () => { for (let i = 1; i <= 2; i += 1) { const client = AllserverClient({ transport: MockedTransport({ uri: "void://very-unique-address-1" }) }); - assert.strictEqual(Reflect.has(client, "foo"), false); // dont have it yet + assert.equal(Reflect.has(client, "foo"), false); // dont have it yet const result = await client.foo({ a: 1 }); - assert.strictEqual(Reflect.has(client, "foo"), true); // have it now! - assert.deepStrictEqual(result, { success: true, code: "CALLED_A", message: "A is good", b: 42 }); + assert.equal(Reflect.has(client, "foo"), true); // have it now! + assert.deepEqual(result, { success: true, code: "CALLED_A", message: "A is good", b: 42 }); } - assert.strictEqual(introspectionCalls, 1); + assert.equal(introspectionCalls, 1); }); it("should re-introspect failed introspections", async () => { @@ -700,13 +700,13 @@ describe("AllserverClient", () => { transport: MockedTransport({ uri: "void://very-unique-address-2" }), callIntrospectedProceduresOnly: false, }); - assert.strictEqual(Reflect.has(client, "foo"), false); // dont have it + assert.equal(Reflect.has(client, "foo"), false); // dont have it const result = await client.foo({ a: 1 }); - assert.strictEqual(Reflect.has(client, "foo"), false); // still don't have it - assert.deepStrictEqual(result, { success: true, code: "CALLED_A", message: "A is good", b: 42 }); + assert.equal(Reflect.has(client, "foo"), false); // still don't have it + assert.deepEqual(result, { success: true, code: "CALLED_A", message: "A is good", b: 42 }); } - assert.strictEqual(introspectionCalls, 2); + assert.equal(introspectionCalls, 2); }); it("should not auto introspect if asked so", (done) => { @@ -723,12 +723,12 @@ describe("AllserverClient", () => { autoIntrospect: false, transport: MockedTransport({ uri: "void://very-unique-address-3" }), }); - assert.strictEqual(Reflect.has(client, "foo"), false); // dont have it + assert.equal(Reflect.has(client, "foo"), false); // dont have it client .foo({ a: 1 }) .then((result) => { - assert.strictEqual(Reflect.has(client, "foo"), false); // still don't have it - assert.deepStrictEqual(result, { success: true, code: "CALLED_A", message: "A is good", b: 42 }); + assert.equal(Reflect.has(client, "foo"), false); // still don't have it + assert.deepEqual(result, { success: true, code: "CALLED_A", message: "A is good", b: 42 }); done(); }) .catch(done); @@ -748,12 +748,12 @@ describe("AllserverClient", () => { const client = AllserverClient({ transport: MockedTransport({ uri: "void://very-unique-address-3" }), }); - assert.strictEqual(Reflect.has(client, "foo"), false); // dont have it + assert.equal(Reflect.has(client, "foo"), false); // dont have it client .foo({ a: 1 }) .then((result) => { - assert.strictEqual(Reflect.has(client, "foo"), false); // still don't have it - assert.deepStrictEqual(result, { + assert.equal(Reflect.has(client, "foo"), false); // still don't have it + assert.deepEqual(result, { success: false, code: "SOME_ERROR", message: "My message", @@ -775,10 +775,10 @@ describe("AllserverClient", () => { }); function protectedsAreOk(protecteds) { - assert.strictEqual(protecteds.neverThrow, false); - assert.strictEqual(protecteds.dynamicMethods, false); - assert.strictEqual(protecteds.autoIntrospect, false); - assert.strictEqual(typeof protecteds.nameMapper, "function"); + assert.equal(protecteds.neverThrow, false); + assert.equal(protecteds.dynamicMethods, false); + assert.equal(protecteds.autoIntrospect, false); + assert.equal(typeof protecteds.nameMapper, "function"); } protectedsAreOk(NewClient.compose.deepProperties[p]); @@ -800,19 +800,19 @@ describe("AllserverClient", () => { }); function protectedsAreOk(protecteds) { - assert.strictEqual(protecteds.neverThrow, false); - assert.strictEqual(protecteds.dynamicMethods, false); - assert.strictEqual(protecteds.autoIntrospect, false); - assert.strictEqual(typeof protecteds.nameMapper, "function"); - assert.deepStrictEqual(protecteds.before, [before, before2]); - assert.deepStrictEqual(protecteds.after, [after, after2]); + assert.equal(protecteds.neverThrow, false); + assert.equal(protecteds.dynamicMethods, false); + assert.equal(protecteds.autoIntrospect, false); + assert.equal(typeof protecteds.nameMapper, "function"); + assert.deepEqual(protecteds.before, [before, before2]); + assert.deepEqual(protecteds.after, [after, after2]); } protectedsAreOk(NewClient({ uri: "void://bla", before: before2, after: after2 })[p]); }); it("should create new factory", () => { - assert.notStrictEqual(AllserverClient, AllserverClient.defaults()); + assert.notEqual(AllserverClient, AllserverClient.defaults()); }); }); }); diff --git a/test/client/HttpClientTransport.js b/test/client/HttpClientTransport.test.js similarity index 76% rename from test/client/HttpClientTransport.js rename to test/client/HttpClientTransport.test.js index 639c165..e3d5e74 100644 --- a/test/client/HttpClientTransport.js +++ b/test/client/HttpClientTransport.test.js @@ -1,4 +1,4 @@ -const assert = require("node:assert"); +const assert = require("node:assert/strict"); const { describe, it } = require("node:test"); const { HttpClientTransport } = require("../.."); @@ -8,7 +8,7 @@ describe("HttpClientTransport", () => { it("should accept uri without end slash", () => { const transport = HttpClientTransport({ uri: "http://bla" }); - assert.strictEqual(transport.uri, "http://bla/"); + assert.equal(transport.uri, "http://bla/"); }); it("should assign headers", () => { @@ -16,7 +16,7 @@ describe("HttpClientTransport", () => { const ctx = transport.createCallContext({}); - assert.strictEqual(ctx.http.headers.authorization, "Basic token"); + assert.equal(ctx.http.headers.authorization, "Basic token"); }); }); @@ -24,8 +24,8 @@ describe("HttpClientTransport", () => { it("should add response to context", async () => { const MockedTransport = HttpClientTransport.props({ async fetch(uri, options) { - assert.strictEqual(uri, "http://localhost/"); - assert.strictEqual(options.method, "POST"); + assert.equal(uri, "http://localhost/"); + assert.equal(options.method, "POST"); return new globalThis.Response(JSON.stringify({ success: true, code: "OK", message: "called" }), { status: 200, headers: { "Content-Type": "application/json" }, @@ -39,7 +39,7 @@ describe("HttpClientTransport", () => { const result = await transport.call(ctx); assert(ctx.http.response instanceof globalThis.Response); - assert.deepStrictEqual(result, { success: true, code: "OK", message: "called" }); + assert.deepEqual(result, { success: true, code: "OK", message: "called" }); }); }); }); diff --git a/test/client/LambdaClientTransport.test.js b/test/client/LambdaClientTransport.test.js index 59b62bf..ab95f73 100644 --- a/test/client/LambdaClientTransport.test.js +++ b/test/client/LambdaClientTransport.test.js @@ -1,4 +1,4 @@ -const assert = require("node:assert"); +const assert = require("node:assert/strict"); const { describe, it } = require("node:test"); const { LambdaClientTransport } = require("../.."); @@ -9,8 +9,8 @@ describe("LambdaClientTransport", () => { const MockedTransport = LambdaClientTransport.props({ awsSdkLambdaClient: { async invoke({ FunctionName, Payload }) { - assert.strictEqual(FunctionName, "my-function"); - assert.deepStrictEqual(JSON.parse(Payload), { _: { procedureName: "" } }); + assert.equal(FunctionName, "my-function"); + assert.deepEqual(JSON.parse(Payload), { _: { procedureName: "" } }); return { Payload: Uint8Array.from( Buffer.from(JSON.stringify({ success: true, code: "OK", message: "called" })) @@ -25,7 +25,7 @@ describe("LambdaClientTransport", () => { const result = await transport.call(ctx); assert(ctx.lambda?.response?.Payload instanceof Uint8Array); - assert.deepStrictEqual(result, { success: true, code: "OK", message: "called" }); + assert.deepEqual(result, { success: true, code: "OK", message: "called" }); }); it("should handle misconfiguration", async () => { @@ -57,7 +57,7 @@ describe("LambdaClientTransport", () => { await transport.call(ctx).catch((err) => (error = err)); assert(error); - assert.strictEqual(error.noNetToServer, true); + assert.equal(error.noNetToServer, true); } }); @@ -86,7 +86,7 @@ describe("LambdaClientTransport", () => { assert(error); assert(ctx.lambda?.response?.Payload instanceof Uint8Array); - assert.strictEqual(error.code, expectedCode); + assert.equal(error.code, expectedCode); } }); }); diff --git a/test/integration/integration.test.js b/test/integration/integration.test.js index 78a5abe..0764cf0 100644 --- a/test/integration/integration.test.js +++ b/test/integration/integration.test.js @@ -1,4 +1,4 @@ -const assert = require("node:assert"); +const assert = require("node:assert/strict"); const { describe, it } = require("node:test"); const procedures = { @@ -39,54 +39,54 @@ async function callClientMethods(client) { // twice in a row response = await client.sayHello({ name: "world" }); const expectedHello = { success: true, code: "SUCCESS", message: "Success", sayHello: "Hello world" }; - assert.deepStrictEqual(response, expectedHello); + assert.deepEqual(response, expectedHello); response = await client.sayHello({ name: "world" }); - assert.deepStrictEqual(response, expectedHello); + assert.deepEqual(response, expectedHello); // Introspection work response = await client.introspect({}); - assert.deepStrictEqual(Object.keys(procedures), Object.keys(JSON.parse(response.procedures))); + assert.deepEqual(Object.keys(procedures), Object.keys(JSON.parse(response.procedures))); // Turn off introspection response = await client.introspection({ enable: false }); - assert.strictEqual(response.success, true); + assert.equal(response.success, true); // Introspection should not work now response = await client.introspect(); - assert.strictEqual(response.success, false); - assert.strictEqual(response.code, "ALLSERVER_CLIENT_INTROSPECTION_FAILED"); + assert.equal(response.success, false); + assert.equal(response.code, "ALLSERVER_CLIENT_INTROSPECTION_FAILED"); // Turn on introspection response = await client.introspection({ enable: true }); - assert.strictEqual(response.success, true); + assert.equal(response.success, true); // Introspection should be back working response = await client.introspect(); - assert.strictEqual(response.success, true); - assert.strictEqual(response.code, "ALLSERVER_INTROSPECTION"); + assert.equal(response.success, true); + assert.equal(response.code, "ALLSERVER_INTROSPECTION"); for (const number of [0, 1, 2, 3]) { response = await client.gate({ number }); - assert.strictEqual(response.success, number <= 2); + assert.equal(response.success, number <= 2); } response = await client.throws({}); - assert.strictEqual(response.success, false); - assert.strictEqual(response.code, "ALLSERVER_PROCEDURE_ERROR"); + assert.equal(response.success, false); + assert.equal(response.code, "ALLSERVER_PROCEDURE_ERROR"); assert(response.message.startsWith("'Cannot read ")); assert(response.message.endsWith(" error in 'throws' procedure")); response = await client.throwsBadArgs({}); - assert.strictEqual(response.success, false); - assert.strictEqual(response.code, "ERR_ASSERTION"); - assert.strictEqual(response.message, `'missingArg is mandatory' error in 'throwsBadArgs' procedure`); + assert.equal(response.success, false); + assert.equal(response.code, "ERR_ASSERTION"); + assert.equal(response.message, `'missingArg is mandatory' error in 'throwsBadArgs' procedure`); response = await client.unexist({}); - assert.strictEqual(response.success, false); - assert.strictEqual(response.code, "ALLSERVER_CLIENT_PROCEDURE_NOT_FOUND"); - assert.strictEqual(response.message, "Procedure 'unexist' not found via introspection"); + assert.equal(response.success, false); + assert.equal(response.code, "ALLSERVER_CLIENT_PROCEDURE_NOT_FOUND"); + assert.equal(response.message, "Procedure 'unexist' not found via introspection"); client[Symbol.for("AllserverClient")].timeout = 1; response = await client.forcedTimeout({}); - assert.strictEqual(response.success, false); - assert.strictEqual(response.code, "ALLSERVER_CLIENT_TIMEOUT"); - assert.strictEqual(response.message, "The remote procedure forcedTimeout timed out in 1 ms"); + assert.equal(response.success, false); + assert.equal(response.code, "ALLSERVER_CLIENT_TIMEOUT"); + assert.equal(response.message, "The remote procedure forcedTimeout timed out in 1 ms"); client[Symbol.for("AllserverClient")].timeout = 0; } @@ -114,10 +114,10 @@ describe("integration", function () { let response; response = await httpClient.sayHello({ name: "world" }); - assert.strictEqual(response.success, false); - assert.strictEqual(response.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); - assert.strictEqual(response.message, "Couldn't reach remote procedure: sayHello"); - if (response.error.cause) assert.strictEqual(response.error.cause.code, "ECONNREFUSED"); + assert.equal(response.success, false); + assert.equal(response.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); + assert.equal(response.message, "Couldn't reach remote procedure: sayHello"); + if (response.error.cause) assert.equal(response.error.cause.code, "ECONNREFUSED"); else assert(response.error.message.includes("ECONNREFUSED")); const httpServer = Allserver({ procedures, transport: HttpTransport({ port: 40000 }) }); @@ -129,17 +129,17 @@ describe("integration", function () { // Should return 400 response = await httpClient.throwsBadArgs(); - assert.strictEqual(response.code, "ERR_ASSERTION"); - assert.strictEqual(response.error.status, 400); + assert.equal(response.code, "ERR_ASSERTION"); + assert.equal(response.error.status, 400); // Should return 404 response = await httpClient.unexist(); - assert.strictEqual(response.code, "ALLSERVER_CLIENT_PROCEDURE_NOT_FOUND"); + assert.equal(response.code, "ALLSERVER_CLIENT_PROCEDURE_NOT_FOUND"); // Should return 500 response = await httpClient.throws(); - assert.strictEqual(response.code, "ALLSERVER_PROCEDURE_ERROR"); - assert.strictEqual(response.error.status, 500); + assert.equal(response.code, "ALLSERVER_PROCEDURE_ERROR"); + assert.equal(response.error.status, 500); await httpServer.stop(); }); @@ -157,7 +157,7 @@ describe("integration", function () { }) ).json(); const expectedHello = { success: true, code: "SUCCESS", message: "Success", sayHello: "Hello world" }; - assert.deepStrictEqual(response, expectedHello); + assert.deepEqual(response, expectedHello); // HTTP-ony specific tests @@ -167,13 +167,13 @@ describe("integration", function () { body: Buffer.allocUnsafe(999), }); assert(!response.ok); - assert.strictEqual(response.status, 400); + assert.equal(response.status, 400); // Should call using GET with query params response = await fetch("http://localhost:40001/sayHello?name=world"); assert(response.ok); const body = await response.json(); - assert.deepStrictEqual(body, expectedHello); + assert.deepEqual(body, expectedHello); const httpClient = AllserverClient({ uri: "http://localhost:40001" }); await callClientMethods(httpClient); @@ -190,10 +190,10 @@ describe("integration", function () { let response; response = await httpClient.sayHello({ name: "world" }); - assert.strictEqual(response.success, false); - assert.strictEqual(response.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); - assert.strictEqual(response.message, "Couldn't reach remote procedure: sayHello"); - if (response.error.cause) assert.strictEqual(response.error.cause.code, "ECONNREFUSED"); + assert.equal(response.success, false); + assert.equal(response.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); + assert.equal(response.message, "Couldn't reach remote procedure: sayHello"); + if (response.error.cause) assert.equal(response.error.cause.code, "ECONNREFUSED"); else assert(response.error.message.includes("ECONNREFUSED")); const app = express(); @@ -208,17 +208,17 @@ describe("integration", function () { // Should return 400 response = await httpClient.throwsBadArgs(); - assert.strictEqual(response.code, "ERR_ASSERTION"); - assert.strictEqual(response.error.status, 400); + assert.equal(response.code, "ERR_ASSERTION"); + assert.equal(response.error.status, 400); // Should return 404 response = await httpClient.unexist(); - assert.strictEqual(response.code, "ALLSERVER_CLIENT_PROCEDURE_NOT_FOUND"); + assert.equal(response.code, "ALLSERVER_CLIENT_PROCEDURE_NOT_FOUND"); // Should return 500 response = await httpClient.throws(); - assert.strictEqual(response.code, "ALLSERVER_PROCEDURE_ERROR"); - assert.strictEqual(response.error.status, 500); + assert.equal(response.code, "ALLSERVER_PROCEDURE_ERROR"); + assert.equal(response.error.status, 500); if (server.closeIdleConnections) server.closeIdleConnections(); await server.close(); @@ -241,7 +241,7 @@ describe("integration", function () { }) ).json(); const expectedHello = { success: true, code: "SUCCESS", message: "Success", sayHello: "Hello world" }; - assert.deepStrictEqual(response, expectedHello); + assert.deepEqual(response, expectedHello); // HTTP-ony specific tests @@ -252,13 +252,13 @@ describe("integration", function () { body: Buffer.allocUnsafe(999), }); assert(!response.ok); - assert.strictEqual(response.status, 400); + assert.equal(response.status, 400); // Should call using GET with query params response = await fetch("http://localhost:40003/express/allsever/sayHello?name=world"); assert(response.ok); const body = await response.json(); - assert.deepStrictEqual(body, expectedHello); + assert.deepEqual(body, expectedHello); const httpClient = AllserverClient({ uri: "http://localhost:40003/express/allsever" }); await callClientMethods(httpClient); @@ -279,9 +279,9 @@ describe("integration", function () { transport: GrpcClientTransport({ protoFile, uri: "grpc://localhost:50051" }), }); response = await grpcClient.sayHello({ name: "world" }); - assert.strictEqual(response.success, false); - assert.strictEqual(response.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); - assert.strictEqual(response.message, "Couldn't reach remote procedure: sayHello"); + assert.equal(response.success, false); + assert.equal(response.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); + assert.equal(response.message, "Couldn't reach remote procedure: sayHello"); // Without protoFile grpcClient = AllserverClient({ @@ -289,9 +289,9 @@ describe("integration", function () { }); response = await grpcClient.sayHello({ name: "world" }); - assert.strictEqual(response.success, false); - assert.strictEqual(response.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); - assert.strictEqual(response.message, "Couldn't reach remote procedure: sayHello"); + assert.equal(response.success, false); + assert.equal(response.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); + assert.equal(response.message, "Couldn't reach remote procedure: sayHello"); const grpcServer = Allserver({ procedures, @@ -326,7 +326,7 @@ describe("integration", function () { const response = await client.sayHello({ name: "world" }); const expectedHello = { success: true, code: "SUCCESS", message: "Success", sayHello: "Hello world" }; - assert.deepStrictEqual(response, expectedHello); + assert.deepEqual(response, expectedHello); await grpcServer.stop(); }); @@ -400,10 +400,10 @@ describe("integration", function () { let bullmqClient = AllserverClient({ uri: `bullmq://localhost:65432` }); // This port should not have Redis server on it. const response = await bullmqClient.sayHello({ name: "world" }); - assert.strictEqual(response.success, false); - assert.strictEqual(response.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); - assert.strictEqual(response.message, "Couldn't reach remote procedure: sayHello"); - assert.strictEqual(response.error.code, "ECONNREFUSED"); + assert.equal(response.success, false); + assert.equal(response.code, "ALLSERVER_CLIENT_PROCEDURE_UNREACHABLE"); + assert.equal(response.message, "Couldn't reach remote procedure: sayHello"); + assert.equal(response.error.code, "ECONNREFUSED"); if (process.platform === "win32") return; @@ -442,7 +442,7 @@ describe("integration", function () { const job = await queue.add("sayHello", { name: "world" }, jobsOptions); const response = await job.waitUntilFinished(queueEvents, 1000); const expectedHello = { success: true, code: "SUCCESS", message: "Success", sayHello: "Hello world" }; - assert.deepStrictEqual(response, expectedHello); + assert.deepEqual(response, expectedHello); await bullmqServer.stop(); }); diff --git a/test/server/Allserver.test.js b/test/server/Allserver.test.js index f042f0d..a1343f4 100644 --- a/test/server/Allserver.test.js +++ b/test/server/Allserver.test.js @@ -48,10 +48,10 @@ describe("Allserver", () => { let ctx = { void: { proc: "testMethod" } }; server.handleCall(ctx); - assert.strictEqual(ctx.callNumber, 0); + assert.equal(ctx.callNumber, 0); ctx = { void: { proc: "testMethod" } }; server.handleCall(ctx); - assert.strictEqual(ctx.callNumber, 1); + assert.equal(ctx.callNumber, 1); }); it("should call if procedures is an array", async () => { @@ -67,7 +67,7 @@ describe("Allserver", () => { let ctx = { void: { proc: "0" } }; await server.handleCall(ctx); - assert.strictEqual(called, true); + assert.equal(called, true); }); it("should reply ALLSERVER_PROCEDURE_NOT_FOUND", async () => { @@ -81,7 +81,7 @@ describe("Allserver", () => { }; }, reply(ctx) { - assert.deepStrictEqual(ctx.result, { + assert.deepEqual(ctx.result, { success: false, code: "ALLSERVER_PROCEDURE_NOT_FOUND", message: "Procedure not found: dontExist", @@ -103,7 +103,7 @@ describe("Allserver", () => { let replied = false; const MockedTransport = VoidTransport.methods({ reply(ctx) { - assert.deepStrictEqual(ctx.result, { + assert.deepEqual(ctx.result, { success: true, code: "OPENED", message: "Bla", @@ -115,7 +115,7 @@ describe("Allserver", () => { const server = Allserver({ procedures: { foo({ bar }) { - assert.strictEqual(bar, "baz"); + assert.equal(bar, "baz"); return { success: true, code: "OPENED", message: "Bla", data: "point" }; }, }, @@ -131,7 +131,7 @@ describe("Allserver", () => { let replied = false; const MockedTransport = VoidTransport.methods({ reply(ctx) { - assert.deepStrictEqual(ctx.result, { + assert.deepEqual(ctx.result, { success: false, code: "ALLSERVER_PROCEDURE_ERROR", message: "''undefined' is not a function' error in 'foo' procedure", @@ -143,8 +143,8 @@ describe("Allserver", () => { const server = Allserver({ logger: { error(err, code) { - assert.strictEqual(code, "ALLSERVER_PROCEDURE_ERROR"); - assert.strictEqual(err.message, "'undefined' is not a function"); + assert.equal(code, "ALLSERVER_PROCEDURE_ERROR"); + assert.equal(err.message, "'undefined' is not a function"); logged = true; }, }, @@ -171,10 +171,10 @@ describe("Allserver", () => { const ctx = { void: { proc: "testMethod" } }; await server.handleCall(ctx); - assert.strictEqual(typeof ctx.result, "object"); - assert.strictEqual(typeof ctx.result.success, "boolean"); - assert.strictEqual(typeof ctx.result.code, "string"); - assert.strictEqual(typeof ctx.result.message, "string"); + assert.equal(typeof ctx.result, "object"); + assert.equal(typeof ctx.result.success, "boolean"); + assert.equal(typeof ctx.result.code, "string"); + assert.equal(typeof ctx.result.message, "string"); } }); }); @@ -184,8 +184,8 @@ describe("Allserver", () => { let started = false; const MockedTransport = VoidTransport.methods({ async startServer(...args) { - assert.strictEqual(args.length, 1); - assert.strictEqual(args[0].allserver, server); + assert.equal(args.length, 1); + assert.equal(args[0].allserver, server); started = true; }, }); @@ -226,7 +226,7 @@ describe("Allserver", () => { assert(!ctx.introspection); }, async reply(ctx) { - assert.strictEqual(ctx.result, undefined); + assert.equal(ctx.result, undefined); replied = true; }, }); @@ -246,7 +246,7 @@ describe("Allserver", () => { assert(!ctx.introspection); }, async reply(ctx) { - assert.strictEqual(ctx.result, undefined); + assert.equal(ctx.result, undefined); replied = true; }, }); @@ -263,7 +263,7 @@ describe("Allserver", () => { const MockedTransport = VoidTransport.methods({ isIntrospection: () => true, getProcedureName(ctx) { - assert.strictEqual(ctx.void.proc, "introspect"); + assert.equal(ctx.void.proc, "introspect"); return ""; }, prepareIntrospectionReply(ctx) { @@ -275,7 +275,7 @@ describe("Allserver", () => { }; }, reply(ctx) { - assert.deepStrictEqual(ctx.introspection, { foo: "function", bar: "function" }); + assert.deepEqual(ctx.introspection, { foo: "function", bar: "function" }); replied = true; }, }); @@ -296,8 +296,8 @@ describe("Allserver", () => { let called = false; const server = Allserver({ before(ctx) { - assert.strictEqual(this, server, "The `this` context must be the server itself"); - assert.deepStrictEqual(ctx, { + assert.equal(this, server, "The `this` context must be the server itself"); + assert.deepEqual(ctx, { arg: { _: { procedureName: "testMethod" } }, callNumber: 0, procedure: server.procedures.testMethod, @@ -323,8 +323,8 @@ describe("Allserver", () => { const server = Allserver({ logger: { error(err, code) { - assert.strictEqual(code, "ALLSERVER_MIDDLEWARE_ERROR"); - assert.strictEqual(err.message, "Handle me please"); + assert.equal(code, "ALLSERVER_MIDDLEWARE_ERROR"); + assert.equal(err.message, "Handle me please"); logged = true; }, }, @@ -342,8 +342,8 @@ describe("Allserver", () => { await server.handleCall(ctx); assert(logged); - assert.strictEqual(lastMiddlewareCalled, false); - assert.deepStrictEqual(ctx.result, { + assert.equal(lastMiddlewareCalled, false); + assert.deepEqual(ctx.result, { success: false, code: "ALLSERVER_MIDDLEWARE_ERROR", message: "'Handle me please' error in 'before' middleware", @@ -372,8 +372,8 @@ describe("Allserver", () => { let ctx = { void: { proc: "testMethod" } }; await server.handleCall(ctx); - assert.deepStrictEqual(called, [1, 2]); - assert.deepStrictEqual(ctx.result, { + assert.deepEqual(called, [1, 2]); + assert.deepEqual(ctx.result, { success: false, code: "BAD_AUTH_OR_SOMETHING", message: "Bad auth or something", @@ -384,7 +384,7 @@ describe("Allserver", () => { let replied = false; const MockedTransport = VoidTransport.methods({ async reply(ctx) { - assert.strictEqual(ctx.result, 42); + assert.equal(ctx.result, 42); replied = true; }, }); @@ -445,12 +445,12 @@ describe("Allserver", () => { } }, () => { - assert.strictEqual(getTraceId(), "my-random-trace-id"); + assert.equal(getTraceId(), "my-random-trace-id"); called.push(1); return undefined; }, () => { - assert.strictEqual(getTraceId(), "my-random-trace-id"); + assert.equal(getTraceId(), "my-random-trace-id"); called.push(2); return { success: false, code: "BAD_AUTH_OR_SOMETHING", message: "Bad auth or something" }; }, @@ -464,8 +464,8 @@ describe("Allserver", () => { let ctx = { void: { proc: "testMethod" }, arg: { _: { traceId: "my-random-trace-id" } } }; await server.handleCall(ctx); - assert.deepStrictEqual(called, [1, 2]); - assert.deepStrictEqual(ctx.result, { + assert.deepEqual(called, [1, 2]); + assert.deepEqual(ctx.result, { success: false, code: "BAD_AUTH_OR_SOMETHING", message: "Bad auth or something", @@ -478,8 +478,8 @@ describe("Allserver", () => { let called = false; const server = Allserver({ after(ctx) { - assert.strictEqual(this, server, "The `this` context must be the server itself"); - assert.deepStrictEqual(ctx, { + assert.equal(this, server, "The `this` context must be the server itself"); + assert.deepEqual(ctx, { arg: { _: { procedureName: "testMethod" } }, callNumber: 0, procedure: server.procedures.testMethod, @@ -509,8 +509,8 @@ describe("Allserver", () => { const server = Allserver({ logger: { error(err, code) { - assert.strictEqual(code, "ALLSERVER_MIDDLEWARE_ERROR"); - assert.strictEqual(err.message, "Handle me please"); + assert.equal(code, "ALLSERVER_MIDDLEWARE_ERROR"); + assert.equal(err.message, "Handle me please"); logged = true; }, }, @@ -523,7 +523,7 @@ describe("Allserver", () => { await server.handleCall(ctx); assert(logged); - assert.deepStrictEqual(ctx.result, { + assert.deepEqual(ctx.result, { success: false, code: "ALLSERVER_MIDDLEWARE_ERROR", message: "'Handle me please' error in 'after' middleware", @@ -552,8 +552,8 @@ describe("Allserver", () => { let ctx = { void: { proc: "testMethod" } }; await server.handleCall(ctx); - assert.deepStrictEqual(called, [1, 2]); - assert.deepStrictEqual(ctx.result, { + assert.deepEqual(called, [1, 2]); + assert.deepEqual(ctx.result, { success: false, code: "OVERWRITING_RESULT", message: "Something something", @@ -564,7 +564,7 @@ describe("Allserver", () => { let replied = false; const MockedTransport = VoidTransport.methods({ async reply(ctx) { - assert.strictEqual(ctx.result, 42); + assert.equal(ctx.result, 42); replied = true; }, }); @@ -598,17 +598,17 @@ describe("Allserver", () => { ], procedures: { testMethod() { - assert.strictEqual(getTraceId(), "my-random-trace-id"); + assert.equal(getTraceId(), "my-random-trace-id"); called.push("testMethod"); }, }, after: [ () => { - assert.strictEqual(getTraceId(), "my-random-trace-id"); + assert.equal(getTraceId(), "my-random-trace-id"); called.push(1); }, () => { - assert.strictEqual(getTraceId(), "my-random-trace-id"); + assert.equal(getTraceId(), "my-random-trace-id"); called.push(2); }, ], @@ -617,8 +617,8 @@ describe("Allserver", () => { let ctx = { void: { proc: "testMethod" }, arg: { _: { traceId: "my-random-trace-id" } } }; await server.handleCall(ctx); - assert.deepStrictEqual(called, ["testMethod", 1, 2]); - assert.deepStrictEqual(ctx.result, { + assert.deepEqual(called, ["testMethod", 1, 2]); + assert.deepEqual(ctx.result, { success: true, code: "SUCCESS", message: "Success", @@ -643,12 +643,12 @@ describe("Allserver", () => { let ctx = { void: { proc: "testMethod" } }; await server.handleCall(ctx); - assert.deepStrictEqual(ctx.result, { + assert.deepEqual(ctx.result, { success: false, code: "ALLSERVER_MIDDLEWARE_ERROR", message: "'Bad middleware' error in 'before' middleware", }); - assert.strictEqual(ctx.error, err); + assert.equal(ctx.error, err); assert(afterCalled); }); }); @@ -662,7 +662,7 @@ describe("Allserver", () => { assert(server.transport.micro); // Users console logger by default - assert.strictEqual(server.logger, console); + assert.equal(server.logger, console); }); }); @@ -678,12 +678,12 @@ describe("Allserver", () => { const NewServer = Allserver.defaults({ procedures, transport, logger, introspection, before, after }); function propsAreOk(server) { - assert.strictEqual(server.procedures, procedures); - assert.strictEqual(server.transport, transport); - assert.strictEqual(server.logger, logger); - assert.strictEqual(server.introspection, introspection); - assert.deepStrictEqual(server.before, [before]); - assert.deepStrictEqual(server.after, [after]); + assert.equal(server.procedures, procedures); + assert.equal(server.transport, transport); + assert.equal(server.logger, logger); + assert.equal(server.introspection, introspection); + assert.deepEqual(server.before, [before]); + assert.deepEqual(server.after, [after]); } propsAreOk(NewServer()); @@ -702,19 +702,19 @@ describe("Allserver", () => { const NewServer = Allserver.defaults({ procedures, transport, logger, introspection, before, after }); function propsAreOk(props) { - assert.strictEqual(props.procedures, procedures); - assert.strictEqual(props.transport, transport); - assert.strictEqual(props.logger, logger); - assert.strictEqual(props.introspection, introspection); - assert.deepStrictEqual(props.before, [before, before2]); - assert.deepStrictEqual(props.after, [after, after2]); + assert.equal(props.procedures, procedures); + assert.equal(props.transport, transport); + assert.equal(props.logger, logger); + assert.equal(props.introspection, introspection); + assert.deepEqual(props.before, [before, before2]); + assert.deepEqual(props.after, [after, after2]); } propsAreOk(NewServer({ before: before2, after: after2 })); }); it("should create new factory", () => { - assert.notStrictEqual(Allserver, Allserver.defaults()); + assert.notEqual(Allserver, Allserver.defaults()); }); }); }); From 589db30ad65ce82fae0f7753ac874e5a8977ec20 Mon Sep 17 00:00:00 2001 From: vasyl Date: Fri, 31 Oct 2025 18:00:34 +1100 Subject: [PATCH 17/18] implement `timeout` in the AllserverClient side. --- README.md | 7 ++++++- src/client/AllserverClient.js | 11 ++++++++--- src/client/BullmqClientTransport.js | 4 +--- src/client/ClientTransport.js | 5 ++++- src/client/LambdaClientTransport.js | 11 +++++++---- test/integration/integration.test.js | 12 +++++++----- 6 files changed, 33 insertions(+), 17 deletions(-) diff --git a/README.md b/README.md index 9a9b0fa..adc7b2e 100644 --- a/README.md +++ b/README.md @@ -673,6 +673,9 @@ aws lambda invoke --function-name my-lambda-name --payload '{"_":{"procedureName - `transport`
The transport implementation object. The `uri` is ignored if this option provided. If not given then it will be automatically created based on the `uri` schema. E.g. if it starts with `http://` or `https://` then `HttpClientTransport` will be used. If starts with `grpc://` then `GrpcClientTransport` will be used. If starts with `bullmq://` then `BullmqClientTransport` is used. +- `timeout=60_000`
+ Set it to `0` if you don't need a timeout. If the procedure call takes longer than this value then the `AllserverClient` will return `success=false` and `code=ALLSERVER_CLIENT_TIMEOUT`. + - `neverThrow=true`
Set it to `false` if you want to get exceptions when there are a network, or a server errors during a procedure call. Otherwise, the standard `{success,code,message}` object is returned from method calls. The Allserver error `code`s are always start with `"ALLSERVER_"`. E.g. `"ALLSERVER_CLIENT_MALFORMED_INTROSPECTION"`. @@ -701,6 +704,7 @@ You can change the above mentioned options default values like this: ```js AllseverClient = AllserverClient.defaults({ transport, + timeout, neverThrow, dynamicMethods, autoIntrospect, @@ -904,7 +908,8 @@ const client = AllserverClient({ ctx.http.headers.authorization = "Basic my-token"; }, async after(ctx) { - if (ctx.error) console.error(ctx.error); else console.log(ctx.result); + if (ctx.error) console.error(ctx.error); + else console.log(ctx.result); }, }); ``` diff --git a/src/client/AllserverClient.js b/src/client/AllserverClient.js index 59fcf62..0e1342c 100644 --- a/src/client/AllserverClient.js +++ b/src/client/AllserverClient.js @@ -142,7 +142,7 @@ module.exports = require("stampit")({ // The protocol implementation strategy. transport: null, // The maximum time to wait until returning the ALLSERVER_CLIENT_TIMEOUT error. 0 means - no timeout. - timeout: 0, + timeout: 60_000, // Disable any exception throwing when calling any methods. Otherwise, throws network and server errors. neverThrow: true, // Automatically find (introspect) and call corresponding remote procedures. Use only the methods defined in client side. @@ -204,7 +204,7 @@ module.exports = require("stampit")({ const getTransport = stamp.compose.deepConfiguration.transports[schema.toLowerCase()]; if (!getTransport) throw new Error(`Schema not supported: ${uri}`); - this[p].transport = getTransport()({ uri }); + this[p].transport = getTransport()({ uri, timeout: this[p].timeout }); } if (before) this[p].before = [].concat(this[p].before).concat(before).filter(isFunction); @@ -289,6 +289,9 @@ module.exports = require("stampit")({ const transportMethod = ctx.isIntrospection ? "introspect" : "call"; // In JavaScript if the `timeout` is null or undefined or some other object this condition will return `false` if (this[p].timeout > 0) { + let timeout = this[p].timeout; + // Let's give a chance to the Transport to return its native timeout response before returning Client's timeout response. + if (timeout >= 100) timeout = Math.round(timeout / 100 + timeout); return Promise.race([ this[p].transport[transportMethod](ctx), new Promise((resolve) => @@ -299,7 +302,7 @@ module.exports = require("stampit")({ code: "ALLSERVER_CLIENT_TIMEOUT", message: `The remote procedure ${ctx.procedureName} timed out in ${this[p].timeout} ms`, }), - this[p].timeout + timeout ) ), ]); @@ -353,6 +356,7 @@ module.exports = require("stampit")({ statics: { defaults({ transport, + timeout, neverThrow, dynamicMethods, autoIntrospect, @@ -367,6 +371,7 @@ module.exports = require("stampit")({ return this.deepProps({ [p]: { transport, + timeout, neverThrow, dynamicMethods, callIntrospectedProceduresOnly, diff --git a/src/client/BullmqClientTransport.js b/src/client/BullmqClientTransport.js index ce8808b..35019df 100644 --- a/src/client/BullmqClientTransport.js +++ b/src/client/BullmqClientTransport.js @@ -4,7 +4,6 @@ module.exports = require("./ClientTransport").compose({ props: { Queue: require("bullmq").Queue, QueueEvents: require("bullmq").QueueEvents, - _timeout: 60000, _queue: null, _queueEvents: null, _jobsOptions: null, @@ -22,7 +21,6 @@ module.exports = require("./ClientTransport").compose({ retryStrategy: null, // only one attempt to connect }; } - this._timeout = timeout || this._timeout; this._queue = new this.Queue(queueName, { connection: connectionOptions }); this._queue.on("error", () => {}); // The only reason we subscribe is to avoid bullmq to print errors to console @@ -49,7 +47,7 @@ module.exports = require("./ClientTransport").compose({ try { await this._queue.waitUntilReady(); const job = await bullmq.queue.add(procedureName, bullmq.data, bullmq.jobsOptions); - return await job.waitUntilFinished(bullmq.queueEvents, this._timeout); + return await job.waitUntilFinished(bullmq.queueEvents, this.timeout); // this.timeout is a property of the parent ClientTransport } catch (err) { if (err.code === "ECONNREFUSED") err.noNetToServer = true; throw err; diff --git a/src/client/ClientTransport.js b/src/client/ClientTransport.js index 91d0a04..3eaf1e8 100644 --- a/src/client/ClientTransport.js +++ b/src/client/ClientTransport.js @@ -5,13 +5,16 @@ module.exports = require("stampit")({ props: { uri: null, + timeout: 60_000, }, - init({ uri }) { + init({ uri, timeout }) { if (!isFunction(this.introspect)) throw new Error("ClientTransport must implement introspect()"); if (!isFunction(this.call)) throw new Error("ClientTransport must implement call()"); this.uri = uri || this.uri; if (!isString(this.uri)) throw new Error("`uri` connection string is required"); + + this.timeout = timeout != null ? timeout : this.timeout; }, }); diff --git a/src/client/LambdaClientTransport.js b/src/client/LambdaClientTransport.js index 499f44c..764564b 100644 --- a/src/client/LambdaClientTransport.js +++ b/src/client/LambdaClientTransport.js @@ -25,10 +25,13 @@ module.exports = require("./ClientTransport").compose({ async call(ctx) { let invocationResponse; try { - invocationResponse = await this.awsSdkLambdaClient.invoke({ - FunctionName: this.uri.substring("lambda://".length), - Payload: JSON.stringify(ctx.arg), - }); + invocationResponse = await this.awsSdkLambdaClient.invoke( + { + FunctionName: this.uri.substring("lambda://".length), + Payload: JSON.stringify(ctx.arg), + }, + { requestTimeout: this.timeout } // this.timeout is a property of the parent ClientTransport + ); ctx.lambda.response = invocationResponse; } catch (e) { if (e.name.includes("ProviderError") || e.name.includes("NotFound")) e.noNetToServer = true; diff --git a/test/integration/integration.test.js b/test/integration/integration.test.js index 0764cf0..b8f7f47 100644 --- a/test/integration/integration.test.js +++ b/test/integration/integration.test.js @@ -416,7 +416,9 @@ describe("integration", function () { bullmqClient = AllserverClient({ uri: `bullmq://localhost:${port}` }); await callClientMethods(bullmqClient); - bullmqClient = AllserverClient({ transport: BullmqClientTransport({ uri: `redis://localhost:${port}` }) }); + bullmqClient = AllserverClient({ + transport: BullmqClientTransport({ uri: `redis://localhost:${port}` }), + }); await callClientMethods(bullmqClient); await bullmqServer.stop(); @@ -428,7 +430,10 @@ describe("integration", function () { const port = 6379; const bullmqServer = Allserver({ procedures, - transport: BullmqTransport({ queueName: "OtherName", connectionOptions: { host: "localhost", port } }), + transport: BullmqTransport({ + queueName: "OtherName", + connectionOptions: { host: "localhost", port }, + }), }); await bullmqServer.start(); @@ -447,7 +452,4 @@ describe("integration", function () { await bullmqServer.stop(); }); }); - { - timeout: 5000; - } }); From 88f087d9fea4918b679c8dda281ac5d002b825d0 Mon Sep 17 00:00:00 2001 From: vasyl Date: Fri, 31 Oct 2025 18:00:41 +1100 Subject: [PATCH 18/18] fix flaky tests --- example/{index.test.js => example.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename example/{index.test.js => example.js} (100%) diff --git a/example/index.test.js b/example/example.js similarity index 100% rename from example/index.test.js rename to example/example.js