From 2238df20acdff05251b6e99df87abdc8a7d3c6b0 Mon Sep 17 00:00:00 2001 From: denihs Date: Wed, 23 Nov 2022 20:02:23 -0400 Subject: [PATCH] Running dpp-sever tests without Fibers --- .../common/livedata_connection.js | 41 ++++++++----- .../livedata_server_async_tests.js | 10 +++- .../ddp-server-async/livedata_server_tests.js | 60 +++++++++++-------- packages/meteor/dynamics_nodejs.js | 9 +++ 4 files changed, 79 insertions(+), 41 deletions(-) diff --git a/packages/ddp-client-async/common/livedata_connection.js b/packages/ddp-client-async/common/livedata_connection.js index c30ff6f48d..1855a0e0f5 100644 --- a/packages/ddp-client-async/common/livedata_connection.js +++ b/packages/ddp-client-async/common/livedata_connection.js @@ -606,14 +606,12 @@ export class Connection { DDP._CurrentMethodInvocation._set(); DDP._CurrentMethodInvocation._setCallAsyncMethodRunning(true); return new Promise((resolve, reject) => { - this.applyAsync(name, args, { isFromCallAsync: true }, (err, result) => { - DDP._CurrentMethodInvocation._setCallAsyncMethodRunning(false); - if (err) { - reject(err); - return; - } - resolve(result); - }); + this.applyAsync(name, args, { isFromCallAsync: true }) + .then(result => { + DDP._CurrentMethodInvocation._setCallAsyncMethodRunning(false); + resolve(result); + }) + .catch(reject); }); } @@ -669,9 +667,8 @@ export class Connection { * @param {Boolean} options.noRetry (Client only) if true, don't send this method again on reload, simply call the callback an error with the error code 'invocation-failed'. * @param {Boolean} options.throwStubExceptions (Client only) If true, exceptions thrown by method stubs will be thrown instead of logged, and the method will not be invoked on the server. * @param {Boolean} options.returnStubValue (Client only) If true then in cases where we would have otherwise discarded the stub's return value and returned undefined, instead we go ahead and return it. Specifically, this is any time other than when (a) we are already inside a stub or (b) we are in Node and no callback was provided. Currently we require this flag to be explicitly passed to reduce the likelihood that stub return values will be confused with server return values; we may improve this in future. - * @param {Function} [asyncCallback] Optional callback. */ - async applyAsync(name, args, options, callback) { + async applyAsync(name, args, options) { const { stubInvocation, invocation, ...stubOptions } = this._stubCall(name, EJSON.clone(args), options); if (stubOptions.hasStub) { if ( @@ -703,7 +700,7 @@ export class Connection { stubOptions.exception = e; } } - return this._apply(name, stubOptions, args, options, callback); + return this._apply(name, stubOptions, args, options, null); } _apply(name, stubCallValue, args, options, callback) { @@ -803,8 +800,24 @@ export class Connection { } else { // On the server, make the function synchronous. Throw on // errors, return on success. - future = new Future(); - callback = future.resolver(); + // TODO fibers: before this was a future, now it's a promise. + // Do more tests around this. + + if (!options.isFromCallAsync) { + throw new Error("Can't create a future for Meteor.call()"); + } + + future = new Promise((resolve, reject) => { + callback = (...allArgs) => { + let args = Array.from(allArgs); + let err = args.shift(); + if (err) { + reject(err); + return; + } + resolve(...args); + } + }); } } @@ -849,7 +862,7 @@ export class Connection { // If we're using the default callback on the server, // block waiting for the result. if (future) { - return future.wait(); + return future; } return options.returnStubValue ? stubReturnValue : undefined; } diff --git a/packages/ddp-server-async/livedata_server_async_tests.js b/packages/ddp-server-async/livedata_server_async_tests.js index d145aeee92..ca00cc338b 100644 --- a/packages/ddp-server-async/livedata_server_async_tests.js +++ b/packages/ddp-server-async/livedata_server_async_tests.js @@ -23,6 +23,8 @@ Meteor.publish('livedata_server_test_sub_context_async', async function( var methodInvocation = DDP._CurrentMethodInvocation.get(); var publicationInvocation = DDP._CurrentPublicationInvocation.get(); +// console.log('methodInvocation', methodInvocation); +// console.log('publicationInvocation', !!publicationInvocation); // Check the publish function's environment variables and context. if (callback) { callback.call(this, methodInvocation, publicationInvocation); @@ -33,6 +35,12 @@ Meteor.publish('livedata_server_test_sub_context_async', async function( this.onStop(function() { var onStopMethodInvocation = DDP._CurrentMethodInvocation.get(); var onStopPublicationInvocation = DDP._CurrentPublicationInvocation.get(); + // console.log('onStopMethodInvocation', onStopMethodInvocation); + + + console.log('onStopPublicationInvocation', !!onStopPublicationInvocation, this.userId); + + callback.call( this, onStopMethodInvocation, @@ -45,7 +53,7 @@ Meteor.publish('livedata_server_test_sub_context_async', async function( this.stop(); } else { this.ready(); - Meteor.call('livedata_server_test_setuserid', userId); + await Meteor.callAsync('livedata_server_test_setuserid', userId); } }); diff --git a/packages/ddp-server-async/livedata_server_tests.js b/packages/ddp-server-async/livedata_server_tests.js index cde56b6196..0e7c970b12 100644 --- a/packages/ddp-server-async/livedata_server_tests.js +++ b/packages/ddp-server-async/livedata_server_tests.js @@ -87,8 +87,8 @@ Meteor.methods({ return this.connection && this.connection.id; }, - livedata_server_test_outer: function () { - return Meteor.call('livedata_server_test_inner'); + livedata_server_test_outer: async function () { + return await Meteor.callAsync('livedata_server_test_inner'); }, livedata_server_test_setuserid: function (userId) { @@ -108,12 +108,17 @@ Tinytest.addAsync( }); makeTestConnection( - test, - function (clientConn, serverConn) { - clientConn.call('livedata_server_test_inner'); - clientConn.disconnect(); - }, - onComplete + test, + function(clientConn, serverConn) { + clientConn + .callAsync('livedata_server_test_inner') + .then(() => clientConn.disconnect()) + .catch(e => { + onComplete(); + throw new Meteor.Error(e); + }); + }, + onComplete ); } ); @@ -125,10 +130,11 @@ Tinytest.addAsync( makeTestConnection( test, function (clientConn, serverConn) { - var res = clientConn.call('livedata_server_test_inner'); - test.equal(res, serverConn.id); - clientConn.disconnect(); - onComplete(); + clientConn.callAsync('livedata_server_test_inner').then(res => { + test.equal(res, serverConn.id); + clientConn.disconnect(); + onComplete(); + }); }, onComplete ); @@ -142,10 +148,11 @@ Tinytest.addAsync( makeTestConnection( test, function (clientConn, serverConn) { - var res = clientConn.call('livedata_server_test_outer'); - test.equal(res, serverConn.id); - clientConn.disconnect(); - onComplete(); + clientConn.callAsync('livedata_server_test_outer').then(res => { + test.equal(res, serverConn.id); + clientConn.disconnect(); + onComplete(); + }); }, onComplete ); @@ -163,10 +170,10 @@ Meteor.publish("livedata_server_test_sub", function (connectionId) { this.stop(); }); -Meteor.publish("livedata_server_test_sub_method", function (connectionId) { +Meteor.publish("livedata_server_test_sub_method", async function (connectionId) { var callback = onSubscription[connectionId]; if (callback) { - var id = Meteor.call('livedata_server_test_inner'); + var id = await Meteor.callAsync('livedata_server_test_inner'); callback(id); } this.stop(); @@ -311,13 +318,13 @@ Tinytest.addAsync( ); Meteor.methods({ - testResolvedPromise(arg) { - const invocation1 = DDP._CurrentMethodInvocation.get(); + async testResolvedPromise(arg) { + const invocationRunningFromCallAsync1 = DDP._CurrentMethodInvocation._isCallAsyncMethodRunning(); return Promise.resolve(arg).then(result => { - const invocation2 = DDP._CurrentMethodInvocation.get(); - // This equality holds because Promise callbacks are bound to the - // dynamic environment where .then was called. - if (invocation1 !== invocation2) { + const invocationRunningFromCallAsync2 = DDP._CurrentMethodInvocation._isCallAsyncMethodRunning(); + // What matters here is that both invocations are coming from the same call, + // so both of them can be considered a simulation. + if (invocationRunningFromCallAsync1 !== invocationRunningFromCallAsync2) { throw new Meteor.Error("invocation mismatch"); } return result + " after waiting"; @@ -344,9 +351,10 @@ Meteor.methods({ Tinytest.addAsync( "livedata server - waiting for Promise", - (test, onComplete) => makeTestConnection(test, (clientConn, serverConn) => { + (test, onComplete) => makeTestConnection(test, async (clientConn, serverConn) => { + const testResolvedPromiseResult = await clientConn.callAsync("testResolvedPromise", "clientConn.call"); test.equal( - clientConn.call("testResolvedPromise", "clientConn.call"), + testResolvedPromiseResult, "clientConn.call after waiting" ); diff --git a/packages/meteor/dynamics_nodejs.js b/packages/meteor/dynamics_nodejs.js index 2a278acd5e..0d8ba74727 100644 --- a/packages/meteor/dynamics_nodejs.js +++ b/packages/meteor/dynamics_nodejs.js @@ -3,6 +3,7 @@ var Fiber = Meteor._isFibersEnabled && Npm.require('fibers'); let nextSlot = 0; +let callAsyncMethodRunning = false; Meteor._nodeCodeMustBeInFiber = function () { if (!Fiber.current) { @@ -111,6 +112,14 @@ class EnvironmentVariableAsync { this._set(value); return saved; } + + _isCallAsyncMethodRunning() { + return callAsyncMethodRunning; + } + + _setCallAsyncMethodRunning(value) { + callAsyncMethodRunning = value; + } } /**