diff --git a/packages/ddp-client/package.js b/packages/ddp-client/package.js index b5a729e39d..a46d5ef164 100644 --- a/packages/ddp-client/package.js +++ b/packages/ddp-client/package.js @@ -60,5 +60,7 @@ Package.onTest((api) => { api.addFiles('test/livedata_tests.js'); api.addFiles('test/livedata_test_service.js'); api.addFiles('test/random_stream_tests.js'); + api.addFiles('test/async_stubs/client.js', 'client'); + api.addFiles('test/async_stubs/server_setup.js', 'server'); api.addFiles('test/livedata_callAsync_tests.js'); }); diff --git a/packages/ddp-client/test/async_stubs/client.js b/packages/ddp-client/test/async_stubs/client.js new file mode 100644 index 0000000000..0cb627204d --- /dev/null +++ b/packages/ddp-client/test/async_stubs/client.js @@ -0,0 +1,225 @@ + +let events = []; +Meteor.methods({ + 'sync-stub'() { + events.push('sync-stub'); + return 'sync-stub-result' + }, + async 'async-stub'() { + events.push('start async-stub'); + await 0; + events.push('end async-stub'); + return 'async-stub-result' + }, + callAsyncFromSyncStub() { + events.push('callAsyncFromSyncStub'); + Meteor.callAsync('async-stub'); + }, + async callSyncStubFromAsyncStub() { + events.push('start callSyncStubFromAsyncStub'); + await 0 + let result = Meteor.call('sync-stub'); + events.push('end callSyncStubFromAsyncStub'); + return result; + }, + callSyncStubFromSyncStub() { + events.push('callSyncStubFromSyncStub'); + return Meteor.call('sync-stub'); + }, + callAsyncStubFromAsyncStub() { + events.push('callAsyncStubFromAsyncStub'); + return Meteor.callAsync('async-stub'); + } +}); + +Tinytest.addAsync('applyAsync - server only', async function (test) { + let serverResolver; + let serverPromise = new Promise((resolve) => { + serverResolver = resolve; + }); + + let stubResult = await Meteor.applyAsync('server-only-sync', [], { returnStubValue: true }, (err, result) => { + console.log(err); + if (!err) { + serverResolver(result); + } + }); + + let serverResult = await serverPromise; + + test.equal(stubResult, undefined); + test.equal(serverResult, 'sync-result'); +}); + +Tinytest.addAsync('applyAsync - sync stub', async function (test) { + let serverResolver; + let serverPromise = new Promise((resolve) => { + serverResolver = resolve; + }); + + let stubResult = await Meteor.applyAsync('sync-stub', [], { + returnStubValue: true + }, (err, result) => { + console.log(err); + if (!err) { + serverResolver(result); + } + }); + + let serverResult = await serverPromise; + + test.equal(stubResult, 'sync-stub-result'); + test.equal(serverResult, 'sync-server-result'); +}); + +Tinytest.addAsync('applyAsync - callAsync', async function (test) { + let serverResult = await Meteor.callAsync('async-stub'); + + test.equal(serverResult, 'async-server-result'); +}); + +Tinytest.addAsync('applyAsync - callAsync twice', async function (test) { + events = []; + let promise1 = Meteor.callAsync('async-stub'); + let promise2 = Meteor.callAsync('async-stub'); + + let results = await Promise.all([promise1, promise2]); + + test.equal(events, ['start async-stub', 'end async-stub', 'start async-stub', 'end async-stub']); + test.equal(results, ['async-server-result', 'async-server-result']); +}); + +// Broken in Meteor 2.13: https://github.com/meteor/meteor/issues/12889#issue-1998128607 +Tinytest.addAsync('applyAsync - callAsync from async stub', async function (test) { + await Meteor.callAsync('getAndResetEvents'); + events = []; + let serverResolver; + let serverPromise = new Promise((resolve) => { + serverResolver = resolve; + }); + let stubResult = await Meteor.applyAsync('callAsyncStubFromAsyncStub', [], { returnStubValue: true }, (err, result) => { + if (!err) { + serverResolver(result); + } + }); + let serverResult = await serverPromise; + + let serverEvents = await Meteor.callAsync('getAndResetEvents'); + + test.equal(stubResult, 'async-stub-result'); + test.equal(serverResult, 'server result'); + test.equal(events, ['callAsyncStubFromAsyncStub', 'start async-stub', 'end async-stub']); + test.equal(serverEvents, ['callAsyncStubFromAsyncStub']); +}); + + +Tinytest.addAsync('applyAsync - callAsync in then', async function (test) { + await Meteor.callAsync('getAndResetEvents'); + + events = []; + let result = await Meteor.callAsync('async-stub').then(() => Meteor.callAsync('async-stub')); + let serverEvents = await Meteor.callAsync('getAndResetEvents'); + + test.equal(events, ['start async-stub', 'end async-stub', 'start async-stub', 'end async-stub']); + test.equal(serverEvents, ['async-stub', 'async-stub']); + test.equal(result, 'async-server-result'); +}); + +Tinytest.addAsync('applyAsync - call from async stub', async function (test) { + await Meteor.callAsync('getAndResetEvents'); + events = []; + let serverResolver; + let serverPromise = new Promise((resolve) => { + serverResolver = resolve; + }); + let stubResult = await Meteor.applyAsync('callSyncStubFromAsyncStub', [], { returnStubValue: true }, (err, result) => { + if (!err) { + serverResolver(result); + } + }); + let serverResult = await serverPromise; + + let serverEvents = await Meteor.callAsync('getAndResetEvents'); + + test.equal(stubResult, 'sync-stub-result'); + test.equal(serverResult, 'server result'); + test.equal(events, ['start callSyncStubFromAsyncStub', 'sync-stub', 'end callSyncStubFromAsyncStub']); + test.equal(serverEvents, ['callSyncStubFromAsyncStub']); +}); + +Tinytest.addAsync('apply - call from sync stub', async function (test) { + await Meteor.callAsync('getAndResetEvents'); + events = []; + let serverResolver; + let serverPromise = new Promise((resolve) => { + serverResolver = resolve; + }); + let stubResult = Meteor.apply('callSyncStubFromSyncStub', [], { returnStubValue: true }, (err, result) => { + if (!err) { + serverResolver(result); + } + }); + let serverResult = await serverPromise; + + let serverEvents = await Meteor.callAsync('getAndResetEvents'); + + test.equal(stubResult, 'sync-stub-result'); + test.equal(serverResult, 'server result'); + test.equal(events, ['callSyncStubFromSyncStub', 'sync-stub']); + test.equal(serverEvents, ['callSyncStubFromSyncStub']); +}); + +Tinytest.addAsync('apply - proper order with applyAsync', async function (test) { + await Meteor.callAsync('getAndResetEvents'); + events = []; + let serverResolver; + let serverPromise = new Promise((resolve) => { + serverResolver = resolve; + }); + + let promise1 = Meteor.callAsync('callSyncStubFromAsyncStub'); + let stubResult = Meteor.apply('callSyncStubFromSyncStub', [], { returnStubValue: true }, (err, result) => { + if (!err) { + serverResolver(result); + } + }); + let promise2 = Meteor.callAsync('server-only-sync'); + let [ + serverResult, + result1, + result2 + ] = await Promise.all([serverPromise, promise1, promise2]); + + let serverEvents = await Meteor.callAsync('getAndResetEvents'); + + test.equal(stubResult, 'sync-stub-result'); + test.equal(serverResult, 'server result'); + test.equal(result1, 'server result'); + test.equal(result2, 'sync-result'); + test.equal(events, ['callSyncStubFromSyncStub', 'sync-stub', 'start callSyncStubFromAsyncStub', 'sync-stub', 'end callSyncStubFromAsyncStub']); + test.equal(serverEvents, ['callSyncStubFromAsyncStub', 'callSyncStubFromSyncStub', 'server-only-sync']); +}); + +Tinytest.addAsync('apply - wait', async function (test) { + await Meteor.callAsync('getAndResetEvents'); + events = []; + let serverResolver; + let serverPromise = new Promise((resolve) => { + serverResolver = resolve; + }); + + let stubResult = Meteor.apply( + 'callSyncStubFromSyncStub', + [], + { returnStubValue: true, wait: true }, + (err, result) => { + if (!err) { + serverResolver(result); + } + }); + + const serverResult = await serverPromise; + + test.equal(stubResult, 'sync-stub-result'); + test.equal(serverResult, 'server result'); +}); diff --git a/packages/ddp-client/test/async_stubs/server_setup.js b/packages/ddp-client/test/async_stubs/server_setup.js new file mode 100644 index 0000000000..065b725182 --- /dev/null +++ b/packages/ddp-client/test/async_stubs/server_setup.js @@ -0,0 +1,44 @@ +let events = []; + +Meteor.methods({ + getAndResetEvents() { + let oldEvents = events; + events = []; + + return oldEvents; + }, + 'server-only-sync' () { + events.push('server-only-sync'); + return 'sync-result'; + }, + async 'server-only-async' () { + events.push('server-only-async'); + await 0 + return 'server-only-async-result'; + }, + 'sync-stub' () { + events.push('sync-stub'); + return 'sync-server-result' + }, + 'async-stub' () { + events.push('async-stub'); + return 'async-server-result' + }, + 'callAsyncFromSyncStub'() { + events.push('callAsyncFromSyncStub'); + }, + 'callSyncStubFromAsyncStub'() { + events.push('callSyncStubFromAsyncStub'); + + return 'server result'; + }, + 'callSyncStubFromSyncStub'() { + events.push('callSyncStubFromSyncStub'); + return 'server result'; + }, + 'callAsyncStubFromAsyncStub'() { + events.push('callAsyncStubFromAsyncStub'); + + return 'server result'; + } +});