* Add a failing test for issue #7340 If any delay occurs after "message.event" assignation in LiveQueryServer._onAfterSave, the next subscription or request with a different event might overwrite it, and by that using the wrong "push" function name. * Remove updade of message and use res.event instead This prevent computing function name from a incorrect event if multiple subscriptions override one by one the message.event. * Update CHANGELOG.md * Replace setTimeout by async/await expressions
This commit is contained in:
committed by
GitHub
parent
45d00cee60
commit
87dcd23b6a
@@ -9,6 +9,8 @@ const queryHashValue = 'hash';
|
||||
const testUserId = 'userId';
|
||||
const testClassName = 'TestObject';
|
||||
|
||||
const timeout = () => jasmine.timeout(100);
|
||||
|
||||
describe('ParseLiveQueryServer', function () {
|
||||
beforeEach(function (done) {
|
||||
// Mock ParseWebSocketServer
|
||||
@@ -750,10 +752,10 @@ describe('ParseLiveQueryServer', function () {
|
||||
|
||||
// Make sure we send command to client, since _matchesACL is async, we have to
|
||||
// wait and check
|
||||
setTimeout(function () {
|
||||
expect(client.pushDelete).toHaveBeenCalled();
|
||||
done();
|
||||
}, jasmine.ASYNC_TEST_WAIT_TIME);
|
||||
await timeout();
|
||||
|
||||
expect(client.pushDelete).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
|
||||
it('has no subscription and can handle object save command', async () => {
|
||||
@@ -785,14 +787,14 @@ describe('ParseLiveQueryServer', function () {
|
||||
parseLiveQueryServer._onAfterSave(message);
|
||||
|
||||
// Make sure we do not send command to client
|
||||
setTimeout(function () {
|
||||
expect(client.pushCreate).not.toHaveBeenCalled();
|
||||
expect(client.pushEnter).not.toHaveBeenCalled();
|
||||
expect(client.pushUpdate).not.toHaveBeenCalled();
|
||||
expect(client.pushDelete).not.toHaveBeenCalled();
|
||||
expect(client.pushLeave).not.toHaveBeenCalled();
|
||||
done();
|
||||
}, jasmine.ASYNC_TEST_WAIT_TIME);
|
||||
await timeout();
|
||||
|
||||
expect(client.pushCreate).not.toHaveBeenCalled();
|
||||
expect(client.pushEnter).not.toHaveBeenCalled();
|
||||
expect(client.pushUpdate).not.toHaveBeenCalled();
|
||||
expect(client.pushDelete).not.toHaveBeenCalled();
|
||||
expect(client.pushLeave).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
|
||||
it('can handle object enter command which matches some subscriptions', async done => {
|
||||
@@ -822,14 +824,14 @@ describe('ParseLiveQueryServer', function () {
|
||||
parseLiveQueryServer._onAfterSave(message);
|
||||
|
||||
// Make sure we send enter command to client
|
||||
setTimeout(function () {
|
||||
expect(client.pushCreate).not.toHaveBeenCalled();
|
||||
expect(client.pushEnter).toHaveBeenCalled();
|
||||
expect(client.pushUpdate).not.toHaveBeenCalled();
|
||||
expect(client.pushDelete).not.toHaveBeenCalled();
|
||||
expect(client.pushLeave).not.toHaveBeenCalled();
|
||||
done();
|
||||
}, jasmine.ASYNC_TEST_WAIT_TIME);
|
||||
await timeout();
|
||||
|
||||
expect(client.pushCreate).not.toHaveBeenCalled();
|
||||
expect(client.pushEnter).toHaveBeenCalled();
|
||||
expect(client.pushUpdate).not.toHaveBeenCalled();
|
||||
expect(client.pushDelete).not.toHaveBeenCalled();
|
||||
expect(client.pushLeave).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
|
||||
it('can handle object update command which matches some subscriptions', async done => {
|
||||
@@ -855,14 +857,14 @@ describe('ParseLiveQueryServer', function () {
|
||||
parseLiveQueryServer._onAfterSave(message);
|
||||
|
||||
// Make sure we send update command to client
|
||||
setTimeout(function () {
|
||||
expect(client.pushCreate).not.toHaveBeenCalled();
|
||||
expect(client.pushEnter).not.toHaveBeenCalled();
|
||||
expect(client.pushUpdate).toHaveBeenCalled();
|
||||
expect(client.pushDelete).not.toHaveBeenCalled();
|
||||
expect(client.pushLeave).not.toHaveBeenCalled();
|
||||
done();
|
||||
}, jasmine.ASYNC_TEST_WAIT_TIME);
|
||||
await timeout();
|
||||
|
||||
expect(client.pushCreate).not.toHaveBeenCalled();
|
||||
expect(client.pushEnter).not.toHaveBeenCalled();
|
||||
expect(client.pushUpdate).toHaveBeenCalled();
|
||||
expect(client.pushDelete).not.toHaveBeenCalled();
|
||||
expect(client.pushLeave).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
|
||||
it('can handle object leave command which matches some subscriptions', async done => {
|
||||
@@ -892,14 +894,81 @@ describe('ParseLiveQueryServer', function () {
|
||||
parseLiveQueryServer._onAfterSave(message);
|
||||
|
||||
// Make sure we send leave command to client
|
||||
setTimeout(function () {
|
||||
expect(client.pushCreate).not.toHaveBeenCalled();
|
||||
expect(client.pushEnter).not.toHaveBeenCalled();
|
||||
expect(client.pushUpdate).not.toHaveBeenCalled();
|
||||
expect(client.pushDelete).not.toHaveBeenCalled();
|
||||
expect(client.pushLeave).toHaveBeenCalled();
|
||||
done();
|
||||
}, jasmine.ASYNC_TEST_WAIT_TIME);
|
||||
await timeout();
|
||||
|
||||
expect(client.pushCreate).not.toHaveBeenCalled();
|
||||
expect(client.pushEnter).not.toHaveBeenCalled();
|
||||
expect(client.pushUpdate).not.toHaveBeenCalled();
|
||||
expect(client.pushDelete).not.toHaveBeenCalled();
|
||||
expect(client.pushLeave).toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
|
||||
it('sends correct events for object with multiple subscriptions', async done => {
|
||||
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
||||
|
||||
Parse.Cloud.afterLiveQueryEvent('TestObject', () => {
|
||||
// Simulate delay due to trigger, auth, etc.
|
||||
return jasmine.timeout(10);
|
||||
});
|
||||
|
||||
// Make mock request message
|
||||
const message = generateMockMessage(true);
|
||||
// Add mock client
|
||||
const clientId = 1;
|
||||
const client = addMockClient(parseLiveQueryServer, clientId);
|
||||
client.sessionToken = 'sessionToken';
|
||||
|
||||
// Mock queryHash for this special test
|
||||
const mockQueryHash = jasmine.createSpy('matchesQuery').and.returnValue('hash1');
|
||||
jasmine.mockLibrary('../lib/LiveQuery/QueryTools', 'queryHash', mockQueryHash);
|
||||
// Add mock subscription 1
|
||||
const requestId2 = 2;
|
||||
await addMockSubscription(parseLiveQueryServer, clientId, requestId2, null, null, 'hash1');
|
||||
|
||||
// Mock queryHash for this special test
|
||||
const mockQueryHash2 = jasmine.createSpy('matchesQuery').and.returnValue('hash2');
|
||||
jasmine.mockLibrary('../lib/LiveQuery/QueryTools', 'queryHash', mockQueryHash2);
|
||||
// Add mock subscription 2
|
||||
const requestId3 = 3;
|
||||
await addMockSubscription(parseLiveQueryServer, clientId, requestId3, null, null, 'hash2');
|
||||
// Mock _matchesSubscription to return matching
|
||||
// In order to mimic a leave, then enter, we need original match return true
|
||||
// and the current match return false, then the other way around
|
||||
let counter = 0;
|
||||
parseLiveQueryServer._matchesSubscription = function (parseObject) {
|
||||
if (!parseObject) {
|
||||
return false;
|
||||
}
|
||||
counter += 1;
|
||||
// true, false, false, true
|
||||
return counter < 2 || counter > 3;
|
||||
};
|
||||
parseLiveQueryServer._matchesACL = function () {
|
||||
// Simulate call
|
||||
return jasmine.timeout(10).then(() => true);
|
||||
};
|
||||
parseLiveQueryServer._onAfterSave(message);
|
||||
|
||||
// Make sure we send leave and enter command to client
|
||||
await timeout();
|
||||
|
||||
expect(client.pushCreate).not.toHaveBeenCalled();
|
||||
expect(client.pushEnter).toHaveBeenCalledTimes(1);
|
||||
expect(client.pushEnter).toHaveBeenCalledWith(
|
||||
requestId3,
|
||||
{ key: 'value', className: 'TestObject' },
|
||||
{ key: 'originalValue', className: 'TestObject' }
|
||||
);
|
||||
expect(client.pushUpdate).not.toHaveBeenCalled();
|
||||
expect(client.pushDelete).not.toHaveBeenCalled();
|
||||
expect(client.pushLeave).toHaveBeenCalledTimes(1);
|
||||
expect(client.pushLeave).toHaveBeenCalledWith(
|
||||
requestId2,
|
||||
{ key: 'value', className: 'TestObject' },
|
||||
{ key: 'originalValue', className: 'TestObject' }
|
||||
);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can handle update command with original object', async done => {
|
||||
@@ -936,15 +1005,15 @@ describe('ParseLiveQueryServer', function () {
|
||||
parseLiveQueryServer._onAfterSave(message);
|
||||
|
||||
// Make sure we send update command to client
|
||||
setTimeout(function () {
|
||||
expect(client.pushUpdate).toHaveBeenCalled();
|
||||
const args = parseWebSocket.send.calls.mostRecent().args;
|
||||
const toSend = JSON.parse(args[0]);
|
||||
await timeout();
|
||||
|
||||
expect(toSend.object).toBeDefined();
|
||||
expect(toSend.original).toBeDefined();
|
||||
done();
|
||||
}, jasmine.ASYNC_TEST_WAIT_TIME);
|
||||
expect(client.pushUpdate).toHaveBeenCalled();
|
||||
const args = parseWebSocket.send.calls.mostRecent().args;
|
||||
const toSend = JSON.parse(args[0]);
|
||||
|
||||
expect(toSend.object).toBeDefined();
|
||||
expect(toSend.original).toBeDefined();
|
||||
done();
|
||||
});
|
||||
|
||||
it('can handle object create command which matches some subscriptions', async done => {
|
||||
@@ -970,14 +1039,14 @@ describe('ParseLiveQueryServer', function () {
|
||||
parseLiveQueryServer._onAfterSave(message);
|
||||
|
||||
// Make sure we send create command to client
|
||||
setTimeout(function () {
|
||||
expect(client.pushCreate).toHaveBeenCalled();
|
||||
expect(client.pushEnter).not.toHaveBeenCalled();
|
||||
expect(client.pushUpdate).not.toHaveBeenCalled();
|
||||
expect(client.pushDelete).not.toHaveBeenCalled();
|
||||
expect(client.pushLeave).not.toHaveBeenCalled();
|
||||
done();
|
||||
}, jasmine.ASYNC_TEST_WAIT_TIME);
|
||||
await timeout();
|
||||
|
||||
expect(client.pushCreate).toHaveBeenCalled();
|
||||
expect(client.pushEnter).not.toHaveBeenCalled();
|
||||
expect(client.pushUpdate).not.toHaveBeenCalled();
|
||||
expect(client.pushDelete).not.toHaveBeenCalled();
|
||||
expect(client.pushLeave).not.toHaveBeenCalled();
|
||||
done();
|
||||
});
|
||||
|
||||
it('can handle create command with fields', async done => {
|
||||
@@ -1020,14 +1089,14 @@ describe('ParseLiveQueryServer', function () {
|
||||
parseLiveQueryServer._onAfterSave(message);
|
||||
|
||||
// Make sure we send create command to client
|
||||
setTimeout(function () {
|
||||
expect(client.pushCreate).toHaveBeenCalled();
|
||||
const args = parseWebSocket.send.calls.mostRecent().args;
|
||||
const toSend = JSON.parse(args[0]);
|
||||
expect(toSend.object).toBeDefined();
|
||||
expect(toSend.original).toBeUndefined();
|
||||
done();
|
||||
}, jasmine.ASYNC_TEST_WAIT_TIME);
|
||||
await timeout();
|
||||
|
||||
expect(client.pushCreate).toHaveBeenCalled();
|
||||
const args = parseWebSocket.send.calls.mostRecent().args;
|
||||
const toSend = JSON.parse(args[0]);
|
||||
expect(toSend.object).toBeDefined();
|
||||
expect(toSend.original).toBeUndefined();
|
||||
done();
|
||||
});
|
||||
|
||||
it('can match subscription for null or undefined parse object', function () {
|
||||
@@ -1737,7 +1806,8 @@ describe('ParseLiveQueryServer', function () {
|
||||
clientId,
|
||||
requestId,
|
||||
parseWebSocket,
|
||||
query
|
||||
query,
|
||||
customQueryHashValue
|
||||
) {
|
||||
// If parseWebSocket is null, we use the default one
|
||||
if (!parseWebSocket) {
|
||||
@@ -1765,12 +1835,12 @@ describe('ParseLiveQueryServer', function () {
|
||||
// Make mock subscription
|
||||
const subscription = parseLiveQueryServer.subscriptions
|
||||
.get(query.className)
|
||||
.get(queryHashValue);
|
||||
.get(customQueryHashValue || queryHashValue);
|
||||
subscription.hasSubscribingClient = function () {
|
||||
return false;
|
||||
};
|
||||
subscription.className = query.className;
|
||||
subscription.hash = queryHashValue;
|
||||
subscription.hash = customQueryHashValue || queryHashValue;
|
||||
if (subscription.clientRequestIds && subscription.clientRequestIds.has(clientId)) {
|
||||
subscription.clientRequestIds.get(clientId).push(requestId);
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user