Throws an error and prevents LiveQuery from reconnecting. Fixes Monitoring installationId. Allow installationId to be sent to and from client.
2056 lines
65 KiB
JavaScript
2056 lines
65 KiB
JavaScript
const Parse = require('parse/node');
|
|
const ParseLiveQueryServer = require('../lib/LiveQuery/ParseLiveQueryServer')
|
|
.ParseLiveQueryServer;
|
|
const ParseServer = require('../lib/ParseServer').default;
|
|
const LiveQueryController = require('../lib/Controllers/LiveQueryController')
|
|
.LiveQueryController;
|
|
const auth = require('../lib/Auth');
|
|
|
|
// Global mock info
|
|
const queryHashValue = 'hash';
|
|
const testUserId = 'userId';
|
|
const testClassName = 'TestObject';
|
|
|
|
describe('ParseLiveQueryServer', function() {
|
|
beforeEach(function(done) {
|
|
// Mock ParseWebSocketServer
|
|
const mockParseWebSocketServer = jasmine.createSpy('ParseWebSocketServer');
|
|
jasmine.mockLibrary(
|
|
'../lib/LiveQuery/ParseWebSocketServer',
|
|
'ParseWebSocketServer',
|
|
mockParseWebSocketServer
|
|
);
|
|
// Mock Client
|
|
const mockClient = function(id, socket, hasMasterKey) {
|
|
this.pushConnect = jasmine.createSpy('pushConnect');
|
|
this.pushSubscribe = jasmine.createSpy('pushSubscribe');
|
|
this.pushUnsubscribe = jasmine.createSpy('pushUnsubscribe');
|
|
this.pushDelete = jasmine.createSpy('pushDelete');
|
|
this.pushCreate = jasmine.createSpy('pushCreate');
|
|
this.pushEnter = jasmine.createSpy('pushEnter');
|
|
this.pushUpdate = jasmine.createSpy('pushUpdate');
|
|
this.pushLeave = jasmine.createSpy('pushLeave');
|
|
this.addSubscriptionInfo = jasmine.createSpy('addSubscriptionInfo');
|
|
this.getSubscriptionInfo = jasmine.createSpy('getSubscriptionInfo');
|
|
this.deleteSubscriptionInfo = jasmine.createSpy('deleteSubscriptionInfo');
|
|
this.hasMasterKey = hasMasterKey;
|
|
};
|
|
mockClient.pushError = jasmine.createSpy('pushError');
|
|
jasmine.mockLibrary('../lib/LiveQuery/Client', 'Client', mockClient);
|
|
// Mock Subscription
|
|
const mockSubscriotion = function() {
|
|
this.addClientSubscription = jasmine.createSpy('addClientSubscription');
|
|
this.deleteClientSubscription = jasmine.createSpy(
|
|
'deleteClientSubscription'
|
|
);
|
|
};
|
|
jasmine.mockLibrary(
|
|
'../lib/LiveQuery/Subscription',
|
|
'Subscription',
|
|
mockSubscriotion
|
|
);
|
|
// Mock queryHash
|
|
const mockQueryHash = jasmine
|
|
.createSpy('matchesQuery')
|
|
.and.returnValue(queryHashValue);
|
|
jasmine.mockLibrary(
|
|
'../lib/LiveQuery/QueryTools',
|
|
'queryHash',
|
|
mockQueryHash
|
|
);
|
|
// Mock matchesQuery
|
|
const mockMatchesQuery = jasmine
|
|
.createSpy('matchesQuery')
|
|
.and.returnValue(true);
|
|
jasmine.mockLibrary(
|
|
'../lib/LiveQuery/QueryTools',
|
|
'matchesQuery',
|
|
mockMatchesQuery
|
|
);
|
|
// Mock ParsePubSub
|
|
const mockParsePubSub = {
|
|
createPublisher: function() {
|
|
return {
|
|
publish: jasmine.createSpy('publish'),
|
|
on: jasmine.createSpy('on'),
|
|
};
|
|
},
|
|
createSubscriber: function() {
|
|
return {
|
|
subscribe: jasmine.createSpy('subscribe'),
|
|
on: jasmine.createSpy('on'),
|
|
};
|
|
},
|
|
};
|
|
jasmine.mockLibrary(
|
|
'../lib/LiveQuery/ParsePubSub',
|
|
'ParsePubSub',
|
|
mockParsePubSub
|
|
);
|
|
spyOn(auth, 'getAuthForSessionToken').and.callFake(
|
|
({ sessionToken, cacheController }) => {
|
|
if (typeof sessionToken === 'undefined') {
|
|
return Promise.reject();
|
|
}
|
|
if (sessionToken === null) {
|
|
return Promise.reject();
|
|
}
|
|
if (sessionToken === 'pleaseThrow') {
|
|
return Promise.reject();
|
|
}
|
|
if (sessionToken === 'invalid') {
|
|
return Promise.reject(
|
|
new Parse.Error(
|
|
Parse.Error.INVALID_SESSION_TOKEN,
|
|
'invalid session token'
|
|
)
|
|
);
|
|
}
|
|
return Promise.resolve(
|
|
new auth.Auth({ cacheController, user: { id: testUserId } })
|
|
);
|
|
}
|
|
);
|
|
done();
|
|
});
|
|
|
|
it('can be initialized', function() {
|
|
const httpServer = {};
|
|
const parseLiveQueryServer = new ParseLiveQueryServer(httpServer);
|
|
|
|
expect(parseLiveQueryServer.clientId).toBeUndefined();
|
|
expect(parseLiveQueryServer.clients.size).toBe(0);
|
|
expect(parseLiveQueryServer.subscriptions.size).toBe(0);
|
|
});
|
|
|
|
it('can be initialized from ParseServer', function() {
|
|
const httpServer = {};
|
|
const parseLiveQueryServer = ParseServer.createLiveQueryServer(
|
|
httpServer,
|
|
{}
|
|
);
|
|
|
|
expect(parseLiveQueryServer.clientId).toBeUndefined();
|
|
expect(parseLiveQueryServer.clients.size).toBe(0);
|
|
expect(parseLiveQueryServer.subscriptions.size).toBe(0);
|
|
});
|
|
|
|
it('can be initialized from ParseServer without httpServer', function(done) {
|
|
const parseLiveQueryServer = ParseServer.createLiveQueryServer(undefined, {
|
|
port: 22345,
|
|
});
|
|
|
|
expect(parseLiveQueryServer.clientId).toBeUndefined();
|
|
expect(parseLiveQueryServer.clients.size).toBe(0);
|
|
expect(parseLiveQueryServer.subscriptions.size).toBe(0);
|
|
parseLiveQueryServer.server.close(done);
|
|
});
|
|
|
|
describe_only_db('mongo')('initialization', () => {
|
|
it('can be initialized through ParseServer without liveQueryServerOptions', function(done) {
|
|
const parseServer = ParseServer.start({
|
|
appId: 'hello',
|
|
masterKey: 'world',
|
|
port: 22345,
|
|
mountPath: '/1',
|
|
serverURL: 'http://localhost:12345/1',
|
|
liveQuery: {
|
|
classNames: ['Yolo'],
|
|
},
|
|
startLiveQueryServer: true,
|
|
serverStartComplete: () => {
|
|
expect(parseServer.liveQueryServer).not.toBeUndefined();
|
|
expect(parseServer.liveQueryServer.server).toBe(parseServer.server);
|
|
parseServer.server.close(done);
|
|
},
|
|
});
|
|
});
|
|
|
|
it('can be initialized through ParseServer with liveQueryServerOptions', function(done) {
|
|
const parseServer = ParseServer.start({
|
|
appId: 'hello',
|
|
masterKey: 'world',
|
|
port: 22346,
|
|
mountPath: '/1',
|
|
serverURL: 'http://localhost:12345/1',
|
|
liveQuery: {
|
|
classNames: ['Yolo'],
|
|
},
|
|
liveQueryServerOptions: {
|
|
port: 22347,
|
|
},
|
|
serverStartComplete: () => {
|
|
expect(parseServer.liveQueryServer).not.toBeUndefined();
|
|
expect(parseServer.liveQueryServer.server).not.toBe(
|
|
parseServer.server
|
|
);
|
|
parseServer.liveQueryServer.server.close(
|
|
parseServer.server.close.bind(parseServer.server, done)
|
|
);
|
|
},
|
|
});
|
|
});
|
|
});
|
|
|
|
it('properly passes the CLP to afterSave/afterDelete hook', function(done) {
|
|
function setPermissionsOnClass(className, permissions, doPut) {
|
|
const request = require('request');
|
|
let op = request.post;
|
|
if (doPut) {
|
|
op = request.put;
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
op(
|
|
{
|
|
url: Parse.serverURL + '/schemas/' + className,
|
|
headers: {
|
|
'X-Parse-Application-Id': Parse.applicationId,
|
|
'X-Parse-Master-Key': Parse.masterKey,
|
|
},
|
|
json: true,
|
|
body: {
|
|
classLevelPermissions: permissions,
|
|
},
|
|
},
|
|
(error, response, body) => {
|
|
if (error) {
|
|
return reject(error);
|
|
}
|
|
if (body.error) {
|
|
return reject(body);
|
|
}
|
|
return resolve(body);
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
let saveSpy;
|
|
let deleteSpy;
|
|
reconfigureServer({
|
|
liveQuery: {
|
|
classNames: ['Yolo'],
|
|
},
|
|
})
|
|
.then(parseServer => {
|
|
saveSpy = spyOn(parseServer.config.liveQueryController, 'onAfterSave');
|
|
deleteSpy = spyOn(
|
|
parseServer.config.liveQueryController,
|
|
'onAfterDelete'
|
|
);
|
|
return setPermissionsOnClass('Yolo', {
|
|
create: { '*': true },
|
|
delete: { '*': true },
|
|
});
|
|
})
|
|
.then(() => {
|
|
const obj = new Parse.Object('Yolo');
|
|
return obj.save();
|
|
})
|
|
.then(obj => {
|
|
return obj.destroy();
|
|
})
|
|
.then(() => {
|
|
expect(saveSpy).toHaveBeenCalled();
|
|
const saveArgs = saveSpy.calls.mostRecent().args;
|
|
expect(saveArgs.length).toBe(4);
|
|
expect(saveArgs[0]).toBe('Yolo');
|
|
expect(saveArgs[3]).toEqual({
|
|
get: {},
|
|
count: {},
|
|
addField: {},
|
|
create: { '*': true },
|
|
find: {},
|
|
update: {},
|
|
delete: { '*': true },
|
|
protectedFields: {},
|
|
});
|
|
|
|
expect(deleteSpy).toHaveBeenCalled();
|
|
const deleteArgs = deleteSpy.calls.mostRecent().args;
|
|
expect(deleteArgs.length).toBe(4);
|
|
expect(deleteArgs[0]).toBe('Yolo');
|
|
expect(deleteArgs[3]).toEqual({
|
|
get: {},
|
|
count: {},
|
|
addField: {},
|
|
create: { '*': true },
|
|
find: {},
|
|
update: {},
|
|
delete: { '*': true },
|
|
protectedFields: {},
|
|
});
|
|
done();
|
|
})
|
|
.catch(done.fail);
|
|
});
|
|
|
|
it('can handle connect command', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const parseWebSocket = {
|
|
clientId: -1,
|
|
};
|
|
parseLiveQueryServer._validateKeys = jasmine
|
|
.createSpy('validateKeys')
|
|
.and.returnValue(true);
|
|
parseLiveQueryServer._handleConnect(parseWebSocket, {
|
|
sessionToken: 'token',
|
|
});
|
|
|
|
const clientKeys = parseLiveQueryServer.clients.keys();
|
|
expect(parseLiveQueryServer.clients.size).toBe(1);
|
|
const firstKey = clientKeys.next().value;
|
|
expect(parseWebSocket.clientId).toBe(firstKey);
|
|
const client = parseLiveQueryServer.clients.get(firstKey);
|
|
expect(client).not.toBeNull();
|
|
// Make sure we send connect response to the client
|
|
expect(client.pushConnect).toHaveBeenCalled();
|
|
});
|
|
|
|
it('can handle subscribe command without clientId', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const incompleteParseConn = {};
|
|
parseLiveQueryServer._handleSubscribe(incompleteParseConn, {});
|
|
|
|
const Client = require('../lib/LiveQuery/Client').Client;
|
|
expect(Client.pushError).toHaveBeenCalled();
|
|
});
|
|
|
|
it('can handle subscribe command with new query', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Add mock client
|
|
const clientId = 1;
|
|
const client = addMockClient(parseLiveQueryServer, clientId);
|
|
// Handle mock subscription
|
|
const parseWebSocket = {
|
|
clientId: clientId,
|
|
};
|
|
const query = {
|
|
className: 'test',
|
|
where: {
|
|
key: 'value',
|
|
},
|
|
fields: ['test'],
|
|
};
|
|
const requestId = 2;
|
|
const request = {
|
|
query: query,
|
|
requestId: requestId,
|
|
sessionToken: 'sessionToken',
|
|
};
|
|
parseLiveQueryServer._handleSubscribe(parseWebSocket, request);
|
|
|
|
// Make sure we add the subscription to the server
|
|
const subscriptions = parseLiveQueryServer.subscriptions;
|
|
expect(subscriptions.size).toBe(1);
|
|
expect(subscriptions.get(query.className)).not.toBeNull();
|
|
const classSubscriptions = subscriptions.get(query.className);
|
|
expect(classSubscriptions.size).toBe(1);
|
|
expect(classSubscriptions.get('hash')).not.toBeNull();
|
|
// TODO(check subscription constructor to verify we pass the right argument)
|
|
// Make sure we add clientInfo to the subscription
|
|
const subscription = classSubscriptions.get('hash');
|
|
expect(subscription.addClientSubscription).toHaveBeenCalledWith(
|
|
clientId,
|
|
requestId
|
|
);
|
|
// Make sure we add subscriptionInfo to the client
|
|
const args = client.addSubscriptionInfo.calls.first().args;
|
|
expect(args[0]).toBe(requestId);
|
|
expect(args[1].fields).toBe(query.fields);
|
|
expect(args[1].sessionToken).toBe(request.sessionToken);
|
|
// Make sure we send subscribe response to the client
|
|
expect(client.pushSubscribe).toHaveBeenCalledWith(requestId);
|
|
});
|
|
|
|
it('can handle subscribe command with existing query', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Add two mock clients
|
|
const clientId = 1;
|
|
addMockClient(parseLiveQueryServer, clientId);
|
|
const clientIdAgain = 2;
|
|
const clientAgain = addMockClient(parseLiveQueryServer, clientIdAgain);
|
|
// Add subscription for mock client 1
|
|
const parseWebSocket = {
|
|
clientId: clientId,
|
|
};
|
|
const requestId = 2;
|
|
const query = {
|
|
className: 'test',
|
|
where: {
|
|
key: 'value',
|
|
},
|
|
fields: ['test'],
|
|
};
|
|
addMockSubscription(
|
|
parseLiveQueryServer,
|
|
clientId,
|
|
requestId,
|
|
parseWebSocket,
|
|
query
|
|
);
|
|
// Add subscription for mock client 2
|
|
const parseWebSocketAgain = {
|
|
clientId: clientIdAgain,
|
|
};
|
|
const queryAgain = {
|
|
className: 'test',
|
|
where: {
|
|
key: 'value',
|
|
},
|
|
fields: ['testAgain'],
|
|
};
|
|
const requestIdAgain = 1;
|
|
addMockSubscription(
|
|
parseLiveQueryServer,
|
|
clientIdAgain,
|
|
requestIdAgain,
|
|
parseWebSocketAgain,
|
|
queryAgain
|
|
);
|
|
|
|
// Make sure we only have one subscription
|
|
const subscriptions = parseLiveQueryServer.subscriptions;
|
|
expect(subscriptions.size).toBe(1);
|
|
expect(subscriptions.get(query.className)).not.toBeNull();
|
|
const classSubscriptions = subscriptions.get(query.className);
|
|
expect(classSubscriptions.size).toBe(1);
|
|
expect(classSubscriptions.get('hash')).not.toBeNull();
|
|
// Make sure we add clientInfo to the subscription
|
|
const subscription = classSubscriptions.get('hash');
|
|
// Make sure client 2 info has been added
|
|
let args = subscription.addClientSubscription.calls.mostRecent().args;
|
|
expect(args).toEqual([clientIdAgain, requestIdAgain]);
|
|
// Make sure we add subscriptionInfo to the client 2
|
|
args = clientAgain.addSubscriptionInfo.calls.mostRecent().args;
|
|
expect(args[0]).toBe(requestIdAgain);
|
|
expect(args[1].fields).toBe(queryAgain.fields);
|
|
});
|
|
|
|
it('can handle unsubscribe command without clientId', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const incompleteParseConn = {};
|
|
parseLiveQueryServer._handleUnsubscribe(incompleteParseConn, {});
|
|
|
|
const Client = require('../lib/LiveQuery/Client').Client;
|
|
expect(Client.pushError).toHaveBeenCalled();
|
|
});
|
|
|
|
it('can handle unsubscribe command without not existed client', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const parseWebSocket = {
|
|
clientId: 1,
|
|
};
|
|
parseLiveQueryServer._handleUnsubscribe(parseWebSocket, {});
|
|
|
|
const Client = require('../lib/LiveQuery/Client').Client;
|
|
expect(Client.pushError).toHaveBeenCalled();
|
|
});
|
|
|
|
it('can handle unsubscribe command without not existed query', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Add mock client
|
|
const clientId = 1;
|
|
addMockClient(parseLiveQueryServer, clientId);
|
|
// Handle unsubscribe command
|
|
const parseWebSocket = {
|
|
clientId: 1,
|
|
};
|
|
parseLiveQueryServer._handleUnsubscribe(parseWebSocket, {});
|
|
|
|
const Client = require('../lib/LiveQuery/Client').Client;
|
|
expect(Client.pushError).toHaveBeenCalled();
|
|
});
|
|
|
|
it('can handle unsubscribe command', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Add mock client
|
|
const clientId = 1;
|
|
const client = addMockClient(parseLiveQueryServer, clientId);
|
|
// Add subscription for mock client
|
|
const parseWebSocket = {
|
|
clientId: 1,
|
|
};
|
|
const requestId = 2;
|
|
const subscription = addMockSubscription(
|
|
parseLiveQueryServer,
|
|
clientId,
|
|
requestId,
|
|
parseWebSocket
|
|
);
|
|
// Mock client.getSubscriptionInfo
|
|
const subscriptionInfo = client.addSubscriptionInfo.calls.mostRecent()
|
|
.args[1];
|
|
client.getSubscriptionInfo = function() {
|
|
return subscriptionInfo;
|
|
};
|
|
// Handle unsubscribe command
|
|
const requestAgain = {
|
|
requestId: requestId,
|
|
};
|
|
parseLiveQueryServer._handleUnsubscribe(parseWebSocket, requestAgain);
|
|
|
|
// Make sure we delete subscription from client
|
|
expect(client.deleteSubscriptionInfo).toHaveBeenCalledWith(requestId);
|
|
// Make sure we delete client from subscription
|
|
expect(subscription.deleteClientSubscription).toHaveBeenCalledWith(
|
|
clientId,
|
|
requestId
|
|
);
|
|
// Make sure we clear subscription in the server
|
|
const subscriptions = parseLiveQueryServer.subscriptions;
|
|
expect(subscriptions.size).toBe(0);
|
|
});
|
|
|
|
it('can set connect command message handler for a parseWebSocket', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Register mock connect/subscribe/unsubscribe handler for the server
|
|
parseLiveQueryServer._handleConnect = jasmine.createSpy('_handleSubscribe');
|
|
// Make mock parseWebsocket
|
|
const EventEmitter = require('events');
|
|
const parseWebSocket = new EventEmitter();
|
|
// Register message handlers for the parseWebSocket
|
|
parseLiveQueryServer._onConnect(parseWebSocket);
|
|
|
|
// Check connect request
|
|
const connectRequest = {
|
|
op: 'connect',
|
|
applicationId: '1',
|
|
installationId: '1234',
|
|
};
|
|
// Trigger message event
|
|
parseWebSocket.emit('message', connectRequest);
|
|
// Make sure _handleConnect is called
|
|
const args = parseLiveQueryServer._handleConnect.calls.mostRecent().args;
|
|
expect(args[0]).toBe(parseWebSocket);
|
|
});
|
|
|
|
it('can set subscribe command message handler for a parseWebSocket', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Register mock connect/subscribe/unsubscribe handler for the server
|
|
parseLiveQueryServer._handleSubscribe = jasmine.createSpy(
|
|
'_handleSubscribe'
|
|
);
|
|
// Make mock parseWebsocket
|
|
const EventEmitter = require('events');
|
|
const parseWebSocket = new EventEmitter();
|
|
// Register message handlers for the parseWebSocket
|
|
parseLiveQueryServer._onConnect(parseWebSocket);
|
|
|
|
// Check subscribe request
|
|
const subscribeRequest = JSON.stringify({
|
|
op: 'subscribe',
|
|
requestId: 1,
|
|
query: { className: 'Test', where: {} },
|
|
});
|
|
// Trigger message event
|
|
parseWebSocket.emit('message', subscribeRequest);
|
|
// Make sure _handleSubscribe is called
|
|
const args = parseLiveQueryServer._handleSubscribe.calls.mostRecent().args;
|
|
expect(args[0]).toBe(parseWebSocket);
|
|
expect(JSON.stringify(args[1])).toBe(subscribeRequest);
|
|
});
|
|
|
|
it('can set unsubscribe command message handler for a parseWebSocket', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Register mock connect/subscribe/unsubscribe handler for the server
|
|
parseLiveQueryServer._handleUnsubscribe = jasmine.createSpy(
|
|
'_handleSubscribe'
|
|
);
|
|
// Make mock parseWebsocket
|
|
const EventEmitter = require('events');
|
|
const parseWebSocket = new EventEmitter();
|
|
// Register message handlers for the parseWebSocket
|
|
parseLiveQueryServer._onConnect(parseWebSocket);
|
|
|
|
// Check unsubscribe request
|
|
const unsubscribeRequest = JSON.stringify({
|
|
op: 'unsubscribe',
|
|
requestId: 1,
|
|
});
|
|
// Trigger message event
|
|
parseWebSocket.emit('message', unsubscribeRequest);
|
|
// Make sure _handleUnsubscribe is called
|
|
const args = parseLiveQueryServer._handleUnsubscribe.calls.mostRecent()
|
|
.args;
|
|
expect(args[0]).toBe(parseWebSocket);
|
|
expect(JSON.stringify(args[1])).toBe(unsubscribeRequest);
|
|
});
|
|
|
|
it('can set update command message handler for a parseWebSocket', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Register mock connect/subscribe/unsubscribe handler for the server
|
|
spyOn(parseLiveQueryServer, '_handleUpdateSubscription').and.callThrough();
|
|
spyOn(parseLiveQueryServer, '_handleUnsubscribe').and.callThrough();
|
|
spyOn(parseLiveQueryServer, '_handleSubscribe').and.callThrough();
|
|
|
|
// Make mock parseWebsocket
|
|
const EventEmitter = require('events');
|
|
const parseWebSocket = new EventEmitter();
|
|
|
|
// Register message handlers for the parseWebSocket
|
|
parseLiveQueryServer._onConnect(parseWebSocket);
|
|
|
|
// Check updateRequest request
|
|
const updateRequest = JSON.stringify({
|
|
op: 'update',
|
|
requestId: 1,
|
|
query: { className: 'Test', where: {} },
|
|
});
|
|
// Trigger message event
|
|
parseWebSocket.emit('message', updateRequest);
|
|
// Make sure _handleUnsubscribe is called
|
|
const args = parseLiveQueryServer._handleUpdateSubscription.calls.mostRecent()
|
|
.args;
|
|
expect(args[0]).toBe(parseWebSocket);
|
|
expect(JSON.stringify(args[1])).toBe(updateRequest);
|
|
expect(parseLiveQueryServer._handleUnsubscribe).toHaveBeenCalled();
|
|
const unsubArgs = parseLiveQueryServer._handleUnsubscribe.calls.mostRecent()
|
|
.args;
|
|
expect(unsubArgs.length).toBe(3);
|
|
expect(unsubArgs[2]).toBe(false);
|
|
expect(parseLiveQueryServer._handleSubscribe).toHaveBeenCalled();
|
|
});
|
|
|
|
it('can set missing command message handler for a parseWebSocket', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make mock parseWebsocket
|
|
const EventEmitter = require('events');
|
|
const parseWebSocket = new EventEmitter();
|
|
// Register message handlers for the parseWebSocket
|
|
parseLiveQueryServer._onConnect(parseWebSocket);
|
|
|
|
// Check invalid request
|
|
const invalidRequest = '{}';
|
|
// Trigger message event
|
|
parseWebSocket.emit('message', invalidRequest);
|
|
const Client = require('../lib/LiveQuery/Client').Client;
|
|
expect(Client.pushError).toHaveBeenCalled();
|
|
});
|
|
|
|
it('can set unknown command message handler for a parseWebSocket', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make mock parseWebsocket
|
|
const EventEmitter = require('events');
|
|
const parseWebSocket = new EventEmitter();
|
|
// Register message handlers for the parseWebSocket
|
|
parseLiveQueryServer._onConnect(parseWebSocket);
|
|
|
|
// Check unknown request
|
|
const unknownRequest = '{"op":"unknown"}';
|
|
// Trigger message event
|
|
parseWebSocket.emit('message', unknownRequest);
|
|
const Client = require('../lib/LiveQuery/Client').Client;
|
|
expect(Client.pushError).toHaveBeenCalled();
|
|
});
|
|
|
|
it('can set disconnect command message handler for a parseWebSocket which has not registered to the server', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const EventEmitter = require('events');
|
|
const parseWebSocket = new EventEmitter();
|
|
parseWebSocket.clientId = 1;
|
|
// Register message handlers for the parseWebSocket
|
|
parseLiveQueryServer._onConnect(parseWebSocket);
|
|
|
|
// Make sure we do not crash
|
|
// Trigger disconnect event
|
|
parseWebSocket.emit('disconnect');
|
|
});
|
|
|
|
it('can forward event to cloud code', function() {
|
|
const cloudCodeHandler = {
|
|
handler: () => {},
|
|
};
|
|
const spy = spyOn(cloudCodeHandler, 'handler').and.callThrough();
|
|
Parse.Cloud.onLiveQueryEvent(cloudCodeHandler.handler);
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const EventEmitter = require('events');
|
|
const parseWebSocket = new EventEmitter();
|
|
parseWebSocket.clientId = 1;
|
|
// Register message handlers for the parseWebSocket
|
|
parseLiveQueryServer._onConnect(parseWebSocket);
|
|
|
|
// Make sure we do not crash
|
|
// Trigger disconnect event
|
|
parseWebSocket.emit('disconnect');
|
|
expect(spy).toHaveBeenCalled();
|
|
// call for ws_connect, another for ws_disconnect
|
|
expect(spy.calls.count()).toBe(2);
|
|
});
|
|
|
|
// TODO: Test server can set disconnect command message handler for a parseWebSocket
|
|
|
|
it('has no subscription and can handle object delete command', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make deletedParseObject
|
|
const parseObject = new Parse.Object(testClassName);
|
|
parseObject._finishFetch({
|
|
key: 'value',
|
|
className: testClassName,
|
|
});
|
|
// Make mock message
|
|
const message = {
|
|
currentParseObject: parseObject,
|
|
};
|
|
// Make sure we do not crash in this case
|
|
parseLiveQueryServer._onAfterDelete(message, {});
|
|
});
|
|
|
|
it('can handle object delete command which does not match any subscription', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make deletedParseObject
|
|
const parseObject = new Parse.Object(testClassName);
|
|
parseObject._finishFetch({
|
|
key: 'value',
|
|
className: testClassName,
|
|
});
|
|
// Make mock message
|
|
const message = {
|
|
currentParseObject: parseObject,
|
|
};
|
|
|
|
// Add mock client
|
|
const clientId = 1;
|
|
addMockClient(parseLiveQueryServer, clientId);
|
|
// Add mock subscription
|
|
const requestId = 2;
|
|
addMockSubscription(parseLiveQueryServer, clientId, requestId);
|
|
const client = parseLiveQueryServer.clients.get(clientId);
|
|
// Mock _matchesSubscription to return not matching
|
|
parseLiveQueryServer._matchesSubscription = function() {
|
|
return false;
|
|
};
|
|
parseLiveQueryServer._matchesACL = function() {
|
|
return true;
|
|
};
|
|
parseLiveQueryServer._onAfterDelete(message);
|
|
|
|
// Make sure we do not send command to client
|
|
expect(client.pushDelete).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('can handle object delete command which matches some subscriptions', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make deletedParseObject
|
|
const parseObject = new Parse.Object(testClassName);
|
|
parseObject._finishFetch({
|
|
key: 'value',
|
|
className: testClassName,
|
|
});
|
|
// Make mock message
|
|
const message = {
|
|
currentParseObject: parseObject,
|
|
};
|
|
// Add mock client
|
|
const clientId = 1;
|
|
addMockClient(parseLiveQueryServer, clientId);
|
|
// Add mock subscription
|
|
const requestId = 2;
|
|
addMockSubscription(parseLiveQueryServer, clientId, requestId);
|
|
const client = parseLiveQueryServer.clients.get(clientId);
|
|
// Mock _matchesSubscription to return matching
|
|
parseLiveQueryServer._matchesSubscription = function() {
|
|
return true;
|
|
};
|
|
parseLiveQueryServer._matchesACL = function() {
|
|
return Promise.resolve(true);
|
|
};
|
|
parseLiveQueryServer._onAfterDelete(message);
|
|
|
|
// 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);
|
|
});
|
|
|
|
it('has no subscription and can handle object save command', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make mock request message
|
|
const message = generateMockMessage();
|
|
// Make sure we do not crash in this case
|
|
parseLiveQueryServer._onAfterSave(message);
|
|
});
|
|
|
|
it('can handle object save command which does not match any subscription', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make mock request message
|
|
const message = generateMockMessage();
|
|
// Add mock client
|
|
const clientId = 1;
|
|
const client = addMockClient(parseLiveQueryServer, clientId);
|
|
// Add mock subscription
|
|
const requestId = 2;
|
|
addMockSubscription(parseLiveQueryServer, clientId, requestId);
|
|
// Mock _matchesSubscription to return not matching
|
|
parseLiveQueryServer._matchesSubscription = function() {
|
|
return false;
|
|
};
|
|
parseLiveQueryServer._matchesACL = function() {
|
|
return Promise.resolve(true);
|
|
};
|
|
// Trigger onAfterSave
|
|
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);
|
|
});
|
|
|
|
it('can handle object enter command which matches some subscriptions', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make mock request message
|
|
const message = generateMockMessage(true);
|
|
// Add mock client
|
|
const clientId = 1;
|
|
const client = addMockClient(parseLiveQueryServer, clientId);
|
|
// Add mock subscription
|
|
const requestId = 2;
|
|
addMockSubscription(parseLiveQueryServer, clientId, requestId);
|
|
// Mock _matchesSubscription to return matching
|
|
// In order to mimic a enter, we need original match return false
|
|
// and the current match return true
|
|
let counter = 0;
|
|
parseLiveQueryServer._matchesSubscription = function(parseObject) {
|
|
if (!parseObject) {
|
|
return false;
|
|
}
|
|
counter += 1;
|
|
return counter % 2 === 0;
|
|
};
|
|
parseLiveQueryServer._matchesACL = function() {
|
|
return Promise.resolve(true);
|
|
};
|
|
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);
|
|
});
|
|
|
|
it('can handle object update command which matches some subscriptions', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make mock request message
|
|
const message = generateMockMessage(true);
|
|
// Add mock client
|
|
const clientId = 1;
|
|
const client = addMockClient(parseLiveQueryServer, clientId);
|
|
// Add mock subscription
|
|
const requestId = 2;
|
|
addMockSubscription(parseLiveQueryServer, clientId, requestId);
|
|
// Mock _matchesSubscription to return matching
|
|
parseLiveQueryServer._matchesSubscription = function(parseObject) {
|
|
if (!parseObject) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
parseLiveQueryServer._matchesACL = function() {
|
|
return Promise.resolve(true);
|
|
};
|
|
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);
|
|
});
|
|
|
|
it('can handle object leave command which matches some subscriptions', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make mock request message
|
|
const message = generateMockMessage(true);
|
|
// Add mock client
|
|
const clientId = 1;
|
|
const client = addMockClient(parseLiveQueryServer, clientId);
|
|
// Add mock subscription
|
|
const requestId = 2;
|
|
addMockSubscription(parseLiveQueryServer, clientId, requestId);
|
|
// Mock _matchesSubscription to return matching
|
|
// In order to mimic a leave, we need original match return true
|
|
// and the current match return false
|
|
let counter = 0;
|
|
parseLiveQueryServer._matchesSubscription = function(parseObject) {
|
|
if (!parseObject) {
|
|
return false;
|
|
}
|
|
counter += 1;
|
|
return counter % 2 !== 0;
|
|
};
|
|
parseLiveQueryServer._matchesACL = function() {
|
|
return Promise.resolve(true);
|
|
};
|
|
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);
|
|
});
|
|
|
|
it('can handle update command with original object', function(done) {
|
|
jasmine.restoreLibrary('../lib/LiveQuery/Client', 'Client');
|
|
const Client = require('../lib/LiveQuery/Client').Client;
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make mock request message
|
|
const message = generateMockMessage(true);
|
|
|
|
const clientId = 1;
|
|
const parseWebSocket = {
|
|
clientId,
|
|
send: jasmine.createSpy('send'),
|
|
};
|
|
const client = new Client(clientId, parseWebSocket);
|
|
spyOn(client, 'pushUpdate').and.callThrough();
|
|
parseLiveQueryServer.clients.set(clientId, client);
|
|
|
|
// Add mock subscription
|
|
const requestId = 2;
|
|
|
|
addMockSubscription(
|
|
parseLiveQueryServer,
|
|
clientId,
|
|
requestId,
|
|
parseWebSocket
|
|
);
|
|
// Mock _matchesSubscription to return matching
|
|
parseLiveQueryServer._matchesSubscription = function(parseObject) {
|
|
if (!parseObject) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
parseLiveQueryServer._matchesACL = function() {
|
|
return Promise.resolve(true);
|
|
};
|
|
|
|
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]);
|
|
|
|
expect(toSend.object).toBeDefined();
|
|
expect(toSend.original).toBeDefined();
|
|
done();
|
|
}, jasmine.ASYNC_TEST_WAIT_TIME);
|
|
});
|
|
|
|
it('can handle object create command which matches some subscriptions', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make mock request message
|
|
const message = generateMockMessage();
|
|
// Add mock client
|
|
const clientId = 1;
|
|
const client = addMockClient(parseLiveQueryServer, clientId);
|
|
// Add mock subscription
|
|
const requestId = 2;
|
|
addMockSubscription(parseLiveQueryServer, clientId, requestId);
|
|
// Mock _matchesSubscription to return matching
|
|
parseLiveQueryServer._matchesSubscription = function(parseObject) {
|
|
if (!parseObject) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
parseLiveQueryServer._matchesACL = function() {
|
|
return Promise.resolve(true);
|
|
};
|
|
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);
|
|
});
|
|
|
|
it('can handle create command with fields', function(done) {
|
|
jasmine.restoreLibrary('../lib/LiveQuery/Client', 'Client');
|
|
const Client = require('../lib/LiveQuery/Client').Client;
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make mock request message
|
|
const message = generateMockMessage();
|
|
|
|
const clientId = 1;
|
|
const parseWebSocket = {
|
|
clientId,
|
|
send: jasmine.createSpy('send'),
|
|
};
|
|
const client = new Client(clientId, parseWebSocket);
|
|
spyOn(client, 'pushCreate').and.callThrough();
|
|
parseLiveQueryServer.clients.set(clientId, client);
|
|
|
|
// Add mock subscription
|
|
const requestId = 2;
|
|
const query = {
|
|
className: testClassName,
|
|
where: {
|
|
key: 'value',
|
|
},
|
|
fields: ['test'],
|
|
};
|
|
addMockSubscription(
|
|
parseLiveQueryServer,
|
|
clientId,
|
|
requestId,
|
|
parseWebSocket,
|
|
query
|
|
);
|
|
// Mock _matchesSubscription to return matching
|
|
parseLiveQueryServer._matchesSubscription = function(parseObject) {
|
|
if (!parseObject) {
|
|
return false;
|
|
}
|
|
return true;
|
|
};
|
|
parseLiveQueryServer._matchesACL = function() {
|
|
return Promise.resolve(true);
|
|
};
|
|
|
|
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);
|
|
});
|
|
|
|
it('can match subscription for null or undefined parse object', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make mock subscription
|
|
const subscription = {
|
|
match: jasmine.createSpy('match'),
|
|
};
|
|
|
|
expect(parseLiveQueryServer._matchesSubscription(null, subscription)).toBe(
|
|
false
|
|
);
|
|
expect(
|
|
parseLiveQueryServer._matchesSubscription(undefined, subscription)
|
|
).toBe(false);
|
|
// Make sure subscription.match is not called
|
|
expect(subscription.match).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('can match subscription', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make mock subscription
|
|
const subscription = {
|
|
query: {},
|
|
};
|
|
const parseObject = {};
|
|
expect(
|
|
parseLiveQueryServer._matchesSubscription(parseObject, subscription)
|
|
).toBe(true);
|
|
// Make sure matchesQuery is called
|
|
const matchesQuery = require('../lib/LiveQuery/QueryTools').matchesQuery;
|
|
expect(matchesQuery).toHaveBeenCalledWith(parseObject, subscription.query);
|
|
});
|
|
|
|
it('can inflate parse object', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
// Make mock request
|
|
const objectJSON = {
|
|
className: 'testClassName',
|
|
createdAt: '2015-12-22T01:51:12.955Z',
|
|
key: 'value',
|
|
objectId: 'BfwxBCz6yW',
|
|
updatedAt: '2016-01-05T00:46:45.659Z',
|
|
};
|
|
const originalObjectJSON = {
|
|
className: 'testClassName',
|
|
createdAt: '2015-12-22T01:51:12.955Z',
|
|
key: 'originalValue',
|
|
objectId: 'BfwxBCz6yW',
|
|
updatedAt: '2016-01-05T00:46:45.659Z',
|
|
};
|
|
const message = {
|
|
currentParseObject: objectJSON,
|
|
originalParseObject: originalObjectJSON,
|
|
};
|
|
// Inflate the object
|
|
parseLiveQueryServer._inflateParseObject(message);
|
|
|
|
// Verify object
|
|
const object = message.currentParseObject;
|
|
expect(object instanceof Parse.Object).toBeTruthy();
|
|
expect(object.get('key')).toEqual('value');
|
|
expect(object.className).toEqual('testClassName');
|
|
expect(object.id).toBe('BfwxBCz6yW');
|
|
expect(object.createdAt).not.toBeUndefined();
|
|
expect(object.updatedAt).not.toBeUndefined();
|
|
// Verify original object
|
|
const originalObject = message.originalParseObject;
|
|
expect(originalObject instanceof Parse.Object).toBeTruthy();
|
|
expect(originalObject.get('key')).toEqual('originalValue');
|
|
expect(originalObject.className).toEqual('testClassName');
|
|
expect(originalObject.id).toBe('BfwxBCz6yW');
|
|
expect(originalObject.createdAt).not.toBeUndefined();
|
|
expect(originalObject.updatedAt).not.toBeUndefined();
|
|
});
|
|
|
|
it('can inflate user object', async () => {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const userJSON = {
|
|
username: 'test',
|
|
ACL: {},
|
|
createdAt: '2018-12-21T23:09:51.784Z',
|
|
sessionToken: 'r:1234',
|
|
updatedAt: '2018-12-21T23:09:51.784Z',
|
|
objectId: 'NhF2u9n72W',
|
|
__type: 'Object',
|
|
className: '_User',
|
|
_hashed_password: '1234',
|
|
_email_verify_token: '1234',
|
|
};
|
|
|
|
const originalUserJSON = {
|
|
username: 'test',
|
|
ACL: {},
|
|
createdAt: '2018-12-21T23:09:51.784Z',
|
|
sessionToken: 'r:1234',
|
|
updatedAt: '2018-12-21T23:09:51.784Z',
|
|
objectId: 'NhF2u9n72W',
|
|
__type: 'Object',
|
|
className: '_User',
|
|
_hashed_password: '12345',
|
|
_email_verify_token: '12345',
|
|
};
|
|
|
|
const message = {
|
|
currentParseObject: userJSON,
|
|
originalParseObject: originalUserJSON,
|
|
};
|
|
parseLiveQueryServer._inflateParseObject(message);
|
|
|
|
const object = message.currentParseObject;
|
|
expect(object instanceof Parse.Object).toBeTruthy();
|
|
expect(object.get('_hashed_password')).toBeUndefined();
|
|
expect(object.get('_email_verify_token')).toBeUndefined();
|
|
expect(object.className).toEqual('_User');
|
|
expect(object.id).toBe('NhF2u9n72W');
|
|
expect(object.createdAt).not.toBeUndefined();
|
|
expect(object.updatedAt).not.toBeUndefined();
|
|
|
|
const originalObject = message.originalParseObject;
|
|
expect(originalObject instanceof Parse.Object).toBeTruthy();
|
|
expect(originalObject.get('_hashed_password')).toBeUndefined();
|
|
expect(originalObject.get('_email_verify_token')).toBeUndefined();
|
|
expect(originalObject.className).toEqual('_User');
|
|
expect(originalObject.id).toBe('NhF2u9n72W');
|
|
expect(originalObject.createdAt).not.toBeUndefined();
|
|
expect(originalObject.updatedAt).not.toBeUndefined();
|
|
});
|
|
|
|
it('can match undefined ACL', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const client = {};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(undefined, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(true);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can match ACL with none exist requestId', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
const client = {
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue(undefined),
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(acl, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(false);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can match ACL with public read access', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setPublicReadAccess(true);
|
|
const client = {
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({
|
|
sessionToken: 'sessionToken',
|
|
}),
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(acl, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(true);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can match ACL with valid subscription sessionToken', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setReadAccess(testUserId, true);
|
|
const client = {
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({
|
|
sessionToken: 'sessionToken',
|
|
}),
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(acl, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(true);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can match ACL with valid client sessionToken', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setReadAccess(testUserId, true);
|
|
// Mock sessionTokenCache will return false when sessionToken is undefined
|
|
const client = {
|
|
sessionToken: 'sessionToken',
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({
|
|
sessionToken: undefined,
|
|
}),
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(acl, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(true);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can match ACL with invalid subscription and client sessionToken', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setReadAccess(testUserId, true);
|
|
// Mock sessionTokenCache will return false when sessionToken is undefined
|
|
const client = {
|
|
sessionToken: undefined,
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({
|
|
sessionToken: undefined,
|
|
}),
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(acl, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(false);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can match ACL with subscription sessionToken checking error', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setReadAccess(testUserId, true);
|
|
// Mock sessionTokenCache will return error when sessionToken is null, this is just
|
|
// the behaviour of our mock sessionTokenCache, not real sessionTokenCache
|
|
const client = {
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({
|
|
sessionToken: null,
|
|
}),
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(acl, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(false);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('can match ACL with client sessionToken checking error', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setReadAccess(testUserId, true);
|
|
// Mock sessionTokenCache will return error when sessionToken is null
|
|
const client = {
|
|
sessionToken: null,
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({
|
|
sessionToken: null,
|
|
}),
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(acl, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(false);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("won't match ACL that doesn't have public read or any roles", function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setPublicReadAccess(false);
|
|
const client = {
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({
|
|
sessionToken: 'sessionToken',
|
|
}),
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(acl, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(false);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("won't match non-public ACL with role when there is no user", function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setPublicReadAccess(false);
|
|
acl.setRoleReadAccess('livequery', true);
|
|
const client = {
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({}),
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(acl, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(false);
|
|
done();
|
|
})
|
|
.catch(done.fail);
|
|
});
|
|
|
|
it("won't match ACL with role based read access set to false", function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setPublicReadAccess(false);
|
|
acl.setRoleReadAccess('otherLiveQueryRead', true);
|
|
const client = {
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({
|
|
sessionToken: 'sessionToken',
|
|
}),
|
|
};
|
|
const requestId = 0;
|
|
|
|
spyOn(Parse, 'Query').and.callFake(function() {
|
|
let shouldReturn = false;
|
|
return {
|
|
equalTo() {
|
|
shouldReturn = true;
|
|
// Nothing to do here
|
|
return this;
|
|
},
|
|
containedIn() {
|
|
shouldReturn = false;
|
|
return this;
|
|
},
|
|
find() {
|
|
if (!shouldReturn) {
|
|
return Promise.resolve([]);
|
|
}
|
|
//Return a role with the name "liveQueryRead" as that is what was set on the ACL
|
|
const liveQueryRole = new Parse.Role(
|
|
'liveQueryRead',
|
|
new Parse.ACL()
|
|
);
|
|
liveQueryRole.id = 'abcdef1234';
|
|
return Promise.resolve([liveQueryRole]);
|
|
},
|
|
};
|
|
});
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(acl, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(false);
|
|
done();
|
|
});
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(acl, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(false);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('will match ACL with role based read access set to true', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setPublicReadAccess(false);
|
|
acl.setRoleReadAccess('liveQueryRead', true);
|
|
const client = {
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({
|
|
sessionToken: 'sessionToken',
|
|
}),
|
|
};
|
|
const requestId = 0;
|
|
|
|
spyOn(Parse, 'Query').and.callFake(function() {
|
|
let shouldReturn = false;
|
|
return {
|
|
equalTo() {
|
|
shouldReturn = true;
|
|
// Nothing to do here
|
|
return this;
|
|
},
|
|
containedIn() {
|
|
shouldReturn = false;
|
|
return this;
|
|
},
|
|
find() {
|
|
if (!shouldReturn) {
|
|
return Promise.resolve([]);
|
|
}
|
|
//Return a role with the name "liveQueryRead" as that is what was set on the ACL
|
|
const liveQueryRole = new Parse.Role(
|
|
'liveQueryRead',
|
|
new Parse.ACL()
|
|
);
|
|
liveQueryRole.id = 'abcdef1234';
|
|
return Promise.resolve([liveQueryRole]);
|
|
},
|
|
each(callback) {
|
|
//Return a role with the name "liveQueryRead" as that is what was set on the ACL
|
|
const liveQueryRole = new Parse.Role(
|
|
'liveQueryRead',
|
|
new Parse.ACL()
|
|
);
|
|
liveQueryRole.id = 'abcdef1234';
|
|
callback(liveQueryRole);
|
|
return Promise.resolve();
|
|
},
|
|
};
|
|
});
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(acl, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(true);
|
|
done();
|
|
});
|
|
});
|
|
|
|
describe('class level permissions', () => {
|
|
it('matches CLP when find is closed', done => {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setReadAccess(testUserId, true);
|
|
// Mock sessionTokenCache will return false when sessionToken is undefined
|
|
const client = {
|
|
sessionToken: 'sessionToken',
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({
|
|
sessionToken: undefined,
|
|
}),
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesCLP(
|
|
{
|
|
find: {},
|
|
},
|
|
{ className: 'Yolo' },
|
|
client,
|
|
requestId,
|
|
'find'
|
|
)
|
|
.then(isMatched => {
|
|
expect(isMatched).toBe(false);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('matches CLP when find is open', done => {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setReadAccess(testUserId, true);
|
|
// Mock sessionTokenCache will return false when sessionToken is undefined
|
|
const client = {
|
|
sessionToken: 'sessionToken',
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({
|
|
sessionToken: undefined,
|
|
}),
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesCLP(
|
|
{
|
|
find: { '*': true },
|
|
},
|
|
{ className: 'Yolo' },
|
|
client,
|
|
requestId,
|
|
'find'
|
|
)
|
|
.then(isMatched => {
|
|
expect(isMatched).toBe(true);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('matches CLP when find is restricted to userIds', done => {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setReadAccess(testUserId, true);
|
|
// Mock sessionTokenCache will return false when sessionToken is undefined
|
|
const client = {
|
|
sessionToken: 'sessionToken',
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({
|
|
sessionToken: 'userId',
|
|
}),
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesCLP(
|
|
{
|
|
find: { userId: true },
|
|
},
|
|
{ className: 'Yolo' },
|
|
client,
|
|
requestId,
|
|
'find'
|
|
)
|
|
.then(isMatched => {
|
|
expect(isMatched).toBe(true);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('matches CLP when find is restricted to userIds', done => {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setReadAccess(testUserId, true);
|
|
// Mock sessionTokenCache will return false when sessionToken is undefined
|
|
const client = {
|
|
sessionToken: 'sessionToken',
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({
|
|
sessionToken: undefined,
|
|
}),
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesCLP(
|
|
{
|
|
find: { userId: true },
|
|
},
|
|
{ className: 'Yolo' },
|
|
client,
|
|
requestId,
|
|
'find'
|
|
)
|
|
.then(isMatched => {
|
|
expect(isMatched).toBe(false);
|
|
done();
|
|
});
|
|
});
|
|
});
|
|
|
|
it('can validate key when valid key is provided', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer(
|
|
{},
|
|
{
|
|
keyPairs: {
|
|
clientKey: 'test',
|
|
},
|
|
}
|
|
);
|
|
const request = {
|
|
clientKey: 'test',
|
|
};
|
|
|
|
expect(
|
|
parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)
|
|
).toBeTruthy();
|
|
});
|
|
|
|
it('can validate key when invalid key is provided', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer(
|
|
{},
|
|
{
|
|
keyPairs: {
|
|
clientKey: 'test',
|
|
},
|
|
}
|
|
);
|
|
const request = {
|
|
clientKey: 'error',
|
|
};
|
|
|
|
expect(
|
|
parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)
|
|
).not.toBeTruthy();
|
|
});
|
|
|
|
it('can validate key when key is not provided', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer(
|
|
{},
|
|
{
|
|
keyPairs: {
|
|
clientKey: 'test',
|
|
},
|
|
}
|
|
);
|
|
const request = {};
|
|
|
|
expect(
|
|
parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)
|
|
).not.toBeTruthy();
|
|
});
|
|
|
|
it('can validate key when validKerPairs is empty', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({}, {});
|
|
const request = {};
|
|
|
|
expect(
|
|
parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)
|
|
).toBeTruthy();
|
|
});
|
|
|
|
it('can validate client has master key when valid', function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer(
|
|
{},
|
|
{
|
|
keyPairs: {
|
|
masterKey: 'test',
|
|
},
|
|
}
|
|
);
|
|
const request = {
|
|
masterKey: 'test',
|
|
};
|
|
|
|
expect(
|
|
parseLiveQueryServer._hasMasterKey(request, parseLiveQueryServer.keyPairs)
|
|
).toBeTruthy();
|
|
});
|
|
|
|
it("can validate client doesn't have master key when invalid", function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer(
|
|
{},
|
|
{
|
|
keyPairs: {
|
|
masterKey: 'test',
|
|
},
|
|
}
|
|
);
|
|
const request = {
|
|
masterKey: 'notValid',
|
|
};
|
|
|
|
expect(
|
|
parseLiveQueryServer._hasMasterKey(request, parseLiveQueryServer.keyPairs)
|
|
).not.toBeTruthy();
|
|
});
|
|
|
|
it("can validate client doesn't have master key when not provided", function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer(
|
|
{},
|
|
{
|
|
keyPairs: {
|
|
masterKey: 'test',
|
|
},
|
|
}
|
|
);
|
|
|
|
expect(
|
|
parseLiveQueryServer._hasMasterKey({}, parseLiveQueryServer.keyPairs)
|
|
).not.toBeTruthy();
|
|
});
|
|
|
|
it("can validate client doesn't have master key when validKeyPairs is empty", function() {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({}, {});
|
|
const request = {
|
|
masterKey: 'test',
|
|
};
|
|
|
|
expect(
|
|
parseLiveQueryServer._hasMasterKey(request, parseLiveQueryServer.keyPairs)
|
|
).not.toBeTruthy();
|
|
});
|
|
|
|
it('will match non-public ACL when client has master key', function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setPublicReadAccess(false);
|
|
const client = {
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({}),
|
|
hasMasterKey: true,
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(acl, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(true);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it("won't match non-public ACL when client has no master key", function(done) {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const acl = new Parse.ACL();
|
|
acl.setPublicReadAccess(false);
|
|
const client = {
|
|
getSubscriptionInfo: jasmine
|
|
.createSpy('getSubscriptionInfo')
|
|
.and.returnValue({}),
|
|
hasMasterKey: false,
|
|
};
|
|
const requestId = 0;
|
|
|
|
parseLiveQueryServer
|
|
._matchesACL(acl, client, requestId)
|
|
.then(function(isMatched) {
|
|
expect(isMatched).toBe(false);
|
|
done();
|
|
});
|
|
});
|
|
|
|
it('should properly pull auth from cache', () => {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const promise = parseLiveQueryServer.getAuthForSessionToken('sessionToken');
|
|
const secondPromise = parseLiveQueryServer.getAuthForSessionToken(
|
|
'sessionToken'
|
|
);
|
|
// should be in the cache
|
|
expect(parseLiveQueryServer.authCache.get('sessionToken')).toBe(promise);
|
|
// should be the same promise returned
|
|
expect(promise).toBe(secondPromise);
|
|
// the auth should be called only once
|
|
expect(auth.getAuthForSessionToken.calls.count()).toBe(1);
|
|
});
|
|
|
|
it('should delete from cache throwing auth calls', async () => {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const promise = parseLiveQueryServer.getAuthForSessionToken('pleaseThrow');
|
|
expect(parseLiveQueryServer.authCache.get('pleaseThrow')).toBe(promise);
|
|
// after the promise finishes, it should have removed it from the cache
|
|
expect(await promise).toEqual({});
|
|
expect(parseLiveQueryServer.authCache.get('pleaseThrow')).toBe(undefined);
|
|
});
|
|
|
|
it('should keep a cache of invalid sessions', async () => {
|
|
const parseLiveQueryServer = new ParseLiveQueryServer({});
|
|
const promise = parseLiveQueryServer.getAuthForSessionToken('invalid');
|
|
expect(parseLiveQueryServer.authCache.get('invalid')).toBe(promise);
|
|
// after the promise finishes, it should have removed it from the cache
|
|
await promise;
|
|
const finalResult = await parseLiveQueryServer.authCache.get('invalid');
|
|
expect(finalResult.error).not.toBeUndefined();
|
|
expect(parseLiveQueryServer.authCache.get('invalid')).not.toBe(undefined);
|
|
});
|
|
|
|
afterEach(function() {
|
|
jasmine.restoreLibrary(
|
|
'../lib/LiveQuery/ParseWebSocketServer',
|
|
'ParseWebSocketServer'
|
|
);
|
|
jasmine.restoreLibrary('../lib/LiveQuery/Client', 'Client');
|
|
jasmine.restoreLibrary('../lib/LiveQuery/Subscription', 'Subscription');
|
|
jasmine.restoreLibrary('../lib/LiveQuery/QueryTools', 'queryHash');
|
|
jasmine.restoreLibrary('../lib/LiveQuery/QueryTools', 'matchesQuery');
|
|
jasmine.restoreLibrary('../lib/LiveQuery/ParsePubSub', 'ParsePubSub');
|
|
});
|
|
|
|
// Helper functions to add mock client and subscription to a liveQueryServer
|
|
function addMockClient(parseLiveQueryServer, clientId) {
|
|
const Client = require('../lib/LiveQuery/Client').Client;
|
|
const client = new Client(clientId, {});
|
|
parseLiveQueryServer.clients.set(clientId, client);
|
|
return client;
|
|
}
|
|
|
|
function addMockSubscription(
|
|
parseLiveQueryServer,
|
|
clientId,
|
|
requestId,
|
|
parseWebSocket,
|
|
query
|
|
) {
|
|
// If parseWebSocket is null, we use the default one
|
|
if (!parseWebSocket) {
|
|
const EventEmitter = require('events');
|
|
parseWebSocket = new EventEmitter();
|
|
}
|
|
parseWebSocket.clientId = clientId;
|
|
// If query is null, we use the default one
|
|
if (!query) {
|
|
query = {
|
|
className: testClassName,
|
|
where: {
|
|
key: 'value',
|
|
},
|
|
fields: ['test'],
|
|
};
|
|
}
|
|
const request = {
|
|
query: query,
|
|
requestId: requestId,
|
|
sessionToken: 'sessionToken',
|
|
};
|
|
parseLiveQueryServer._handleSubscribe(parseWebSocket, request);
|
|
|
|
// Make mock subscription
|
|
const subscription = parseLiveQueryServer.subscriptions
|
|
.get(query.className)
|
|
.get(queryHashValue);
|
|
subscription.hasSubscribingClient = function() {
|
|
return false;
|
|
};
|
|
subscription.className = query.className;
|
|
subscription.hash = queryHashValue;
|
|
if (
|
|
subscription.clientRequestIds &&
|
|
subscription.clientRequestIds.has(clientId)
|
|
) {
|
|
subscription.clientRequestIds.get(clientId).push(requestId);
|
|
} else {
|
|
subscription.clientRequestIds = new Map([[clientId, [requestId]]]);
|
|
}
|
|
return subscription;
|
|
}
|
|
|
|
// Helper functiosn to generate request message
|
|
function generateMockMessage(hasOriginalParseObject) {
|
|
const parseObject = new Parse.Object(testClassName);
|
|
parseObject._finishFetch({
|
|
key: 'value',
|
|
className: testClassName,
|
|
});
|
|
const message = {
|
|
currentParseObject: parseObject,
|
|
};
|
|
if (hasOriginalParseObject) {
|
|
const originalParseObject = new Parse.Object(testClassName);
|
|
originalParseObject._finishFetch({
|
|
key: 'originalValue',
|
|
className: testClassName,
|
|
});
|
|
message.originalParseObject = originalParseObject;
|
|
}
|
|
return message;
|
|
}
|
|
});
|
|
|
|
describe('LiveQueryController', () => {
|
|
it('properly passes the CLP to afterSave/afterDelete hook', function(done) {
|
|
function setPermissionsOnClass(className, permissions, doPut) {
|
|
const request = require('request');
|
|
let op = request.post;
|
|
if (doPut) {
|
|
op = request.put;
|
|
}
|
|
return new Promise((resolve, reject) => {
|
|
op(
|
|
{
|
|
url: Parse.serverURL + '/schemas/' + className,
|
|
headers: {
|
|
'X-Parse-Application-Id': Parse.applicationId,
|
|
'X-Parse-Master-Key': Parse.masterKey,
|
|
},
|
|
json: true,
|
|
body: {
|
|
classLevelPermissions: permissions,
|
|
},
|
|
},
|
|
(error, response, body) => {
|
|
if (error) {
|
|
return reject(error);
|
|
}
|
|
if (body.error) {
|
|
return reject(body);
|
|
}
|
|
return resolve(body);
|
|
}
|
|
);
|
|
});
|
|
}
|
|
|
|
let saveSpy;
|
|
let deleteSpy;
|
|
reconfigureServer({
|
|
liveQuery: {
|
|
classNames: ['Yolo'],
|
|
},
|
|
})
|
|
.then(parseServer => {
|
|
saveSpy = spyOn(
|
|
parseServer.config.liveQueryController,
|
|
'onAfterSave'
|
|
).and.callThrough();
|
|
deleteSpy = spyOn(
|
|
parseServer.config.liveQueryController,
|
|
'onAfterDelete'
|
|
).and.callThrough();
|
|
return setPermissionsOnClass('Yolo', {
|
|
create: { '*': true },
|
|
delete: { '*': true },
|
|
});
|
|
})
|
|
.then(() => {
|
|
const obj = new Parse.Object('Yolo');
|
|
return obj.save();
|
|
})
|
|
.then(obj => {
|
|
return obj.destroy();
|
|
})
|
|
.then(() => {
|
|
expect(saveSpy).toHaveBeenCalled();
|
|
const saveArgs = saveSpy.calls.mostRecent().args;
|
|
expect(saveArgs.length).toBe(4);
|
|
expect(saveArgs[0]).toBe('Yolo');
|
|
expect(saveArgs[3]).toEqual({
|
|
get: {},
|
|
count: {},
|
|
addField: {},
|
|
create: { '*': true },
|
|
find: {},
|
|
update: {},
|
|
delete: { '*': true },
|
|
protectedFields: {},
|
|
});
|
|
|
|
expect(deleteSpy).toHaveBeenCalled();
|
|
const deleteArgs = deleteSpy.calls.mostRecent().args;
|
|
expect(deleteArgs.length).toBe(4);
|
|
expect(deleteArgs[0]).toBe('Yolo');
|
|
expect(deleteArgs[3]).toEqual({
|
|
get: {},
|
|
count: {},
|
|
addField: {},
|
|
create: { '*': true },
|
|
find: {},
|
|
update: {},
|
|
delete: { '*': true },
|
|
protectedFields: {},
|
|
});
|
|
done();
|
|
})
|
|
.catch(done.fail);
|
|
});
|
|
|
|
it('should properly pack message request on afterSave', () => {
|
|
const controller = new LiveQueryController({
|
|
classNames: ['Yolo'],
|
|
});
|
|
const spy = spyOn(controller.liveQueryPublisher, 'onCloudCodeAfterSave');
|
|
controller.onAfterSave('Yolo', { o: 1 }, { o: 2 }, { yolo: true });
|
|
expect(spy).toHaveBeenCalled();
|
|
const args = spy.calls.mostRecent().args;
|
|
expect(args.length).toBe(1);
|
|
expect(args[0]).toEqual({
|
|
object: { o: 1 },
|
|
original: { o: 2 },
|
|
classLevelPermissions: { yolo: true },
|
|
});
|
|
});
|
|
|
|
it('should properly pack message request on afterDelete', () => {
|
|
const controller = new LiveQueryController({
|
|
classNames: ['Yolo'],
|
|
});
|
|
const spy = spyOn(controller.liveQueryPublisher, 'onCloudCodeAfterDelete');
|
|
controller.onAfterDelete('Yolo', { o: 1 }, { o: 2 }, { yolo: true });
|
|
expect(spy).toHaveBeenCalled();
|
|
const args = spy.calls.mostRecent().args;
|
|
expect(args.length).toBe(1);
|
|
expect(args[0]).toEqual({
|
|
object: { o: 1 },
|
|
original: { o: 2 },
|
|
classLevelPermissions: { yolo: true },
|
|
});
|
|
});
|
|
|
|
it('should properly pack message request', () => {
|
|
const controller = new LiveQueryController({
|
|
classNames: ['Yolo'],
|
|
});
|
|
expect(controller._makePublisherRequest({})).toEqual({
|
|
object: {},
|
|
original: undefined,
|
|
});
|
|
});
|
|
});
|