Add LiveQuery
This commit is contained in:
290
spec/Client.spec.js
Normal file
290
spec/Client.spec.js
Normal file
@@ -0,0 +1,290 @@
|
||||
var Client = require('../src/LiveQuery/Client').Client;
|
||||
var ParseWebSocket = require('../src/LiveQuery/ParseWebSocketServer').ParseWebSocket;
|
||||
|
||||
describe('Client', function() {
|
||||
|
||||
it('can be initialized', function() {
|
||||
var parseWebSocket = new ParseWebSocket({});
|
||||
var client = new Client(1, parseWebSocket);
|
||||
|
||||
expect(client.id).toBe(1);
|
||||
expect(client.parseWebSocket).toBe(parseWebSocket);
|
||||
expect(client.subscriptionInfos.size).toBe(0);
|
||||
});
|
||||
|
||||
it('can push response', function() {
|
||||
var parseWebSocket = {
|
||||
send: jasmine.createSpy('send')
|
||||
};
|
||||
Client.pushResponse(parseWebSocket, 'message');
|
||||
|
||||
expect(parseWebSocket.send).toHaveBeenCalledWith('message');
|
||||
});
|
||||
|
||||
it('can push error', function() {
|
||||
var parseWebSocket = {
|
||||
send: jasmine.createSpy('send')
|
||||
};
|
||||
Client.pushError(parseWebSocket, 1, 'error', true);
|
||||
|
||||
var lastCall = parseWebSocket.send.calls.first();
|
||||
var messageJSON = JSON.parse(lastCall.args[0]);
|
||||
expect(messageJSON.op).toBe('error');
|
||||
expect(messageJSON.error).toBe('error');
|
||||
expect(messageJSON.code).toBe(1);
|
||||
expect(messageJSON.reconnect).toBe(true);
|
||||
});
|
||||
|
||||
it('can add subscription information', function() {
|
||||
var subscription = {};
|
||||
var fields = ['test'];
|
||||
var subscriptionInfo = {
|
||||
subscription: subscription,
|
||||
fields: fields
|
||||
}
|
||||
var client = new Client(1, {});
|
||||
client.addSubscriptionInfo(1, subscriptionInfo);
|
||||
|
||||
expect(client.subscriptionInfos.size).toBe(1);
|
||||
expect(client.subscriptionInfos.get(1)).toBe(subscriptionInfo);
|
||||
});
|
||||
|
||||
it('can get subscription information', function() {
|
||||
var subscription = {};
|
||||
var fields = ['test'];
|
||||
var subscriptionInfo = {
|
||||
subscription: subscription,
|
||||
fields: fields
|
||||
}
|
||||
var client = new Client(1, {});
|
||||
client.addSubscriptionInfo(1, subscriptionInfo);
|
||||
var subscriptionInfoAgain = client.getSubscriptionInfo(1);
|
||||
|
||||
expect(subscriptionInfoAgain).toBe(subscriptionInfo);
|
||||
});
|
||||
|
||||
it('can delete subscription information', function() {
|
||||
var subscription = {};
|
||||
var fields = ['test'];
|
||||
var subscriptionInfo = {
|
||||
subscription: subscription,
|
||||
fields: fields
|
||||
}
|
||||
var client = new Client(1, {});
|
||||
client.addSubscriptionInfo(1, subscriptionInfo);
|
||||
client.deleteSubscriptionInfo(1);
|
||||
|
||||
expect(client.subscriptionInfos.size).toBe(0);
|
||||
});
|
||||
|
||||
|
||||
it('can generate ParseObject JSON with null selected field', function() {
|
||||
var parseObjectJSON = {
|
||||
key : 'value',
|
||||
className: 'test',
|
||||
objectId: 'test',
|
||||
updatedAt: '2015-12-07T21:27:13.746Z',
|
||||
createdAt: '2015-12-07T21:27:13.746Z',
|
||||
ACL: 'test',
|
||||
};
|
||||
var client = new Client(1, {});
|
||||
|
||||
expect(client._toJSONWithFields(parseObjectJSON, null)).toBe(parseObjectJSON);
|
||||
});
|
||||
|
||||
it('can generate ParseObject JSON with undefined selected field', function() {
|
||||
var parseObjectJSON = {
|
||||
key : 'value',
|
||||
className: 'test',
|
||||
objectId: 'test',
|
||||
updatedAt: '2015-12-07T21:27:13.746Z',
|
||||
createdAt: '2015-12-07T21:27:13.746Z',
|
||||
ACL: 'test',
|
||||
};
|
||||
var client = new Client(1, {});
|
||||
|
||||
expect(client._toJSONWithFields(parseObjectJSON, undefined)).toBe(parseObjectJSON);
|
||||
});
|
||||
|
||||
it('can generate ParseObject JSON with selected fields', function() {
|
||||
var parseObjectJSON = {
|
||||
key : 'value',
|
||||
className: 'test',
|
||||
objectId: 'test',
|
||||
updatedAt: '2015-12-07T21:27:13.746Z',
|
||||
createdAt: '2015-12-07T21:27:13.746Z',
|
||||
ACL: 'test',
|
||||
test: 'test'
|
||||
};
|
||||
var client = new Client(1, {});
|
||||
|
||||
expect(client._toJSONWithFields(parseObjectJSON, ['test'])).toEqual({
|
||||
className: 'test',
|
||||
objectId: 'test',
|
||||
updatedAt: '2015-12-07T21:27:13.746Z',
|
||||
createdAt: '2015-12-07T21:27:13.746Z',
|
||||
ACL: 'test',
|
||||
test: 'test'
|
||||
});
|
||||
});
|
||||
|
||||
it('can generate ParseObject JSON with nonexistent selected fields', function() {
|
||||
var parseObjectJSON = {
|
||||
key : 'value',
|
||||
className: 'test',
|
||||
objectId: 'test',
|
||||
updatedAt: '2015-12-07T21:27:13.746Z',
|
||||
createdAt: '2015-12-07T21:27:13.746Z',
|
||||
ACL: 'test',
|
||||
test: 'test'
|
||||
};
|
||||
var client = new Client(1, {});
|
||||
var limitedParseObject = client._toJSONWithFields(parseObjectJSON, ['name']);
|
||||
|
||||
expect(limitedParseObject).toEqual({
|
||||
className: 'test',
|
||||
objectId: 'test',
|
||||
updatedAt: '2015-12-07T21:27:13.746Z',
|
||||
createdAt: '2015-12-07T21:27:13.746Z',
|
||||
ACL: 'test',
|
||||
});
|
||||
expect('name' in limitedParseObject).toBe(false);
|
||||
});
|
||||
|
||||
it('can push connect response', function() {
|
||||
var parseWebSocket = {
|
||||
send: jasmine.createSpy('send')
|
||||
};
|
||||
var client = new Client(1, parseWebSocket);
|
||||
client.pushConnect();
|
||||
|
||||
var lastCall = parseWebSocket.send.calls.first();
|
||||
var messageJSON = JSON.parse(lastCall.args[0]);
|
||||
expect(messageJSON.op).toBe('connected');
|
||||
expect(messageJSON.clientId).toBe(1);
|
||||
});
|
||||
|
||||
it('can push subscribe response', function() {
|
||||
var parseWebSocket = {
|
||||
send: jasmine.createSpy('send')
|
||||
};
|
||||
var client = new Client(1, parseWebSocket);
|
||||
client.pushSubscribe(2);
|
||||
|
||||
var lastCall = parseWebSocket.send.calls.first();
|
||||
var messageJSON = JSON.parse(lastCall.args[0]);
|
||||
expect(messageJSON.op).toBe('subscribed');
|
||||
expect(messageJSON.clientId).toBe(1);
|
||||
expect(messageJSON.requestId).toBe(2);
|
||||
});
|
||||
|
||||
it('can push unsubscribe response', function() {
|
||||
var parseWebSocket = {
|
||||
send: jasmine.createSpy('send')
|
||||
};
|
||||
var client = new Client(1, parseWebSocket);
|
||||
client.pushUnsubscribe(2);
|
||||
|
||||
var lastCall = parseWebSocket.send.calls.first();
|
||||
var messageJSON = JSON.parse(lastCall.args[0]);
|
||||
expect(messageJSON.op).toBe('unsubscribed');
|
||||
expect(messageJSON.clientId).toBe(1);
|
||||
expect(messageJSON.requestId).toBe(2);
|
||||
});
|
||||
|
||||
it('can push create response', function() {
|
||||
var parseObjectJSON = {
|
||||
key : 'value',
|
||||
className: 'test',
|
||||
objectId: 'test',
|
||||
updatedAt: '2015-12-07T21:27:13.746Z',
|
||||
createdAt: '2015-12-07T21:27:13.746Z',
|
||||
ACL: 'test',
|
||||
test: 'test'
|
||||
};
|
||||
var parseWebSocket = {
|
||||
send: jasmine.createSpy('send')
|
||||
};
|
||||
var client = new Client(1, parseWebSocket);
|
||||
client.pushCreate(2, parseObjectJSON);
|
||||
|
||||
var lastCall = parseWebSocket.send.calls.first();
|
||||
var messageJSON = JSON.parse(lastCall.args[0]);
|
||||
expect(messageJSON.op).toBe('create');
|
||||
expect(messageJSON.clientId).toBe(1);
|
||||
expect(messageJSON.requestId).toBe(2);
|
||||
expect(messageJSON.object).toEqual(parseObjectJSON);
|
||||
});
|
||||
|
||||
it('can push enter response', function() {
|
||||
var parseObjectJSON = {
|
||||
key : 'value',
|
||||
className: 'test',
|
||||
objectId: 'test',
|
||||
updatedAt: '2015-12-07T21:27:13.746Z',
|
||||
createdAt: '2015-12-07T21:27:13.746Z',
|
||||
ACL: 'test',
|
||||
test: 'test'
|
||||
};
|
||||
var parseWebSocket = {
|
||||
send: jasmine.createSpy('send')
|
||||
};
|
||||
var client = new Client(1, parseWebSocket);
|
||||
client.pushEnter(2, parseObjectJSON);
|
||||
|
||||
var lastCall = parseWebSocket.send.calls.first();
|
||||
var messageJSON = JSON.parse(lastCall.args[0]);
|
||||
expect(messageJSON.op).toBe('enter');
|
||||
expect(messageJSON.clientId).toBe(1);
|
||||
expect(messageJSON.requestId).toBe(2);
|
||||
expect(messageJSON.object).toEqual(parseObjectJSON);
|
||||
});
|
||||
|
||||
it('can push update response', function() {
|
||||
var parseObjectJSON = {
|
||||
key : 'value',
|
||||
className: 'test',
|
||||
objectId: 'test',
|
||||
updatedAt: '2015-12-07T21:27:13.746Z',
|
||||
createdAt: '2015-12-07T21:27:13.746Z',
|
||||
ACL: 'test',
|
||||
test: 'test'
|
||||
};
|
||||
var parseWebSocket = {
|
||||
send: jasmine.createSpy('send')
|
||||
};
|
||||
var client = new Client(1, parseWebSocket);
|
||||
client.pushUpdate(2, parseObjectJSON);
|
||||
|
||||
var lastCall = parseWebSocket.send.calls.first();
|
||||
var messageJSON = JSON.parse(lastCall.args[0]);
|
||||
expect(messageJSON.op).toBe('update');
|
||||
expect(messageJSON.clientId).toBe(1);
|
||||
expect(messageJSON.requestId).toBe(2);
|
||||
expect(messageJSON.object).toEqual(parseObjectJSON);
|
||||
});
|
||||
|
||||
it('can push leave response', function() {
|
||||
var parseObjectJSON = {
|
||||
key : 'value',
|
||||
className: 'test',
|
||||
objectId: 'test',
|
||||
updatedAt: '2015-12-07T21:27:13.746Z',
|
||||
createdAt: '2015-12-07T21:27:13.746Z',
|
||||
ACL: 'test',
|
||||
test: 'test'
|
||||
};
|
||||
var parseWebSocket = {
|
||||
send: jasmine.createSpy('send')
|
||||
};
|
||||
var client = new Client(1, parseWebSocket);
|
||||
client.pushLeave(2, parseObjectJSON);
|
||||
|
||||
var lastCall = parseWebSocket.send.calls.first();
|
||||
var messageJSON = JSON.parse(lastCall.args[0]);
|
||||
expect(messageJSON.op).toBe('leave');
|
||||
expect(messageJSON.clientId).toBe(1);
|
||||
expect(messageJSON.requestId).toBe(2);
|
||||
expect(messageJSON.object).toEqual(parseObjectJSON);
|
||||
});
|
||||
});
|
||||
44
spec/EventEmitterPubSub.spec.js
Normal file
44
spec/EventEmitterPubSub.spec.js
Normal file
@@ -0,0 +1,44 @@
|
||||
var EventEmitterPubSub = require('../src/LiveQuery/EventEmitterPubSub').EventEmitterPubSub;
|
||||
|
||||
describe('EventEmitterPubSub', function() {
|
||||
|
||||
it('can publish and subscribe', function() {
|
||||
var publisher = EventEmitterPubSub.createPublisher();
|
||||
var subscriber = EventEmitterPubSub.createSubscriber();
|
||||
subscriber.subscribe('testChannel');
|
||||
// Register mock checked for subscriber
|
||||
var isChecked = false;
|
||||
subscriber.on('message', function(channel, message) {
|
||||
isChecked = true;
|
||||
expect(channel).toBe('testChannel');
|
||||
expect(message).toBe('testMessage');
|
||||
});
|
||||
|
||||
publisher.publish('testChannel', 'testMessage');
|
||||
// Make sure the callback is checked
|
||||
expect(isChecked).toBe(true);
|
||||
});
|
||||
|
||||
it('can unsubscribe', function() {
|
||||
var publisher = EventEmitterPubSub.createPublisher();
|
||||
var subscriber = EventEmitterPubSub.createSubscriber();
|
||||
subscriber.subscribe('testChannel');
|
||||
subscriber.unsubscribe('testChannel');
|
||||
// Register mock checked for subscriber
|
||||
var isCalled = false;
|
||||
subscriber.on('message', function(channel, message) {
|
||||
isCalled = true;
|
||||
});
|
||||
|
||||
publisher.publish('testChannel', 'testMessage');
|
||||
// Make sure the callback is not called
|
||||
expect(isCalled).toBe(false);
|
||||
});
|
||||
|
||||
it('can unsubscribe not subscribing channel', function() {
|
||||
var subscriber = EventEmitterPubSub.createSubscriber();
|
||||
|
||||
// Make sure subscriber does not throw exception
|
||||
subscriber.unsubscribe('testChannel');
|
||||
});
|
||||
});
|
||||
70
spec/ParseCloudCodePublisher.spec.js
Normal file
70
spec/ParseCloudCodePublisher.spec.js
Normal file
@@ -0,0 +1,70 @@
|
||||
var ParseCloudCodePublisher = require('../src/LiveQuery/ParseCloudCodePublisher').ParseCloudCodePublisher;
|
||||
var Parse = require('parse/node');
|
||||
|
||||
describe('ParseCloudCodePublisher', function() {
|
||||
|
||||
beforeEach(function(done) {
|
||||
// Mock ParsePubSub
|
||||
var mockParsePubSub = {
|
||||
createPublisher: jasmine.createSpy('publish').and.returnValue({
|
||||
publish: jasmine.createSpy('publish'),
|
||||
on: jasmine.createSpy('on')
|
||||
}),
|
||||
createSubscriber: jasmine.createSpy('publish').and.returnValue({
|
||||
subscribe: jasmine.createSpy('subscribe'),
|
||||
on: jasmine.createSpy('on')
|
||||
})
|
||||
};
|
||||
jasmine.mockLibrary('../src/LiveQuery/ParsePubSub', 'ParsePubSub', mockParsePubSub);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can initialize', function() {
|
||||
var config = {}
|
||||
var publisher = new ParseCloudCodePublisher(config);
|
||||
|
||||
var ParsePubSub = require('../src/LiveQuery/ParsePubSub').ParsePubSub;
|
||||
expect(ParsePubSub.createPublisher).toHaveBeenCalledWith(config);
|
||||
});
|
||||
|
||||
it('can handle cloud code afterSave request', function() {
|
||||
var publisher = new ParseCloudCodePublisher({});
|
||||
publisher._onCloudCodeMessage = jasmine.createSpy('onCloudCodeMessage');
|
||||
var request = {};
|
||||
publisher.onCloudCodeAfterSave(request);
|
||||
|
||||
expect(publisher._onCloudCodeMessage).toHaveBeenCalledWith('afterSave', request);
|
||||
});
|
||||
|
||||
it('can handle cloud code afterDelete request', function() {
|
||||
var publisher = new ParseCloudCodePublisher({});
|
||||
publisher._onCloudCodeMessage = jasmine.createSpy('onCloudCodeMessage');
|
||||
var request = {};
|
||||
publisher.onCloudCodeAfterDelete(request);
|
||||
|
||||
expect(publisher._onCloudCodeMessage).toHaveBeenCalledWith('afterDelete', request);
|
||||
});
|
||||
|
||||
it('can handle cloud code request', function() {
|
||||
var publisher = new ParseCloudCodePublisher({});
|
||||
var currentParseObject = new Parse.Object('Test');
|
||||
currentParseObject.set('key', 'value');
|
||||
var originalParseObject = new Parse.Object('Test');
|
||||
originalParseObject.set('key', 'originalValue');
|
||||
var request = {
|
||||
object: currentParseObject,
|
||||
original: originalParseObject
|
||||
};
|
||||
publisher._onCloudCodeMessage('afterSave', request);
|
||||
|
||||
var args = publisher.parsePublisher.publish.calls.mostRecent().args;
|
||||
expect(args[0]).toBe('afterSave');
|
||||
var message = JSON.parse(args[1]);
|
||||
expect(message.currentParseObject).toEqual(request.object._toFullJSON());
|
||||
expect(message.originalParseObject).toEqual(request.original._toFullJSON());
|
||||
});
|
||||
|
||||
afterEach(function(){
|
||||
jasmine.restoreLibrary('../src/LiveQuery/ParsePubSub', 'ParsePubSub');
|
||||
});
|
||||
});
|
||||
962
spec/ParseLiveQueryServer.spec.js
Normal file
962
spec/ParseLiveQueryServer.spec.js
Normal file
@@ -0,0 +1,962 @@
|
||||
var Parse = require('parse/node');
|
||||
var ParseLiveQueryServer = require('../src/LiveQuery/ParseLiveQueryServer').ParseLiveQueryServer;
|
||||
|
||||
// Global mock info
|
||||
var queryHashValue = 'hash';
|
||||
var testUserId = 'userId';
|
||||
var testClassName = 'TestObject';
|
||||
|
||||
describe('ParseLiveQueryServer', function() {
|
||||
|
||||
beforeEach(function(done) {
|
||||
// Mock ParseWebSocketServer
|
||||
var mockParseWebSocketServer = jasmine.createSpy('ParseWebSocketServer');
|
||||
jasmine.mockLibrary('../src/LiveQuery/ParseWebSocketServer', 'ParseWebSocketServer', mockParseWebSocketServer);
|
||||
// Mock Client
|
||||
var mockClient = function() {
|
||||
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');
|
||||
}
|
||||
mockClient.pushError = jasmine.createSpy('pushError');
|
||||
jasmine.mockLibrary('../src/LiveQuery/Client', 'Client', mockClient);
|
||||
// Mock Subscription
|
||||
var mockSubscriotion = function() {
|
||||
this.addClientSubscription = jasmine.createSpy('addClientSubscription');
|
||||
this.deleteClientSubscription = jasmine.createSpy('deleteClientSubscription');
|
||||
}
|
||||
jasmine.mockLibrary('../src/LiveQuery/Subscription', 'Subscription', mockSubscriotion);
|
||||
// Mock queryHash
|
||||
var mockQueryHash = jasmine.createSpy('matchesQuery').and.returnValue(queryHashValue);
|
||||
jasmine.mockLibrary('../src/LiveQuery/QueryTools', 'queryHash', mockQueryHash);
|
||||
// Mock matchesQuery
|
||||
var mockMatchesQuery = jasmine.createSpy('matchesQuery').and.returnValue(true);
|
||||
jasmine.mockLibrary('../src/LiveQuery/QueryTools', 'matchesQuery', mockMatchesQuery);
|
||||
// Mock tv4
|
||||
var mockValidate = function() {
|
||||
return true;
|
||||
}
|
||||
jasmine.mockLibrary('tv4', 'validate', mockValidate);
|
||||
// Mock ParsePubSub
|
||||
var 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('../src/LiveQuery/ParsePubSub', 'ParsePubSub', mockParsePubSub);
|
||||
// Make mock SessionTokenCache
|
||||
var mockSessionTokenCache = function(){
|
||||
this.getUserId = function(sessionToken){
|
||||
if (typeof sessionToken === 'undefined') {
|
||||
return Parse.Promise.as(undefined);
|
||||
}
|
||||
if (sessionToken === null) {
|
||||
return Parse.Promise.error();
|
||||
}
|
||||
return Parse.Promise.as(testUserId);
|
||||
};
|
||||
};
|
||||
jasmine.mockLibrary('../src/LiveQuery/SessionTokenCache', 'SessionTokenCache', mockSessionTokenCache);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can be initialized', function() {
|
||||
var httpServer = {};
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, httpServer);
|
||||
|
||||
expect(parseLiveQueryServer.clientId).toBe(0);
|
||||
expect(parseLiveQueryServer.clients.size).toBe(0);
|
||||
expect(parseLiveQueryServer.subscriptions.size).toBe(0);
|
||||
});
|
||||
|
||||
it('can handle connect command', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
var parseWebSocket = {
|
||||
clientId: -1
|
||||
};
|
||||
parseLiveQueryServer._validateKeys = jasmine.createSpy('validateKeys').and.returnValue(true);
|
||||
parseLiveQueryServer._handleConnect(parseWebSocket);
|
||||
|
||||
expect(parseLiveQueryServer.clientId).toBe(1);
|
||||
expect(parseWebSocket.clientId).toBe(0);
|
||||
var client = parseLiveQueryServer.clients.get(0);
|
||||
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() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
var incompleteParseConn = {
|
||||
};
|
||||
parseLiveQueryServer._handleSubscribe(incompleteParseConn, {});
|
||||
|
||||
var Client = require('../src/LiveQuery/Client').Client;
|
||||
expect(Client.pushError).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can handle subscribe command with new query', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Add mock client
|
||||
var clientId = 1;
|
||||
var client = addMockClient(parseLiveQueryServer, clientId);
|
||||
// Handle mock subscription
|
||||
var parseWebSocket = {
|
||||
clientId: clientId
|
||||
};
|
||||
var query = {
|
||||
className: 'test',
|
||||
where: {
|
||||
key: 'value'
|
||||
},
|
||||
fields: [ 'test' ]
|
||||
}
|
||||
var requestId = 2;
|
||||
var request = {
|
||||
query: query,
|
||||
requestId: requestId,
|
||||
sessionToken: 'sessionToken'
|
||||
}
|
||||
parseLiveQueryServer._handleSubscribe(parseWebSocket, request);
|
||||
|
||||
// Make sure we add the subscription to the server
|
||||
var subscriptions = parseLiveQueryServer.subscriptions;
|
||||
expect(subscriptions.size).toBe(1);
|
||||
expect(subscriptions.get(query.className)).not.toBeNull();
|
||||
var 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
|
||||
var subscription = classSubscriptions.get('hash');
|
||||
expect(subscription.addClientSubscription).toHaveBeenCalledWith(clientId, requestId);
|
||||
// Make sure we add subscriptionInfo to the client
|
||||
var 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() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Add two mock clients
|
||||
var clientId = 1;
|
||||
var client = addMockClient(parseLiveQueryServer, clientId);
|
||||
var clientIdAgain = 2;
|
||||
var clientAgain = addMockClient(parseLiveQueryServer, clientIdAgain);
|
||||
// Add subscription for mock client 1
|
||||
var parseWebSocket = {
|
||||
clientId: clientId
|
||||
};
|
||||
var requestId = 2;
|
||||
var query = {
|
||||
className: 'test',
|
||||
where: {
|
||||
key: 'value'
|
||||
},
|
||||
fields: [ 'test' ]
|
||||
}
|
||||
addMockSubscription(parseLiveQueryServer, clientId, requestId, parseWebSocket, query);
|
||||
// Add subscription for mock client 2
|
||||
var parseWebSocketAgain = {
|
||||
clientId: clientIdAgain
|
||||
};
|
||||
var queryAgain = {
|
||||
className: 'test',
|
||||
where: {
|
||||
key: 'value'
|
||||
},
|
||||
fields: [ 'testAgain' ]
|
||||
}
|
||||
var requestIdAgain = 1;
|
||||
addMockSubscription(parseLiveQueryServer, clientIdAgain, requestIdAgain, parseWebSocketAgain, queryAgain);
|
||||
|
||||
// Make sure we only have one subscription
|
||||
var subscriptions = parseLiveQueryServer.subscriptions;
|
||||
expect(subscriptions.size).toBe(1);
|
||||
expect(subscriptions.get(query.className)).not.toBeNull();
|
||||
var classSubscriptions = subscriptions.get(query.className);
|
||||
expect(classSubscriptions.size).toBe(1);
|
||||
expect(classSubscriptions.get('hash')).not.toBeNull();
|
||||
// Make sure we add clientInfo to the subscription
|
||||
var subscription = classSubscriptions.get('hash');
|
||||
// Make sure client 2 info has been added
|
||||
var 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() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
var incompleteParseConn = {
|
||||
};
|
||||
parseLiveQueryServer._handleUnsubscribe(incompleteParseConn, {});
|
||||
|
||||
var Client = require('../src/LiveQuery/Client').Client;
|
||||
expect(Client.pushError).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can handle unsubscribe command without not existed client', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
var parseWebSocket = {
|
||||
clientId: 1
|
||||
};
|
||||
parseLiveQueryServer._handleUnsubscribe(parseWebSocket, {});
|
||||
|
||||
var Client = require('../src/LiveQuery/Client').Client;
|
||||
expect(Client.pushError).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can handle unsubscribe command without not existed query', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Add mock client
|
||||
var clientId = 1;
|
||||
var client = addMockClient(parseLiveQueryServer, clientId);
|
||||
// Handle unsubscribe command
|
||||
var parseWebSocket = {
|
||||
clientId: 1
|
||||
};
|
||||
parseLiveQueryServer._handleUnsubscribe(parseWebSocket, {});
|
||||
|
||||
var Client = require('../src/LiveQuery/Client').Client;
|
||||
expect(Client.pushError).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can handle unsubscribe command', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Add mock client
|
||||
var clientId = 1;
|
||||
var client = addMockClient(parseLiveQueryServer, clientId);
|
||||
// Add subscription for mock client
|
||||
var parseWebSocket = {
|
||||
clientId: 1
|
||||
};
|
||||
var requestId = 2;
|
||||
var subscription = addMockSubscription(parseLiveQueryServer, clientId, requestId, parseWebSocket);
|
||||
// Mock client.getSubscriptionInfo
|
||||
var subscriptionInfo = client.addSubscriptionInfo.calls.mostRecent().args[1];
|
||||
client.getSubscriptionInfo = function() {
|
||||
return subscriptionInfo;
|
||||
};
|
||||
// Handle unsubscribe command
|
||||
var 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
|
||||
var subscriptions = parseLiveQueryServer.subscriptions;
|
||||
expect(subscriptions.size).toBe(0);
|
||||
});
|
||||
|
||||
it('can set connect command message handler for a parseWebSocket', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Register mock connect/subscribe/unsubscribe handler for the server
|
||||
parseLiveQueryServer._handleConnect = jasmine.createSpy('_handleSubscribe');
|
||||
// Make mock parseWebsocket
|
||||
var EventEmitter = require('events');
|
||||
var parseWebSocket = new EventEmitter();
|
||||
// Register message handlers for the parseWebSocket
|
||||
parseLiveQueryServer._onConnect(parseWebSocket);
|
||||
|
||||
// Check connect request
|
||||
var connectRequest = {
|
||||
op: 'connect'
|
||||
};
|
||||
// Trigger message event
|
||||
parseWebSocket.emit('message', connectRequest);
|
||||
// Make sure _handleConnect is called
|
||||
var args = parseLiveQueryServer._handleConnect.calls.mostRecent().args;
|
||||
expect(args[0]).toBe(parseWebSocket);
|
||||
});
|
||||
|
||||
it('can set subscribe command message handler for a parseWebSocket', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Register mock connect/subscribe/unsubscribe handler for the server
|
||||
parseLiveQueryServer._handleSubscribe = jasmine.createSpy('_handleSubscribe');
|
||||
// Make mock parseWebsocket
|
||||
var EventEmitter = require('events');
|
||||
var parseWebSocket = new EventEmitter();
|
||||
// Register message handlers for the parseWebSocket
|
||||
parseLiveQueryServer._onConnect(parseWebSocket);
|
||||
|
||||
// Check subscribe request
|
||||
var subscribeRequest = '{"op":"subscribe"}';
|
||||
// Trigger message event
|
||||
parseWebSocket.emit('message', subscribeRequest);
|
||||
// Make sure _handleSubscribe is called
|
||||
var 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() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Register mock connect/subscribe/unsubscribe handler for the server
|
||||
parseLiveQueryServer._handleUnsubscribe = jasmine.createSpy('_handleSubscribe');
|
||||
// Make mock parseWebsocket
|
||||
var EventEmitter = require('events');
|
||||
var parseWebSocket = new EventEmitter();
|
||||
// Register message handlers for the parseWebSocket
|
||||
parseLiveQueryServer._onConnect(parseWebSocket);
|
||||
|
||||
// Check unsubscribe request
|
||||
var unsubscribeRequest = '{"op":"unsubscribe"}';
|
||||
// Trigger message event
|
||||
parseWebSocket.emit('message', unsubscribeRequest);
|
||||
// Make sure _handleUnsubscribe is called
|
||||
var args = parseLiveQueryServer._handleUnsubscribe.calls.mostRecent().args;
|
||||
expect(args[0]).toBe(parseWebSocket);
|
||||
expect(JSON.stringify(args[1])).toBe(unsubscribeRequest);
|
||||
});
|
||||
|
||||
it('can set unknown command message handler for a parseWebSocket', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Make mock parseWebsocket
|
||||
var EventEmitter = require('events');
|
||||
var parseWebSocket = new EventEmitter();
|
||||
// Register message handlers for the parseWebSocket
|
||||
parseLiveQueryServer._onConnect(parseWebSocket);
|
||||
|
||||
// Check unknown request
|
||||
var unknownRequest = '{"op":"unknown"}';
|
||||
// Trigger message event
|
||||
parseWebSocket.emit('message', unknownRequest);
|
||||
var Client = require('../src/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() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
var EventEmitter = require('events');
|
||||
var 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');
|
||||
});
|
||||
|
||||
// TODO: Test server can set disconnect command message handler for a parseWebSocket
|
||||
|
||||
it('has no subscription and can handle object delete command', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Make deletedParseObject
|
||||
var parseObject = new Parse.Object(testClassName);
|
||||
parseObject._finishFetch({
|
||||
key: 'value',
|
||||
className: testClassName
|
||||
});
|
||||
// Make mock message
|
||||
var 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() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Make deletedParseObject
|
||||
var parseObject = new Parse.Object(testClassName);
|
||||
parseObject._finishFetch({
|
||||
key: 'value',
|
||||
className: testClassName
|
||||
});
|
||||
// Make mock message
|
||||
var message = {
|
||||
currentParseObject: parseObject
|
||||
};
|
||||
|
||||
// Add mock client
|
||||
var clientId = 1;
|
||||
addMockClient(parseLiveQueryServer, clientId);
|
||||
// Add mock subscription
|
||||
var requestId = 2;
|
||||
addMockSubscription(parseLiveQueryServer, clientId, requestId);
|
||||
var 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) {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Make deletedParseObject
|
||||
var parseObject = new Parse.Object(testClassName);
|
||||
parseObject._finishFetch({
|
||||
key: 'value',
|
||||
className: testClassName
|
||||
});
|
||||
// Make mock message
|
||||
var message = {
|
||||
currentParseObject: parseObject
|
||||
};
|
||||
// Add mock client
|
||||
var clientId = 1;
|
||||
addMockClient(parseLiveQueryServer, clientId);
|
||||
// Add mock subscription
|
||||
var requestId = 2;
|
||||
addMockSubscription(parseLiveQueryServer, clientId, requestId);
|
||||
var client = parseLiveQueryServer.clients.get(clientId);
|
||||
// Mock _matchesSubscription to return matching
|
||||
parseLiveQueryServer._matchesSubscription = function() {
|
||||
return true;
|
||||
};
|
||||
parseLiveQueryServer._matchesACL = function() {
|
||||
return Parse.Promise.as(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() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Make mock request message
|
||||
var 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) {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Make mock request message
|
||||
var message = generateMockMessage();
|
||||
// Add mock client
|
||||
var clientId = 1;
|
||||
var client = addMockClient(parseLiveQueryServer, clientId);
|
||||
// Add mock subscription
|
||||
var requestId = 2;
|
||||
addMockSubscription(parseLiveQueryServer, clientId, requestId);
|
||||
// Mock _matchesSubscription to return not matching
|
||||
parseLiveQueryServer._matchesSubscription = function() {
|
||||
return false;
|
||||
};
|
||||
parseLiveQueryServer._matchesACL = function() {
|
||||
return Parse.Promise.as(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) {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Make mock request message
|
||||
var message = generateMockMessage(true);
|
||||
// Add mock client
|
||||
var clientId = 1;
|
||||
var client = addMockClient(parseLiveQueryServer, clientId);
|
||||
// Add mock subscription
|
||||
var 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
|
||||
var counter = 0;
|
||||
parseLiveQueryServer._matchesSubscription = function(parseObject, subscription){
|
||||
if (!parseObject) {
|
||||
return false;
|
||||
}
|
||||
counter += 1;
|
||||
return counter % 2 === 0;
|
||||
};
|
||||
parseLiveQueryServer._matchesACL = function() {
|
||||
return Parse.Promise.as(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) {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Make mock request message
|
||||
var message = generateMockMessage(true);
|
||||
// Add mock client
|
||||
var clientId = 1;
|
||||
var client = addMockClient(parseLiveQueryServer, clientId);
|
||||
// Add mock subscription
|
||||
var requestId = 2;
|
||||
addMockSubscription(parseLiveQueryServer, clientId, requestId);
|
||||
// Mock _matchesSubscription to return matching
|
||||
parseLiveQueryServer._matchesSubscription = function(parseObject, subscription){
|
||||
if (!parseObject) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
parseLiveQueryServer._matchesACL = function() {
|
||||
return Parse.Promise.as(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) {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Make mock request message
|
||||
var message = generateMockMessage(true);
|
||||
// Add mock client
|
||||
var clientId = 1;
|
||||
var client = addMockClient(parseLiveQueryServer, clientId);
|
||||
// Add mock subscription
|
||||
var 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
|
||||
var counter = 0;
|
||||
parseLiveQueryServer._matchesSubscription = function(parseObject, subscription){
|
||||
if (!parseObject) {
|
||||
return false;
|
||||
}
|
||||
counter += 1;
|
||||
return counter % 2 !== 0;
|
||||
};
|
||||
parseLiveQueryServer._matchesACL = function() {
|
||||
return Parse.Promise.as(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 object create command which matches some subscriptions', function(done) {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Make mock request message
|
||||
var message = generateMockMessage();
|
||||
// Add mock client
|
||||
var clientId = 1;
|
||||
var client = addMockClient(parseLiveQueryServer, clientId);
|
||||
// Add mock subscription
|
||||
var requestId = 2;
|
||||
addMockSubscription(parseLiveQueryServer, clientId, requestId);
|
||||
// Mock _matchesSubscription to return matching
|
||||
parseLiveQueryServer._matchesSubscription = function(parseObject, subscription){
|
||||
if (!parseObject) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
parseLiveQueryServer._matchesACL = function() {
|
||||
return Parse.Promise.as(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 match subscription for null or undefined parse object', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Make mock subscription
|
||||
var 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() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Make mock subscription
|
||||
var subscription = {
|
||||
query: {}
|
||||
}
|
||||
var parseObject = {};
|
||||
expect(parseLiveQueryServer._matchesSubscription(parseObject, subscription)).toBe(true);
|
||||
// Make sure matchesQuery is called
|
||||
var matchesQuery = require('../src/LiveQuery/QueryTools').matchesQuery;
|
||||
expect(matchesQuery).toHaveBeenCalledWith(parseObject, subscription.query);
|
||||
});
|
||||
|
||||
it('can inflate parse object', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
// Make mock request
|
||||
var objectJSON = {
|
||||
"className":"testClassName",
|
||||
"createdAt":"2015-12-22T01:51:12.955Z",
|
||||
"key":"value",
|
||||
"objectId":"BfwxBCz6yW",
|
||||
"updatedAt":"2016-01-05T00:46:45.659Z"
|
||||
};
|
||||
var originalObjectJSON = {
|
||||
"className":"testClassName",
|
||||
"createdAt":"2015-12-22T01:51:12.955Z",
|
||||
"key":"originalValue",
|
||||
"objectId":"BfwxBCz6yW",
|
||||
"updatedAt":"2016-01-05T00:46:45.659Z"
|
||||
};
|
||||
var message = {
|
||||
currentParseObject: objectJSON,
|
||||
originalParseObject: originalObjectJSON
|
||||
};
|
||||
// Inflate the object
|
||||
parseLiveQueryServer._inflateParseObject(message);
|
||||
|
||||
// Verify object
|
||||
var 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
|
||||
var 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 match undefined ACL', function(done) {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
var client = {};
|
||||
var 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) {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
var acl = new Parse.ACL();
|
||||
var client = {
|
||||
getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue(undefined)
|
||||
};
|
||||
var requestId = 0;
|
||||
|
||||
var isChecked = false;
|
||||
parseLiveQueryServer._matchesACL(acl, client, requestId).then(function(isMatched) {
|
||||
expect(isMatched).toBe(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can match ACL with public read access', function(done) {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
var acl = new Parse.ACL();
|
||||
acl.setPublicReadAccess(true);
|
||||
var client = {
|
||||
getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({
|
||||
sessionToken: 'sessionToken'
|
||||
})
|
||||
};
|
||||
var 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) {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
var acl = new Parse.ACL();
|
||||
acl.setReadAccess(testUserId, true);
|
||||
var client = {
|
||||
getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({
|
||||
sessionToken: 'sessionToken'
|
||||
})
|
||||
};
|
||||
var 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) {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
var acl = new Parse.ACL();
|
||||
acl.setReadAccess(testUserId, true);
|
||||
// Mock sessionTokenCache will return false when sessionToken is undefined
|
||||
var client = {
|
||||
sessionToken: 'sessionToken',
|
||||
getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({
|
||||
sessionToken: undefined
|
||||
})
|
||||
};
|
||||
var 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) {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
var acl = new Parse.ACL();
|
||||
acl.setReadAccess(testUserId, true);
|
||||
// Mock sessionTokenCache will return false when sessionToken is undefined
|
||||
var client = {
|
||||
sessionToken: undefined,
|
||||
getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({
|
||||
sessionToken: undefined
|
||||
})
|
||||
};
|
||||
var 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) {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
var 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
|
||||
var client = {
|
||||
getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({
|
||||
sessionToken: null
|
||||
})
|
||||
};
|
||||
var 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) {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer(10, 10, {});
|
||||
var acl = new Parse.ACL();
|
||||
acl.setReadAccess(testUserId, true);
|
||||
// Mock sessionTokenCache will return error when sessionToken is null
|
||||
var client = {
|
||||
sessionToken: null,
|
||||
getSubscriptionInfo: jasmine.createSpy('getSubscriptionInfo').and.returnValue({
|
||||
sessionToken: null
|
||||
})
|
||||
};
|
||||
var requestId = 0;
|
||||
|
||||
parseLiveQueryServer._matchesACL(acl, client, requestId).then(function(isMatched) {
|
||||
expect(isMatched).toBe(false);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can validate key when valid key is provided', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer({}, {
|
||||
keyPairs: {
|
||||
clientKey: 'test'
|
||||
}
|
||||
});
|
||||
var request = {
|
||||
clientKey: 'test'
|
||||
}
|
||||
|
||||
expect(parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)).toBeTruthy();
|
||||
});
|
||||
|
||||
it('can validate key when invalid key is provided', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer({}, {
|
||||
keyPairs: {
|
||||
clientKey: 'test'
|
||||
}
|
||||
});
|
||||
var request = {
|
||||
clientKey: 'error'
|
||||
}
|
||||
|
||||
expect(parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)).not.toBeTruthy();
|
||||
});
|
||||
|
||||
it('can validate key when key is not provided', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer({}, {
|
||||
keyPairs: {
|
||||
clientKey: 'test'
|
||||
}
|
||||
});
|
||||
var request = {
|
||||
}
|
||||
|
||||
expect(parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)).not.toBeTruthy();
|
||||
});
|
||||
|
||||
it('can validate key when validKerPairs is empty', function() {
|
||||
var parseLiveQueryServer = new ParseLiveQueryServer({}, {});
|
||||
var request = {
|
||||
}
|
||||
|
||||
expect(parseLiveQueryServer._validateKeys(request, parseLiveQueryServer.keyPairs)).toBeTruthy();
|
||||
});
|
||||
|
||||
afterEach(function(){
|
||||
jasmine.restoreLibrary('../src/LiveQuery/ParseWebSocketServer', 'ParseWebSocketServer');
|
||||
jasmine.restoreLibrary('../src/LiveQuery/Client', 'Client');
|
||||
jasmine.restoreLibrary('../src/LiveQuery/Subscription', 'Subscription');
|
||||
jasmine.restoreLibrary('../src/LiveQuery/QueryTools', 'queryHash');
|
||||
jasmine.restoreLibrary('../src/LiveQuery/QueryTools', 'matchesQuery');
|
||||
jasmine.restoreLibrary('tv4', 'validate');
|
||||
jasmine.restoreLibrary('../src/LiveQuery/ParsePubSub', 'ParsePubSub');
|
||||
jasmine.restoreLibrary('../src/LiveQuery/SessionTokenCache', 'SessionTokenCache');
|
||||
});
|
||||
|
||||
// Helper functions to add mock client and subscription to a liveQueryServer
|
||||
function addMockClient(parseLiveQueryServer, clientId) {
|
||||
var Client = require('../src/LiveQuery/Client').Client;
|
||||
var 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) {
|
||||
var 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' ]
|
||||
};
|
||||
}
|
||||
var request = {
|
||||
query: query,
|
||||
requestId: requestId,
|
||||
sessionToken: 'sessionToken'
|
||||
};
|
||||
parseLiveQueryServer._handleSubscribe(parseWebSocket, request);
|
||||
|
||||
// Make mock subscription
|
||||
var 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) {
|
||||
var parseObject = new Parse.Object(testClassName);
|
||||
parseObject._finishFetch({
|
||||
key: 'value',
|
||||
className: testClassName
|
||||
});
|
||||
var message = {
|
||||
currentParseObject: parseObject
|
||||
};
|
||||
if (hasOriginalParseObject) {
|
||||
var originalParseObject = new Parse.Object(testClassName);
|
||||
originalParseObject._finishFetch({
|
||||
key: 'originalValue',
|
||||
className: testClassName
|
||||
});
|
||||
message.originalParseObject = originalParseObject;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
});
|
||||
65
spec/ParsePubSub.spec.js
Normal file
65
spec/ParsePubSub.spec.js
Normal file
@@ -0,0 +1,65 @@
|
||||
var ParsePubSub = require('../src/LiveQuery/ParsePubSub').ParsePubSub;
|
||||
|
||||
describe('ParsePubSub', function() {
|
||||
|
||||
beforeEach(function(done) {
|
||||
// Mock RedisPubSub
|
||||
var mockRedisPubSub = {
|
||||
createPublisher: jasmine.createSpy('createPublisherRedis'),
|
||||
createSubscriber: jasmine.createSpy('createSubscriberRedis')
|
||||
};
|
||||
jasmine.mockLibrary('../src/LiveQuery/RedisPubSub', 'RedisPubSub', mockRedisPubSub);
|
||||
// Mock EventEmitterPubSub
|
||||
var mockEventEmitterPubSub = {
|
||||
createPublisher: jasmine.createSpy('createPublisherEventEmitter'),
|
||||
createSubscriber: jasmine.createSpy('createSubscriberEventEmitter')
|
||||
};
|
||||
jasmine.mockLibrary('../src/LiveQuery/EventEmitterPubSub', 'EventEmitterPubSub', mockEventEmitterPubSub);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can create redis publisher', function() {
|
||||
var publisher = ParsePubSub.createPublisher({
|
||||
redisURL: 'redisURL'
|
||||
});
|
||||
|
||||
var RedisPubSub = require('../src/LiveQuery/RedisPubSub').RedisPubSub;
|
||||
var EventEmitterPubSub = require('../src/LiveQuery/EventEmitterPubSub').EventEmitterPubSub;
|
||||
expect(RedisPubSub.createPublisher).toHaveBeenCalledWith('redisURL');
|
||||
expect(EventEmitterPubSub.createPublisher).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can create event emitter publisher', function() {
|
||||
var publisher = ParsePubSub.createPublisher({});
|
||||
|
||||
var RedisPubSub = require('../src/LiveQuery/RedisPubSub').RedisPubSub;
|
||||
var EventEmitterPubSub = require('../src/LiveQuery/EventEmitterPubSub').EventEmitterPubSub;
|
||||
expect(RedisPubSub.createPublisher).not.toHaveBeenCalled();
|
||||
expect(EventEmitterPubSub.createPublisher).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can create redis subscriber', function() {
|
||||
var subscriber = ParsePubSub.createSubscriber({
|
||||
redisURL: 'redisURL'
|
||||
});
|
||||
|
||||
var RedisPubSub = require('../src/LiveQuery/RedisPubSub').RedisPubSub;
|
||||
var EventEmitterPubSub = require('../src/LiveQuery/EventEmitterPubSub').EventEmitterPubSub;
|
||||
expect(RedisPubSub.createSubscriber).toHaveBeenCalledWith('redisURL');
|
||||
expect(EventEmitterPubSub.createSubscriber).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can create event emitter subscriber', function() {
|
||||
var subscriptionInfos = ParsePubSub.createSubscriber({});
|
||||
|
||||
var RedisPubSub = require('../src/LiveQuery/RedisPubSub').RedisPubSub;
|
||||
var EventEmitterPubSub = require('../src/LiveQuery/EventEmitterPubSub').EventEmitterPubSub;
|
||||
expect(RedisPubSub.createSubscriber).not.toHaveBeenCalled();
|
||||
expect(EventEmitterPubSub.createSubscriber).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
afterEach(function(){
|
||||
jasmine.restoreLibrary('../src/LiveQuery/RedisPubSub', 'RedisPubSub');
|
||||
jasmine.restoreLibrary('../src/LiveQuery/EventEmitterPubSub', 'EventEmitterPubSub');
|
||||
});
|
||||
});
|
||||
43
spec/ParseWebSocket.spec.js
Normal file
43
spec/ParseWebSocket.spec.js
Normal file
@@ -0,0 +1,43 @@
|
||||
var ParseWebSocket = require('../src/LiveQuery/ParseWebSocketServer').ParseWebSocket;
|
||||
|
||||
describe('ParseWebSocket', function() {
|
||||
|
||||
it('can be initialized', function() {
|
||||
var ws = {};
|
||||
var parseWebSocket = new ParseWebSocket(ws);
|
||||
|
||||
expect(parseWebSocket.ws).toBe(ws);
|
||||
});
|
||||
|
||||
it('can handle events defined in typeMap', function() {
|
||||
var ws = {
|
||||
on: jasmine.createSpy('on')
|
||||
};
|
||||
var callback = {};
|
||||
var parseWebSocket = new ParseWebSocket(ws);
|
||||
parseWebSocket.on('disconnect', callback);
|
||||
|
||||
expect(parseWebSocket.ws.on).toHaveBeenCalledWith('close', callback);
|
||||
});
|
||||
|
||||
it('can handle events which are not defined in typeMap', function() {
|
||||
var ws = {
|
||||
on: jasmine.createSpy('on')
|
||||
};
|
||||
var callback = {};
|
||||
var parseWebSocket = new ParseWebSocket(ws);
|
||||
parseWebSocket.on('open', callback);
|
||||
|
||||
expect(parseWebSocket.ws.on).toHaveBeenCalledWith('open', callback);
|
||||
});
|
||||
|
||||
it('can send a message', function() {
|
||||
var ws = {
|
||||
send: jasmine.createSpy('send')
|
||||
};
|
||||
var parseWebSocket = new ParseWebSocket(ws);
|
||||
parseWebSocket.send('message')
|
||||
|
||||
expect(parseWebSocket.ws.send).toHaveBeenCalledWith('message');
|
||||
});
|
||||
});
|
||||
37
spec/ParseWebSocketServer.spec.js
Normal file
37
spec/ParseWebSocketServer.spec.js
Normal file
@@ -0,0 +1,37 @@
|
||||
var ParseWebSocketServer = require('../src/LiveQuery/ParseWebSocketServer').ParseWebSocketServer;
|
||||
|
||||
describe('ParseWebSocketServer', function() {
|
||||
|
||||
beforeEach(function(done) {
|
||||
// Mock ws server
|
||||
var EventEmitter = require('events');
|
||||
var mockServer = function() {
|
||||
return new EventEmitter();
|
||||
};
|
||||
jasmine.mockLibrary('ws', 'Server', mockServer);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can handle connect event when ws is open', function(done) {
|
||||
var onConnectCallback = jasmine.createSpy('onConnectCallback');
|
||||
var parseWebSocketServer = new ParseWebSocketServer({}, onConnectCallback, 5).server;
|
||||
var ws = {
|
||||
readyState: 0,
|
||||
OPEN: 0,
|
||||
ping: jasmine.createSpy('ping')
|
||||
};
|
||||
parseWebSocketServer.emit('connection', ws);
|
||||
|
||||
// Make sure callback is called
|
||||
expect(onConnectCallback).toHaveBeenCalled();
|
||||
// Make sure we ping to the client
|
||||
setTimeout(function() {
|
||||
expect(ws.ping).toHaveBeenCalled();
|
||||
done();
|
||||
}, 10)
|
||||
});
|
||||
|
||||
afterEach(function(){
|
||||
jasmine.restoreLibrary('ws', 'Server');
|
||||
});
|
||||
});
|
||||
384
spec/QueryTools.spec.js
Normal file
384
spec/QueryTools.spec.js
Normal file
@@ -0,0 +1,384 @@
|
||||
var Parse = require('parse/node');
|
||||
|
||||
var Id = require('../src/LiveQuery/Id');
|
||||
var QueryTools = require('../src/LiveQuery/QueryTools');
|
||||
var queryHash = QueryTools.queryHash;
|
||||
var matchesQuery = QueryTools.matchesQuery;
|
||||
|
||||
var Item = Parse.Object.extend('Item');
|
||||
|
||||
describe('queryHash', function() {
|
||||
|
||||
it('should always hash a query to the same string', function() {
|
||||
var q = new Parse.Query(Item);
|
||||
q.equalTo('field', 'value');
|
||||
q.exists('name');
|
||||
q.ascending('createdAt');
|
||||
q.limit(10);
|
||||
var firstHash = queryHash(q);
|
||||
var secondHash = queryHash(q);
|
||||
expect(firstHash).toBe(secondHash);
|
||||
});
|
||||
|
||||
it('should return equivalent hashes for equivalent queries', function() {
|
||||
var q1 = new Parse.Query(Item);
|
||||
q1.equalTo('field', 'value');
|
||||
q1.exists('name');
|
||||
q1.lessThan('age', 30);
|
||||
q1.greaterThan('age', 3);
|
||||
q1.ascending('createdAt');
|
||||
q1.include(['name', 'age']);
|
||||
q1.limit(10);
|
||||
|
||||
var q2 = new Parse.Query(Item);
|
||||
q2.limit(10);
|
||||
q2.greaterThan('age', 3);
|
||||
q2.lessThan('age', 30);
|
||||
q2.include(['name', 'age']);
|
||||
q2.ascending('createdAt');
|
||||
q2.exists('name');
|
||||
q2.equalTo('field', 'value');
|
||||
|
||||
var firstHash = queryHash(q1);
|
||||
var secondHash = queryHash(q2);
|
||||
expect(firstHash).toBe(secondHash);
|
||||
|
||||
q1.containedIn('fruit', ['apple', 'banana', 'cherry']);
|
||||
firstHash = queryHash(q1);
|
||||
expect(firstHash).not.toBe(secondHash);
|
||||
|
||||
q2.containedIn('fruit', ['banana', 'cherry', 'apple']);
|
||||
secondHash = queryHash(q2);
|
||||
expect(secondHash).toBe(firstHash);
|
||||
|
||||
q1.containedIn('fruit', ['coconut']);
|
||||
firstHash = queryHash(q1);
|
||||
expect(firstHash).not.toBe(secondHash);
|
||||
|
||||
q1 = new Parse.Query(Item);
|
||||
q1.equalTo('field', 'value');
|
||||
q1.lessThan('age', 30);
|
||||
q1.exists('name');
|
||||
|
||||
q2 = new Parse.Query(Item);
|
||||
q2.equalTo('name', 'person');
|
||||
q2.equalTo('field', 'other');
|
||||
|
||||
firstHash = queryHash(Parse.Query.or(q1, q2));
|
||||
secondHash = queryHash(Parse.Query.or(q2, q1));
|
||||
expect(firstHash).toBe(secondHash);
|
||||
});
|
||||
|
||||
it('should not let fields of different types appear similar', function() {
|
||||
var q1 = new Parse.Query(Item);
|
||||
q1.lessThan('age', 30);
|
||||
|
||||
var q2 = new Parse.Query(Item);
|
||||
q2.equalTo('age', '{$lt:30}');
|
||||
|
||||
expect(queryHash(q1)).not.toBe(queryHash(q2));
|
||||
|
||||
q1 = new Parse.Query(Item);
|
||||
q1.equalTo('age', 15);
|
||||
|
||||
q2.equalTo('age', '15');
|
||||
|
||||
expect(queryHash(q1)).not.toBe(queryHash(q2));
|
||||
});
|
||||
});
|
||||
|
||||
describe('matchesQuery', function() {
|
||||
it('matches blanket queries', function() {
|
||||
var obj = {
|
||||
id: new Id('Klass', 'O1'),
|
||||
value: 12
|
||||
};
|
||||
var q = new Parse.Query('Klass');
|
||||
expect(matchesQuery(obj, q)).toBe(true);
|
||||
|
||||
obj.id = new Id('Other', 'O1');
|
||||
expect(matchesQuery(obj, q)).toBe(false);
|
||||
});
|
||||
|
||||
it('matches existence queries', function() {
|
||||
var obj = {
|
||||
id: new Id('Item', 'O1'),
|
||||
count: 15
|
||||
};
|
||||
var q = new Parse.Query('Item');
|
||||
q.exists('count');
|
||||
expect(matchesQuery(obj, q)).toBe(true);
|
||||
q.exists('name');
|
||||
expect(matchesQuery(obj, q)).toBe(false);
|
||||
});
|
||||
|
||||
it('matches on equality queries', function() {
|
||||
var day = new Date();
|
||||
var location = new Parse.GeoPoint({
|
||||
latitude: 37.484815,
|
||||
longitude: -122.148377
|
||||
});
|
||||
var obj = {
|
||||
id: new Id('Person', 'O1'),
|
||||
score: 12,
|
||||
name: 'Bill',
|
||||
birthday: day,
|
||||
lastLocation: location
|
||||
};
|
||||
|
||||
var q = new Parse.Query('Person');
|
||||
q.equalTo('score', 12);
|
||||
expect(matchesQuery(obj, q)).toBe(true);
|
||||
|
||||
q = new Parse.Query('Person');
|
||||
q.equalTo('name', 'Bill');
|
||||
expect(matchesQuery(obj, q)).toBe(true);
|
||||
q.equalTo('name', 'Jeff');
|
||||
expect(matchesQuery(obj, q)).toBe(false);
|
||||
|
||||
q = new Parse.Query('Person');
|
||||
q.containedIn('name', ['Adam', 'Ben', 'Charles']);
|
||||
expect(matchesQuery(obj, q)).toBe(false);
|
||||
q.containedIn('name', ['Adam', 'Bill', 'Charles']);
|
||||
expect(matchesQuery(obj, q)).toBe(true);
|
||||
|
||||
q = new Parse.Query('Person');
|
||||
q.notContainedIn('name', ['Adam', 'Bill', 'Charles']);
|
||||
expect(matchesQuery(obj, q)).toBe(false);
|
||||
q.notContainedIn('name', ['Adam', 'Ben', 'Charles']);
|
||||
expect(matchesQuery(obj, q)).toBe(true);
|
||||
|
||||
q = new Parse.Query('Person');
|
||||
q.equalTo('birthday', day);
|
||||
expect(matchesQuery(obj, q)).toBe(true);
|
||||
q.equalTo('birthday', new Date());
|
||||
expect(matchesQuery(obj, q)).toBe(false);
|
||||
|
||||
q = new Parse.Query('Person');
|
||||
q.equalTo('lastLocation', new Parse.GeoPoint({
|
||||
latitude: 37.484815,
|
||||
longitude: -122.148377
|
||||
}));
|
||||
expect(matchesQuery(obj, q)).toBe(true);
|
||||
q.equalTo('lastLocation', new Parse.GeoPoint({
|
||||
latitude: 37.4848,
|
||||
longitude: -122.1483
|
||||
}));
|
||||
expect(matchesQuery(obj, q)).toBe(false);
|
||||
|
||||
q.equalTo('lastLocation', new Parse.GeoPoint({
|
||||
latitude: 37.484815,
|
||||
longitude: -122.148377
|
||||
}));
|
||||
q.equalTo('score', 12);
|
||||
q.equalTo('name', 'Bill');
|
||||
q.equalTo('birthday', day);
|
||||
expect(matchesQuery(obj, q)).toBe(true);
|
||||
|
||||
q.equalTo('name', 'bill');
|
||||
expect(matchesQuery(obj, q)).toBe(false);
|
||||
|
||||
var img = {
|
||||
id: new Id('Image', 'I1'),
|
||||
tags: ['nofilter', 'latergram', 'tbt']
|
||||
};
|
||||
|
||||
q = new Parse.Query('Image');
|
||||
q.equalTo('tags', 'selfie');
|
||||
expect(matchesQuery(img, q)).toBe(false);
|
||||
q.equalTo('tags', 'tbt');
|
||||
expect(matchesQuery(img, q)).toBe(true);
|
||||
|
||||
var q2 = new Parse.Query('Image');
|
||||
q2.containsAll('tags', ['latergram', 'nofilter']);
|
||||
expect(matchesQuery(img, q2)).toBe(true);
|
||||
q2.containsAll('tags', ['latergram', 'selfie']);
|
||||
expect(matchesQuery(img, q2)).toBe(false);
|
||||
|
||||
var u = new Parse.User();
|
||||
u.id = 'U2';
|
||||
q = new Parse.Query('Image');
|
||||
q.equalTo('owner', u);
|
||||
|
||||
img = {
|
||||
className: 'Image',
|
||||
objectId: 'I1',
|
||||
owner: {
|
||||
className: '_User',
|
||||
objectId: 'U2'
|
||||
}
|
||||
};
|
||||
expect(matchesQuery(img, q)).toBe(true);
|
||||
|
||||
img.owner.objectId = 'U3';
|
||||
expect(matchesQuery(img, q)).toBe(false);
|
||||
});
|
||||
|
||||
it('matches on inequalities', function() {
|
||||
var player = {
|
||||
id: new Id('Person', 'O1'),
|
||||
score: 12,
|
||||
name: 'Bill',
|
||||
birthday: new Date(1980, 2, 4),
|
||||
};
|
||||
var q = new Parse.Query('Person');
|
||||
q.lessThan('score', 15);
|
||||
expect(matchesQuery(player, q)).toBe(true);
|
||||
q.lessThan('score', 10);
|
||||
expect(matchesQuery(player, q)).toBe(false);
|
||||
|
||||
q = new Parse.Query('Person');
|
||||
q.lessThanOrEqualTo('score', 15);
|
||||
expect(matchesQuery(player, q)).toBe(true);
|
||||
q.lessThanOrEqualTo('score', 12);
|
||||
expect(matchesQuery(player, q)).toBe(true);
|
||||
q.lessThanOrEqualTo('score', 10);
|
||||
expect(matchesQuery(player, q)).toBe(false);
|
||||
|
||||
q = new Parse.Query('Person');
|
||||
q.greaterThan('score', 15);
|
||||
expect(matchesQuery(player, q)).toBe(false);
|
||||
q.greaterThan('score', 10);
|
||||
expect(matchesQuery(player, q)).toBe(true);
|
||||
|
||||
q = new Parse.Query('Person');
|
||||
q.greaterThanOrEqualTo('score', 15);
|
||||
expect(matchesQuery(player, q)).toBe(false);
|
||||
q.greaterThanOrEqualTo('score', 12);
|
||||
expect(matchesQuery(player, q)).toBe(true);
|
||||
q.greaterThanOrEqualTo('score', 10);
|
||||
expect(matchesQuery(player, q)).toBe(true);
|
||||
|
||||
q = new Parse.Query('Person');
|
||||
q.notEqualTo('score', 12);
|
||||
expect(matchesQuery(player, q)).toBe(false);
|
||||
q.notEqualTo('score', 40);
|
||||
expect(matchesQuery(player, q)).toBe(true);
|
||||
});
|
||||
|
||||
it('matches an $or query', function() {
|
||||
var player = {
|
||||
id: new Id('Player', 'P1'),
|
||||
name: 'Player 1',
|
||||
score: 12
|
||||
};
|
||||
var q = new Parse.Query('Player');
|
||||
q.equalTo('name', 'Player 1');
|
||||
var q2 = new Parse.Query('Player');
|
||||
q2.equalTo('name', 'Player 2');
|
||||
var orQuery = Parse.Query.or(q, q2);
|
||||
expect(matchesQuery(player, q)).toBe(true);
|
||||
expect(matchesQuery(player, q2)).toBe(false);
|
||||
expect(matchesQuery(player, orQuery)).toBe(true);
|
||||
});
|
||||
|
||||
it('matches $regex queries', function() {
|
||||
var player = {
|
||||
id: new Id('Player', 'P1'),
|
||||
name: 'Player 1',
|
||||
score: 12
|
||||
};
|
||||
|
||||
var q = new Parse.Query('Player');
|
||||
q.startsWith('name', 'Play');
|
||||
expect(matchesQuery(player, q)).toBe(true);
|
||||
q.startsWith('name', 'Ploy');
|
||||
expect(matchesQuery(player, q)).toBe(false);
|
||||
|
||||
q = new Parse.Query('Player');
|
||||
q.endsWith('name', ' 1');
|
||||
expect(matchesQuery(player, q)).toBe(true);
|
||||
q.endsWith('name', ' 2');
|
||||
expect(matchesQuery(player, q)).toBe(false);
|
||||
|
||||
// Check that special characters are escaped
|
||||
player.name = 'Android-7';
|
||||
q = new Parse.Query('Player');
|
||||
q.contains('name', 'd-7');
|
||||
expect(matchesQuery(player, q)).toBe(true);
|
||||
|
||||
q = new Parse.Query('Player');
|
||||
q.matches('name', /A.d/);
|
||||
expect(matchesQuery(player, q)).toBe(true);
|
||||
|
||||
q.matches('name', /A[^n]d/);
|
||||
expect(matchesQuery(player, q)).toBe(false);
|
||||
|
||||
// Check that the string \\E is returned to normal
|
||||
player.name = 'Slash \\E';
|
||||
q = new Parse.Query('Player');
|
||||
q.endsWith('name', 'h \\E');
|
||||
expect(matchesQuery(player, q)).toBe(true);
|
||||
|
||||
q.endsWith('name', 'h \\Ee');
|
||||
expect(matchesQuery(player, q)).toBe(false);
|
||||
|
||||
player.name = 'Slash \\Q and more';
|
||||
q = new Parse.Query('Player');
|
||||
q.contains('name', 'h \\Q and');
|
||||
expect(matchesQuery(player, q)).toBe(true);
|
||||
q.contains('name', 'h \\Q or');
|
||||
expect(matchesQuery(player, q)).toBe(false);
|
||||
});
|
||||
|
||||
it('matches $nearSphere queries', function() {
|
||||
var q = new Parse.Query('Checkin');
|
||||
q.near('location', new Parse.GeoPoint(20, 20));
|
||||
// With no max distance, any GeoPoint is 'near'
|
||||
var pt = {
|
||||
id: new Id('Checkin', 'C1'),
|
||||
location: new Parse.GeoPoint(40, 40)
|
||||
};
|
||||
expect(matchesQuery(pt, q)).toBe(true);
|
||||
|
||||
q = new Parse.Query('Checkin');
|
||||
pt.location = new Parse.GeoPoint(40, 40);
|
||||
q.withinRadians('location', new Parse.GeoPoint(30, 30), 0.3);
|
||||
expect(matchesQuery(pt, q)).toBe(true);
|
||||
|
||||
q.withinRadians('location', new Parse.GeoPoint(30, 30), 0.2);
|
||||
expect(matchesQuery(pt, q)).toBe(false);
|
||||
});
|
||||
|
||||
it('matches $within queries', function() {
|
||||
var caltrainStation = {
|
||||
id: new Id('Checkin', 'C1'),
|
||||
location: new Parse.GeoPoint(37.776346, -122.394218),
|
||||
name: 'Caltrain'
|
||||
};
|
||||
|
||||
var santaClara = {
|
||||
id: new Id('Checkin', 'C2'),
|
||||
location: new Parse.GeoPoint(37.325635, -121.945753),
|
||||
name: 'Santa Clara'
|
||||
};
|
||||
|
||||
var q = new Parse.Query('Checkin').withinGeoBox(
|
||||
'location',
|
||||
new Parse.GeoPoint(37.708813, -122.526398),
|
||||
new Parse.GeoPoint(37.822802, -122.373962)
|
||||
);
|
||||
|
||||
expect(matchesQuery(caltrainStation, q)).toBe(true);
|
||||
expect(matchesQuery(santaClara, q)).toBe(false);
|
||||
|
||||
// Invalid rectangles
|
||||
q = new Parse.Query('Checkin').withinGeoBox(
|
||||
'location',
|
||||
new Parse.GeoPoint(37.822802, -122.373962),
|
||||
new Parse.GeoPoint(37.708813, -122.526398)
|
||||
);
|
||||
|
||||
expect(matchesQuery(caltrainStation, q)).toBe(false);
|
||||
expect(matchesQuery(santaClara, q)).toBe(false);
|
||||
|
||||
q = new Parse.Query('Checkin').withinGeoBox(
|
||||
'location',
|
||||
new Parse.GeoPoint(37.708813, -122.373962),
|
||||
new Parse.GeoPoint(37.822802, -122.526398)
|
||||
);
|
||||
|
||||
expect(matchesQuery(caltrainStation, q)).toBe(false);
|
||||
expect(matchesQuery(santaClara, q)).toBe(false);
|
||||
});
|
||||
});
|
||||
29
spec/RedisPubSub.spec.js
Normal file
29
spec/RedisPubSub.spec.js
Normal file
@@ -0,0 +1,29 @@
|
||||
var RedisPubSub = require('../src/LiveQuery/RedisPubSub').RedisPubSub;
|
||||
|
||||
describe('RedisPubSub', function() {
|
||||
|
||||
beforeEach(function(done) {
|
||||
// Mock redis
|
||||
var createClient = jasmine.createSpy('createClient');
|
||||
jasmine.mockLibrary('redis', 'createClient', createClient);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can create publisher', function() {
|
||||
var publisher = RedisPubSub.createPublisher('redisAddress');
|
||||
|
||||
var redis = require('redis');
|
||||
expect(redis.createClient).toHaveBeenCalledWith('redisAddress', { no_ready_check: true });
|
||||
});
|
||||
|
||||
it('can create subscriber', function() {
|
||||
var subscriber = RedisPubSub.createSubscriber('redisAddress');
|
||||
|
||||
var redis = require('redis');
|
||||
expect(redis.createClient).toHaveBeenCalledWith('redisAddress', { no_ready_check: true });
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
jasmine.restoreLibrary('redis', 'createClient');
|
||||
});
|
||||
});
|
||||
52
spec/SessionTokenCache.spec.js
Normal file
52
spec/SessionTokenCache.spec.js
Normal file
@@ -0,0 +1,52 @@
|
||||
var SessionTokenCache = require('../src/LiveQuery/SessionTokenCache').SessionTokenCache;
|
||||
|
||||
describe('SessionTokenCache', function() {
|
||||
|
||||
beforeEach(function(done) {
|
||||
var Parse = require('parse/node');
|
||||
// Mock parse
|
||||
var mockUser = {
|
||||
become: jasmine.createSpy('become').and.returnValue(Parse.Promise.as({
|
||||
id: 'userId'
|
||||
}))
|
||||
}
|
||||
jasmine.mockLibrary('parse/node', 'User', mockUser);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can get undefined userId', function(done) {
|
||||
var sessionTokenCache = new SessionTokenCache();
|
||||
|
||||
sessionTokenCache.getUserId(undefined).then((userIdFromCache) => {
|
||||
}, (error) => {
|
||||
expect(error).not.toBeNull();
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can get existing userId', function(done) {
|
||||
var sessionTokenCache = new SessionTokenCache();
|
||||
var sessionToken = 'sessionToken';
|
||||
var userId = 'userId'
|
||||
sessionTokenCache.cache.set(sessionToken, userId);
|
||||
|
||||
sessionTokenCache.getUserId(sessionToken).then((userIdFromCache) => {
|
||||
expect(userIdFromCache).toBe(userId);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('can get new userId', function(done) {
|
||||
var sessionTokenCache = new SessionTokenCache();
|
||||
|
||||
sessionTokenCache.getUserId('sessionToken').then((userIdFromCache) => {
|
||||
expect(userIdFromCache).toBe('userId');
|
||||
expect(sessionTokenCache.cache.length).toBe(1);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
jasmine.restoreLibrary('parse/node', 'User');
|
||||
});
|
||||
});
|
||||
123
spec/Subscription.spec.js
Normal file
123
spec/Subscription.spec.js
Normal file
@@ -0,0 +1,123 @@
|
||||
var Subscription = require('../src/LiveQuery/Subscription').Subscription;
|
||||
|
||||
describe('Subscription', function() {
|
||||
|
||||
beforeEach(function() {
|
||||
var mockError = jasmine.createSpy('error');
|
||||
jasmine.mockLibrary('../src/LiveQuery/PLog', 'error', mockError);
|
||||
});
|
||||
|
||||
it('can be initialized', function() {
|
||||
var subscription = new Subscription('className', { key : 'value' }, 'hash');
|
||||
|
||||
expect(subscription.className).toBe('className');
|
||||
expect(subscription.query).toEqual({ key : 'value' });
|
||||
expect(subscription.hash).toBe('hash');
|
||||
expect(subscription.clientRequestIds.size).toBe(0);
|
||||
});
|
||||
|
||||
it('can check it has subscribing clients', function() {
|
||||
var subscription = new Subscription('className', { key : 'value' }, 'hash');
|
||||
|
||||
expect(subscription.hasSubscribingClient()).toBe(false);
|
||||
});
|
||||
|
||||
it('can check it does not have subscribing clients', function() {
|
||||
var subscription = new Subscription('className', { key : 'value' }, 'hash');
|
||||
subscription.addClientSubscription(1, 1);
|
||||
|
||||
expect(subscription.hasSubscribingClient()).toBe(true);
|
||||
});
|
||||
|
||||
it('can add one request for one client', function() {
|
||||
var subscription = new Subscription('className', { key : 'value' }, 'hash');
|
||||
subscription.addClientSubscription(1, 1);
|
||||
|
||||
expect(subscription.clientRequestIds.size).toBe(1);
|
||||
expect(subscription.clientRequestIds.get(1)).toEqual([1]);
|
||||
});
|
||||
|
||||
it('can add requests for one client', function() {
|
||||
var subscription = new Subscription('className', { key : 'value' }, 'hash');
|
||||
subscription.addClientSubscription(1, 1);
|
||||
subscription.addClientSubscription(1, 2);
|
||||
|
||||
expect(subscription.clientRequestIds.size).toBe(1);
|
||||
expect(subscription.clientRequestIds.get(1)).toEqual([1, 2]);
|
||||
});
|
||||
|
||||
it('can add requests for clients', function() {
|
||||
var subscription = new Subscription('className', { key : 'value' }, 'hash');
|
||||
subscription.addClientSubscription(1, 1);
|
||||
subscription.addClientSubscription(1, 2);
|
||||
subscription.addClientSubscription(2, 2);
|
||||
subscription.addClientSubscription(2, 3);
|
||||
|
||||
expect(subscription.clientRequestIds.size).toBe(2);
|
||||
expect(subscription.clientRequestIds.get(1)).toEqual([1, 2]);
|
||||
expect(subscription.clientRequestIds.get(2)).toEqual([2, 3]);
|
||||
});
|
||||
|
||||
it('can delete requests for nonexistent client', function() {
|
||||
var subscription = new Subscription('className', { key : 'value' }, 'hash');
|
||||
subscription.deleteClientSubscription(1, 1);
|
||||
|
||||
var PLog =require('../src/LiveQuery/PLog');
|
||||
expect(PLog.error).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('can delete nonexistent request for one client', function() {
|
||||
var subscription = new Subscription('className', { key : 'value' }, 'hash');
|
||||
subscription.addClientSubscription(1, 1);
|
||||
subscription.deleteClientSubscription(1, 2);
|
||||
|
||||
var PLog =require('../src/LiveQuery/PLog');
|
||||
expect(PLog.error).toHaveBeenCalled();
|
||||
expect(subscription.clientRequestIds.size).toBe(1);
|
||||
expect(subscription.clientRequestIds.get(1)).toEqual([1]);
|
||||
});
|
||||
|
||||
it('can delete some requests for one client', function() {
|
||||
var subscription = new Subscription('className', { key : 'value' }, 'hash');
|
||||
subscription.addClientSubscription(1, 1);
|
||||
subscription.addClientSubscription(1, 2);
|
||||
subscription.deleteClientSubscription(1, 2);
|
||||
|
||||
var PLog =require('../src/LiveQuery/PLog');
|
||||
expect(PLog.error).not.toHaveBeenCalled();
|
||||
expect(subscription.clientRequestIds.size).toBe(1);
|
||||
expect(subscription.clientRequestIds.get(1)).toEqual([1]);
|
||||
});
|
||||
|
||||
it('can delete all requests for one client', function() {
|
||||
var subscription = new Subscription('className', { key : 'value' }, 'hash');
|
||||
subscription.addClientSubscription(1, 1);
|
||||
subscription.addClientSubscription(1, 2);
|
||||
subscription.deleteClientSubscription(1, 1);
|
||||
subscription.deleteClientSubscription(1, 2);
|
||||
|
||||
var PLog =require('../src/LiveQuery/PLog');
|
||||
expect(PLog.error).not.toHaveBeenCalled();
|
||||
expect(subscription.clientRequestIds.size).toBe(0);
|
||||
});
|
||||
|
||||
it('can delete requests for multiple clients', function() {
|
||||
var subscription = new Subscription('className', { key : 'value' }, 'hash');
|
||||
subscription.addClientSubscription(1, 1);
|
||||
subscription.addClientSubscription(1, 2);
|
||||
subscription.addClientSubscription(2, 1);
|
||||
subscription.addClientSubscription(2, 2);
|
||||
subscription.deleteClientSubscription(1, 2);
|
||||
subscription.deleteClientSubscription(2, 1);
|
||||
subscription.deleteClientSubscription(2, 2);
|
||||
|
||||
var PLog =require('../src/LiveQuery/PLog');
|
||||
expect(PLog.error).not.toHaveBeenCalled();
|
||||
expect(subscription.clientRequestIds.size).toBe(1);
|
||||
expect(subscription.clientRequestIds.get(1)).toEqual([1]);
|
||||
});
|
||||
|
||||
afterEach(function(){
|
||||
jasmine.restoreLibrary('../src/LiveQuery/PLog', 'error');
|
||||
});
|
||||
});
|
||||
@@ -252,3 +252,22 @@ global.jequal = jequal;
|
||||
global.range = range;
|
||||
global.setServerConfiguration = setServerConfiguration;
|
||||
global.defaultConfiguration = defaultConfiguration;
|
||||
|
||||
// LiveQuery test setting
|
||||
require('../src/LiveQuery/PLog').logLevel = 'NONE';
|
||||
var libraryCache = {};
|
||||
jasmine.mockLibrary = function(library, name, mock) {
|
||||
var original = require(library)[name];
|
||||
if (!libraryCache[library]) {
|
||||
libraryCache[library] = {};
|
||||
}
|
||||
require(library)[name] = mock;
|
||||
libraryCache[library][name] = original;
|
||||
}
|
||||
|
||||
jasmine.restoreLibrary = function(library, name) {
|
||||
if (!libraryCache[library] || !libraryCache[library][name]) {
|
||||
throw 'Can not find library ' + library + ' ' + name;
|
||||
}
|
||||
require(library)[name] = libraryCache[library][name];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user