Merge remote-tracking branch 'upstream/master'
This commit is contained in:
307
spec/OAuth.spec.js
Normal file
307
spec/OAuth.spec.js
Normal file
@@ -0,0 +1,307 @@
|
||||
var OAuth = require("../src/oauth/OAuth1Client");
|
||||
var request = require('request');
|
||||
|
||||
describe('OAuth', function() {
|
||||
|
||||
it("Nonce should have right length", (done) => {
|
||||
jequal(OAuth.nonce().length, 30);
|
||||
done();
|
||||
});
|
||||
|
||||
it("Should properly build parameter string", (done) => {
|
||||
var string = OAuth.buildParameterString({c:1, a:2, b:3})
|
||||
jequal(string, "a=2&b=3&c=1");
|
||||
done();
|
||||
});
|
||||
|
||||
it("Should properly build empty parameter string", (done) => {
|
||||
var string = OAuth.buildParameterString()
|
||||
jequal(string, "");
|
||||
done();
|
||||
});
|
||||
|
||||
it("Should properly build signature string", (done) => {
|
||||
var string = OAuth.buildSignatureString("get", "http://dummy.com", "");
|
||||
jequal(string, "GET&http%3A%2F%2Fdummy.com&");
|
||||
done();
|
||||
});
|
||||
|
||||
it("Should properly generate request signature", (done) => {
|
||||
var request = {
|
||||
host: "dummy.com",
|
||||
path: "path"
|
||||
};
|
||||
|
||||
var oauth_params = {
|
||||
oauth_timestamp: 123450000,
|
||||
oauth_nonce: "AAAAAAAAAAAAAAAAA",
|
||||
oauth_consumer_key: "hello",
|
||||
oauth_token: "token"
|
||||
};
|
||||
|
||||
var consumer_secret = "world";
|
||||
var auth_token_secret = "secret";
|
||||
request = OAuth.signRequest(request, oauth_params, consumer_secret, auth_token_secret);
|
||||
jequal(request.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="8K95bpQcDi9Nd2GkhumTVcw4%2BXw%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"');
|
||||
done();
|
||||
});
|
||||
|
||||
it("Should properly build request", (done) => {
|
||||
var options = {
|
||||
host: "dummy.com",
|
||||
consumer_key: "hello",
|
||||
consumer_secret: "world",
|
||||
auth_token: "token",
|
||||
auth_token_secret: "secret",
|
||||
// Custom oauth params for tests
|
||||
oauth_params: {
|
||||
oauth_timestamp: 123450000,
|
||||
oauth_nonce: "AAAAAAAAAAAAAAAAA"
|
||||
}
|
||||
};
|
||||
var path = "path";
|
||||
var method = "get";
|
||||
|
||||
var oauthClient = new OAuth(options);
|
||||
var req = oauthClient.buildRequest(method, path, {"query": "param"});
|
||||
|
||||
jequal(req.host, options.host);
|
||||
jequal(req.path, "/"+path+"?query=param");
|
||||
jequal(req.method, "GET");
|
||||
jequal(req.headers['Content-Type'], 'application/x-www-form-urlencoded');
|
||||
jequal(req.headers['Authorization'], 'OAuth oauth_consumer_key="hello", oauth_nonce="AAAAAAAAAAAAAAAAA", oauth_signature="wNkyEkDE%2F0JZ2idmqyrgHdvC0rs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="123450000", oauth_token="token", oauth_version="1.0"')
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
function validateCannotAuthenticateError(data, done) {
|
||||
jequal(typeof data, "object");
|
||||
jequal(typeof data.errors, "object");
|
||||
var errors = data.errors;
|
||||
jequal(typeof errors[0], "object");
|
||||
// Cannot authenticate error
|
||||
jequal(errors[0].code, 32);
|
||||
done();
|
||||
}
|
||||
|
||||
it("Should fail a GET request", (done) => {
|
||||
var options = {
|
||||
host: "api.twitter.com",
|
||||
consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
};
|
||||
var path = "/1.1/help/configuration.json";
|
||||
var params = {"lang": "en"};
|
||||
var oauthClient = new OAuth(options);
|
||||
oauthClient.get(path, params).then(function(data){
|
||||
validateCannotAuthenticateError(data, done);
|
||||
})
|
||||
});
|
||||
|
||||
it("Should fail a POST request", (done) => {
|
||||
var options = {
|
||||
host: "api.twitter.com",
|
||||
consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
};
|
||||
var body = {
|
||||
lang: "en"
|
||||
};
|
||||
var path = "/1.1/account/settings.json";
|
||||
|
||||
var oauthClient = new OAuth(options);
|
||||
oauthClient.post(path, null, body).then(function(data){
|
||||
validateCannotAuthenticateError(data, done);
|
||||
})
|
||||
});
|
||||
|
||||
it("Should fail a request", (done) => {
|
||||
var options = {
|
||||
host: "localhost",
|
||||
consumer_key: "XXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
consumer_secret: "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
|
||||
};
|
||||
var body = {
|
||||
lang: "en"
|
||||
};
|
||||
var path = "/";
|
||||
|
||||
var oauthClient = new OAuth(options);
|
||||
oauthClient.post(path, null, body).then(function(data){
|
||||
jequal(false, true);
|
||||
done();
|
||||
}).catch(function(){
|
||||
jequal(true, true);
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
["facebook", "github", "instagram", "google", "linkedin", "meetup", "twitter"].map(function(providerName){
|
||||
it("Should validate structure of "+providerName, (done) => {
|
||||
var provider = require("../src/oauth/"+providerName);
|
||||
jequal(typeof provider.validateAuthData, "function");
|
||||
jequal(typeof provider.validateAppId, "function");
|
||||
jequal(provider.validateAuthData({}, {}).constructor, Promise.prototype.constructor);
|
||||
jequal(provider.validateAppId("app", "key", {}).constructor, Promise.prototype.constructor);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
var getMockMyOauthProvider = function() {
|
||||
return {
|
||||
authData: {
|
||||
id: "12345",
|
||||
access_token: "12345",
|
||||
expiration_date: new Date().toJSON(),
|
||||
},
|
||||
shouldError: false,
|
||||
loggedOut: false,
|
||||
synchronizedUserId: null,
|
||||
synchronizedAuthToken: null,
|
||||
synchronizedExpiration: null,
|
||||
|
||||
authenticate: function(options) {
|
||||
if (this.shouldError) {
|
||||
options.error(this, "An error occurred");
|
||||
} else if (this.shouldCancel) {
|
||||
options.error(this, null);
|
||||
} else {
|
||||
options.success(this, this.authData);
|
||||
}
|
||||
},
|
||||
restoreAuthentication: function(authData) {
|
||||
if (!authData) {
|
||||
this.synchronizedUserId = null;
|
||||
this.synchronizedAuthToken = null;
|
||||
this.synchronizedExpiration = null;
|
||||
return true;
|
||||
}
|
||||
this.synchronizedUserId = authData.id;
|
||||
this.synchronizedAuthToken = authData.access_token;
|
||||
this.synchronizedExpiration = authData.expiration_date;
|
||||
return true;
|
||||
},
|
||||
getAuthType: function() {
|
||||
return "myoauth";
|
||||
},
|
||||
deauthenticate: function() {
|
||||
this.loggedOut = true;
|
||||
this.restoreAuthentication(null);
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
var ExtendedUser = Parse.User.extend({
|
||||
extended: function() {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
var createOAuthUser = function(callback) {
|
||||
var jsonBody = {
|
||||
authData: {
|
||||
myoauth: getMockMyOauthProvider().authData
|
||||
}
|
||||
};
|
||||
var headers = {'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
'Content-Type': 'application/json' }
|
||||
|
||||
var options = {
|
||||
headers: {'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
'Content-Type': 'application/json' },
|
||||
url: 'http://localhost:8378/1/users',
|
||||
body: JSON.stringify(jsonBody)
|
||||
};
|
||||
|
||||
return request.post(options, callback);
|
||||
}
|
||||
|
||||
it("should create user with REST API", (done) => {
|
||||
|
||||
createOAuthUser((error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
var b = JSON.parse(body);
|
||||
expect(b.objectId).not.toBeNull();
|
||||
expect(b.objectId).not.toBeUndefined();
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it("should only create a single user with REST API", (done) => {
|
||||
var objectId;
|
||||
createOAuthUser((error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
var b = JSON.parse(body);
|
||||
expect(b.objectId).not.toBeNull();
|
||||
expect(b.objectId).not.toBeUndefined();
|
||||
objectId = b.objectId;
|
||||
|
||||
createOAuthUser((error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
var b = JSON.parse(body);
|
||||
expect(b.objectId).not.toBeNull();
|
||||
expect(b.objectId).not.toBeUndefined();
|
||||
expect(b.objectId).toBe(objectId);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
it("unlink and link with custom provider", (done) => {
|
||||
var provider = getMockMyOauthProvider();
|
||||
Parse.User._registerAuthenticationProvider(provider);
|
||||
Parse.User._logInWith("myoauth", {
|
||||
success: function(model) {
|
||||
ok(model instanceof Parse.User, "Model should be a Parse.User");
|
||||
strictEqual(Parse.User.current(), model);
|
||||
ok(model.extended(), "Should have used the subclass.");
|
||||
strictEqual(provider.authData.id, provider.synchronizedUserId);
|
||||
strictEqual(provider.authData.access_token, provider.synchronizedAuthToken);
|
||||
strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration);
|
||||
ok(model._isLinked("myoauth"), "User should be linked to myoauth");
|
||||
|
||||
model._unlinkFrom("myoauth", {
|
||||
success: function(model) {
|
||||
ok(!model._isLinked("myoauth"),
|
||||
"User should not be linked to myoauth");
|
||||
ok(!provider.synchronizedUserId, "User id should be cleared");
|
||||
ok(!provider.synchronizedAuthToken, "Auth token should be cleared");
|
||||
ok(!provider.synchronizedExpiration,
|
||||
"Expiration should be cleared");
|
||||
|
||||
model._linkWith("myoauth", {
|
||||
success: function(model) {
|
||||
ok(provider.synchronizedUserId, "User id should have a value");
|
||||
ok(provider.synchronizedAuthToken,
|
||||
"Auth token should have a value");
|
||||
ok(provider.synchronizedExpiration,
|
||||
"Expiration should have a value");
|
||||
ok(model._isLinked("myoauth"),
|
||||
"User should be linked to myoauth");
|
||||
done();
|
||||
},
|
||||
error: function(model, error) {
|
||||
ok(false, "linking again should succeed");
|
||||
done();
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function(model, error) {
|
||||
ok(false, "unlinking should succeed");
|
||||
done();
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function(model, error) {
|
||||
ok(false, "linking should have worked");
|
||||
done();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
})
|
||||
234
spec/OneSignalPushAdapter.spec.js
Normal file
234
spec/OneSignalPushAdapter.spec.js
Normal file
@@ -0,0 +1,234 @@
|
||||
|
||||
var OneSignalPushAdapter = require('../src/Adapters/Push/OneSignalPushAdapter');
|
||||
|
||||
describe('OneSignalPushAdapter', () => {
|
||||
it('can be initialized', (done) => {
|
||||
// Make mock config
|
||||
var pushConfig = {
|
||||
oneSignalAppId:"APP ID",
|
||||
oneSignalApiKey:"API KEY"
|
||||
};
|
||||
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter(pushConfig);
|
||||
|
||||
var senderMap = oneSignalPushAdapter.senderMap;
|
||||
|
||||
expect(senderMap.ios instanceof Function).toBe(true);
|
||||
expect(senderMap.android instanceof Function).toBe(true);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can get valid push types', (done) => {
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter();
|
||||
|
||||
expect(oneSignalPushAdapter.getValidPushTypes()).toEqual(['ios', 'android']);
|
||||
done();
|
||||
});
|
||||
|
||||
it('can classify installation', (done) => {
|
||||
// Mock installations
|
||||
var validPushTypes = ['ios', 'android'];
|
||||
var installations = [
|
||||
{
|
||||
deviceType: 'android',
|
||||
deviceToken: 'androidToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'ios',
|
||||
deviceToken: 'iosToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'win',
|
||||
deviceToken: 'winToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'android',
|
||||
deviceToken: undefined
|
||||
}
|
||||
];
|
||||
|
||||
var deviceMap = OneSignalPushAdapter.classifyInstallation(installations, validPushTypes);
|
||||
expect(deviceMap['android']).toEqual([makeDevice('androidToken')]);
|
||||
expect(deviceMap['ios']).toEqual([makeDevice('iosToken')]);
|
||||
expect(deviceMap['win']).toBe(undefined);
|
||||
done();
|
||||
});
|
||||
|
||||
|
||||
it('can send push notifications', (done) => {
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter();
|
||||
|
||||
// Mock android ios senders
|
||||
var androidSender = jasmine.createSpy('send')
|
||||
var iosSender = jasmine.createSpy('send')
|
||||
|
||||
var senderMap = {
|
||||
ios: iosSender,
|
||||
android: androidSender
|
||||
};
|
||||
oneSignalPushAdapter.senderMap = senderMap;
|
||||
|
||||
// Mock installations
|
||||
var installations = [
|
||||
{
|
||||
deviceType: 'android',
|
||||
deviceToken: 'androidToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'ios',
|
||||
deviceToken: 'iosToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'win',
|
||||
deviceToken: 'winToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'android',
|
||||
deviceToken: undefined
|
||||
}
|
||||
];
|
||||
var data = {};
|
||||
|
||||
oneSignalPushAdapter.send(data, installations);
|
||||
// Check android sender
|
||||
expect(androidSender).toHaveBeenCalled();
|
||||
var args = androidSender.calls.first().args;
|
||||
expect(args[0]).toEqual(data);
|
||||
expect(args[1]).toEqual([
|
||||
makeDevice('androidToken')
|
||||
]);
|
||||
// Check ios sender
|
||||
expect(iosSender).toHaveBeenCalled();
|
||||
args = iosSender.calls.first().args;
|
||||
expect(args[0]).toEqual(data);
|
||||
expect(args[1]).toEqual([
|
||||
makeDevice('iosToken')
|
||||
]);
|
||||
done();
|
||||
});
|
||||
|
||||
it("can send iOS notifications", (done) => {
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter();
|
||||
var sendToOneSignal = jasmine.createSpy('sendToOneSignal');
|
||||
oneSignalPushAdapter.sendToOneSignal = sendToOneSignal;
|
||||
|
||||
oneSignalPushAdapter.sendToAPNS({'data':{
|
||||
'badge': 1,
|
||||
'alert': "Example content",
|
||||
'sound': "Example sound",
|
||||
'content-available': 1,
|
||||
'misc-data': 'Example Data'
|
||||
}},[{'deviceToken':'iosToken1'},{'deviceToken':'iosToken2'}])
|
||||
|
||||
expect(sendToOneSignal).toHaveBeenCalled();
|
||||
var args = sendToOneSignal.calls.first().args;
|
||||
expect(args[0]).toEqual({
|
||||
'ios_badgeType':'SetTo',
|
||||
'ios_badgeCount':1,
|
||||
'contents': { 'en':'Example content'},
|
||||
'ios_sound': 'Example sound',
|
||||
'content_available':true,
|
||||
'data':{'misc-data':'Example Data'},
|
||||
'include_ios_tokens':['iosToken1','iosToken2']
|
||||
})
|
||||
done();
|
||||
});
|
||||
|
||||
it("can send Android notifications", (done) => {
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter();
|
||||
var sendToOneSignal = jasmine.createSpy('sendToOneSignal');
|
||||
oneSignalPushAdapter.sendToOneSignal = sendToOneSignal;
|
||||
|
||||
oneSignalPushAdapter.sendToGCM({'data':{
|
||||
'title': 'Example title',
|
||||
'alert': 'Example content',
|
||||
'misc-data': 'Example Data'
|
||||
}},[{'deviceToken':'androidToken1'},{'deviceToken':'androidToken2'}])
|
||||
|
||||
expect(sendToOneSignal).toHaveBeenCalled();
|
||||
var args = sendToOneSignal.calls.first().args;
|
||||
expect(args[0]).toEqual({
|
||||
'contents': { 'en':'Example content'},
|
||||
'title': {'en':'Example title'},
|
||||
'data':{'misc-data':'Example Data'},
|
||||
'include_android_reg_ids': ['androidToken1','androidToken2']
|
||||
})
|
||||
done();
|
||||
});
|
||||
|
||||
it("can post the correct data", (done) => {
|
||||
var pushConfig = {
|
||||
oneSignalAppId:"APP ID",
|
||||
oneSignalApiKey:"API KEY"
|
||||
};
|
||||
var oneSignalPushAdapter = new OneSignalPushAdapter(pushConfig);
|
||||
|
||||
var write = jasmine.createSpy('write');
|
||||
oneSignalPushAdapter.https = {
|
||||
'request': function(a,b) {
|
||||
return {
|
||||
'end':function(){},
|
||||
'on':function(a,b){},
|
||||
'write':write
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var installations = [
|
||||
{
|
||||
deviceType: 'android',
|
||||
deviceToken: 'androidToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'ios',
|
||||
deviceToken: 'iosToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'win',
|
||||
deviceToken: 'winToken'
|
||||
},
|
||||
{
|
||||
deviceType: 'android',
|
||||
deviceToken: undefined
|
||||
}
|
||||
];
|
||||
|
||||
oneSignalPushAdapter.send({'data':{
|
||||
'title': 'Example title',
|
||||
'alert': 'Example content',
|
||||
'content-available':1,
|
||||
'misc-data': 'Example Data'
|
||||
}}, installations);
|
||||
|
||||
expect(write).toHaveBeenCalled();
|
||||
|
||||
// iOS
|
||||
args = write.calls.first().args;
|
||||
expect(args[0]).toEqual(JSON.stringify({
|
||||
'contents': { 'en':'Example content'},
|
||||
'content_available':true,
|
||||
'data':{'title':'Example title','misc-data':'Example Data'},
|
||||
'include_ios_tokens':['iosToken'],
|
||||
'app_id':'APP ID'
|
||||
}));
|
||||
|
||||
// Android
|
||||
args = write.calls.mostRecent().args;
|
||||
expect(args[0]).toEqual(JSON.stringify({
|
||||
'contents': { 'en':'Example content'},
|
||||
'title': {'en':'Example title'},
|
||||
'data':{"content-available":1,'misc-data':'Example Data'},
|
||||
'include_android_reg_ids':['androidToken'],
|
||||
'app_id':'APP ID'
|
||||
}));
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
function makeDevice(deviceToken, appIdentifier) {
|
||||
return {
|
||||
deviceToken: deviceToken
|
||||
};
|
||||
}
|
||||
|
||||
});
|
||||
@@ -129,6 +129,22 @@ describe('miscellaneous', function() {
|
||||
});
|
||||
});
|
||||
|
||||
it('query without limit get default 100 records', function(done) {
|
||||
var objects = [];
|
||||
for (var i = 0; i < 150; i++) {
|
||||
objects.push(new TestObject({name: 'name' + i}));
|
||||
}
|
||||
Parse.Object.saveAll(objects).then(() => {
|
||||
return new Parse.Query(TestObject).find();
|
||||
}).then((results) => {
|
||||
expect(results.length).toEqual(100);
|
||||
done();
|
||||
}, (error) => {
|
||||
fail(error);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('basic saveAll', function(done) {
|
||||
var alpha = new TestObject({ letter: 'alpha' });
|
||||
var beta = new TestObject({ letter: 'beta' });
|
||||
@@ -571,6 +587,35 @@ describe('miscellaneous', function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('test cloud function query parameters', (done) => {
|
||||
Parse.Cloud.define('echoParams', (req, res) => {
|
||||
res.success(req.params);
|
||||
});
|
||||
var headers = {
|
||||
'Content-Type': 'application/json',
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-Javascript-Key': 'test'
|
||||
};
|
||||
request.post({
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/functions/echoParams', //?option=1&other=2
|
||||
qs: {
|
||||
option: 1,
|
||||
other: 2
|
||||
},
|
||||
body: '{"foo":"bar", "other": 1}'
|
||||
}, (error, response, body) => {
|
||||
expect(error).toBe(null);
|
||||
var res = JSON.parse(body).result;
|
||||
expect(res.option).toEqual('1');
|
||||
// Make sure query string params override body params
|
||||
expect(res.other).toEqual('2');
|
||||
expect(res.foo).toEqual("bar");
|
||||
delete Parse.Cloud.Functions['echoParams'];
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('test cloud function parameter validation success', (done) => {
|
||||
// Register a function with validation
|
||||
|
||||
@@ -133,26 +133,6 @@ describe('Installations', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('fails for android with device token', (done) => {
|
||||
var installId = '12345678-abcd-abcd-abcd-123456789abc';
|
||||
var t = '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306';
|
||||
var device = 'android';
|
||||
var input = {
|
||||
'installationId': installId,
|
||||
'deviceType': device,
|
||||
'deviceToken': t,
|
||||
'channels': ['foo', 'bar']
|
||||
};
|
||||
rest.create(config, auth.nobody(config), '_Installation', input)
|
||||
.then(() => {
|
||||
fail('Should not have been able to create an Installation.');
|
||||
done();
|
||||
}).catch((error) => {
|
||||
expect(error.code).toEqual(114);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('fails for android with missing type', (done) => {
|
||||
var installId = '12345678-abcd-abcd-abcd-123456789abc';
|
||||
var input = {
|
||||
|
||||
@@ -8,6 +8,20 @@
|
||||
var request = require('request');
|
||||
var passwordCrypto = require('../src/password');
|
||||
|
||||
function verifyACL(user) {
|
||||
const ACL = user.getACL();
|
||||
expect(ACL.getReadAccess(user)).toBe(true);
|
||||
expect(ACL.getWriteAccess(user)).toBe(true);
|
||||
expect(ACL.getPublicReadAccess()).toBe(true);
|
||||
expect(ACL.getPublicWriteAccess()).toBe(false);
|
||||
const perms = ACL.permissionsById;
|
||||
expect(Object.keys(perms).length).toBe(2);
|
||||
expect(perms[user.id].read).toBe(true);
|
||||
expect(perms[user.id].write).toBe(true);
|
||||
expect(perms['*'].read).toBe(true);
|
||||
expect(perms['*'].write).not.toBe(true);
|
||||
}
|
||||
|
||||
describe('Parse.User testing', () => {
|
||||
it("user sign up class method", (done) => {
|
||||
Parse.User.signUp("asdf", "zxcv", null, {
|
||||
@@ -57,6 +71,7 @@ describe('Parse.User testing', () => {
|
||||
Parse.User.logIn("asdf", "zxcv", {
|
||||
success: function(user) {
|
||||
equal(user.get("username"), "asdf");
|
||||
verifyACL(user);
|
||||
done();
|
||||
}
|
||||
});
|
||||
@@ -816,9 +831,11 @@ describe('Parse.User testing', () => {
|
||||
// server-side.
|
||||
var getMockFacebookProvider = function() {
|
||||
return {
|
||||
userId: "8675309",
|
||||
authToken: "jenny",
|
||||
expiration: new Date().toJSON(),
|
||||
authData: {
|
||||
id: "8675309",
|
||||
access_token: "jenny",
|
||||
expiration_date: new Date().toJSON(),
|
||||
},
|
||||
shouldError: false,
|
||||
loggedOut: false,
|
||||
synchronizedUserId: null,
|
||||
@@ -831,11 +848,7 @@ describe('Parse.User testing', () => {
|
||||
} else if (this.shouldCancel) {
|
||||
options.error(this, null);
|
||||
} else {
|
||||
options.success(this, {
|
||||
id: this.userId,
|
||||
access_token: this.authToken,
|
||||
expiration_date: this.expiration
|
||||
});
|
||||
options.success(this, this.authData);
|
||||
}
|
||||
},
|
||||
restoreAuthentication: function(authData) {
|
||||
@@ -874,13 +887,14 @@ describe('Parse.User testing', () => {
|
||||
ok(model instanceof Parse.User, "Model should be a Parse.User");
|
||||
strictEqual(Parse.User.current(), model);
|
||||
ok(model.extended(), "Should have used subclass.");
|
||||
strictEqual(provider.userId, provider.synchronizedUserId);
|
||||
strictEqual(provider.authToken, provider.synchronizedAuthToken);
|
||||
strictEqual(provider.expiration, provider.synchronizedExpiration);
|
||||
strictEqual(provider.authData.id, provider.synchronizedUserId);
|
||||
strictEqual(provider.authData.access_token, provider.synchronizedAuthToken);
|
||||
strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration);
|
||||
ok(model._isLinked("facebook"), "User should be linked to facebook");
|
||||
done();
|
||||
},
|
||||
error: function(model, error) {
|
||||
console.error(model, error);
|
||||
ok(false, "linking should have worked");
|
||||
done();
|
||||
}
|
||||
@@ -895,9 +909,9 @@ describe('Parse.User testing', () => {
|
||||
ok(model instanceof Parse.User, "Model should be a Parse.User");
|
||||
strictEqual(Parse.User.current(), model);
|
||||
ok(model.extended(), "Should have used the subclass.");
|
||||
strictEqual(provider.userId, provider.synchronizedUserId);
|
||||
strictEqual(provider.authToken, provider.synchronizedAuthToken);
|
||||
strictEqual(provider.expiration, provider.synchronizedExpiration);
|
||||
strictEqual(provider.authData.id, provider.synchronizedUserId);
|
||||
strictEqual(provider.authData.access_token, provider.synchronizedAuthToken);
|
||||
strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration);
|
||||
ok(model._isLinked("facebook"), "User should be linked to facebook");
|
||||
|
||||
Parse.User.logOut();
|
||||
@@ -910,20 +924,22 @@ describe('Parse.User testing', () => {
|
||||
"Model should be a Parse.User");
|
||||
ok(innerModel === Parse.User.current(),
|
||||
"Returned model should be the current user");
|
||||
ok(provider.userId === provider.synchronizedUserId);
|
||||
ok(provider.authToken === provider.synchronizedAuthToken);
|
||||
ok(provider.authData.id === provider.synchronizedUserId);
|
||||
ok(provider.authData.access_token === provider.synchronizedAuthToken);
|
||||
ok(innerModel._isLinked("facebook"),
|
||||
"User should be linked to facebook");
|
||||
ok(innerModel.existed(), "User should not be newly-created");
|
||||
done();
|
||||
},
|
||||
error: function(model, error) {
|
||||
fail(error);
|
||||
ok(false, "LogIn should have worked");
|
||||
done();
|
||||
}
|
||||
});
|
||||
},
|
||||
error: function(model, error) {
|
||||
console.error(model, error);
|
||||
ok(false, "LogIn should have worked");
|
||||
done();
|
||||
}
|
||||
@@ -972,9 +988,9 @@ describe('Parse.User testing', () => {
|
||||
success: function(model) {
|
||||
ok(model instanceof Parse.User, "Model should be a Parse.User");
|
||||
strictEqual(Parse.User.current(), model);
|
||||
strictEqual(provider.userId, provider.synchronizedUserId);
|
||||
strictEqual(provider.authToken, provider.synchronizedAuthToken);
|
||||
strictEqual(provider.expiration, provider.synchronizedExpiration);
|
||||
strictEqual(provider.authData.id, provider.synchronizedUserId);
|
||||
strictEqual(provider.authData.access_token, provider.synchronizedAuthToken);
|
||||
strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration);
|
||||
ok(model._isLinked("facebook"), "User should be linked");
|
||||
done();
|
||||
},
|
||||
@@ -1005,9 +1021,9 @@ describe('Parse.User testing', () => {
|
||||
success: function(model) {
|
||||
ok(model instanceof Parse.User, "Model should be a Parse.User");
|
||||
strictEqual(Parse.User.current(), model);
|
||||
strictEqual(provider.userId, provider.synchronizedUserId);
|
||||
strictEqual(provider.authToken, provider.synchronizedAuthToken);
|
||||
strictEqual(provider.expiration, provider.synchronizedExpiration);
|
||||
strictEqual(provider.authData.id, provider.synchronizedUserId);
|
||||
strictEqual(provider.authData.access_token, provider.synchronizedAuthToken);
|
||||
strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration);
|
||||
ok(model._isLinked("facebook"), "User should be linked.");
|
||||
var user2 = new Parse.User();
|
||||
user2.set("username", "testLinkWithProviderToAlreadyLinkedUser2");
|
||||
@@ -1108,9 +1124,9 @@ describe('Parse.User testing', () => {
|
||||
ok(model instanceof Parse.User, "Model should be a Parse.User.");
|
||||
strictEqual(Parse.User.current(), model);
|
||||
ok(model.extended(), "Should have used the subclass.");
|
||||
strictEqual(provider.userId, provider.synchronizedUserId);
|
||||
strictEqual(provider.authToken, provider.synchronizedAuthToken);
|
||||
strictEqual(provider.expiration, provider.synchronizedExpiration);
|
||||
strictEqual(provider.authData.id, provider.synchronizedUserId);
|
||||
strictEqual(provider.authData.access_token, provider.synchronizedAuthToken);
|
||||
strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration);
|
||||
ok(model._isLinked("facebook"), "User should be linked to facebook.");
|
||||
|
||||
model._unlinkFrom("facebook", {
|
||||
@@ -1144,9 +1160,9 @@ describe('Parse.User testing', () => {
|
||||
ok(model instanceof Parse.User, "Model should be a Parse.User");
|
||||
strictEqual(Parse.User.current(), model);
|
||||
ok(model.extended(), "Should have used the subclass.");
|
||||
strictEqual(provider.userId, provider.synchronizedUserId);
|
||||
strictEqual(provider.authToken, provider.synchronizedAuthToken);
|
||||
strictEqual(provider.expiration, provider.synchronizedExpiration);
|
||||
strictEqual(provider.authData.id, provider.synchronizedUserId);
|
||||
strictEqual(provider.authData.access_token, provider.synchronizedAuthToken);
|
||||
strictEqual(provider.authData.expiration_date, provider.synchronizedExpiration);
|
||||
ok(model._isLinked("facebook"), "User should be linked to facebook");
|
||||
|
||||
model._unlinkFrom("facebook", {
|
||||
@@ -1358,6 +1374,25 @@ describe('Parse.User testing', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('retrieve user data from fetch, make sure the session token hasn\'t changed', (done) => {
|
||||
var user = new Parse.User();
|
||||
user.setPassword("asdf");
|
||||
user.setUsername("zxcv");
|
||||
var currentSessionToken = "";
|
||||
Parse.Promise.as().then(function() {
|
||||
return user.signUp();
|
||||
}).then(function(){
|
||||
currentSessionToken = user.getSessionToken();
|
||||
return user.fetch();
|
||||
}).then(function(u){
|
||||
expect(currentSessionToken).toEqual(u.getSessionToken());
|
||||
done();
|
||||
}, function(error) {
|
||||
ok(false, error);
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
it('user save should fail with invalid email', (done) => {
|
||||
var user = new Parse.User();
|
||||
user.set('username', 'teste');
|
||||
@@ -1587,7 +1622,30 @@ describe('Parse.User testing', () => {
|
||||
}).then(function(newUser) {
|
||||
fail('Session should have been invalidated');
|
||||
done();
|
||||
}, function() {
|
||||
}, function(err) {
|
||||
expect(err.code).toBe(Parse.Error.INVALID_SESSION_TOKEN);
|
||||
expect(err.message).toBe('invalid session token');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('test parse user become', (done) => {
|
||||
var sessionToken = null;
|
||||
Parse.Promise.as().then(function() {
|
||||
return Parse.User.signUp("flessard", "folo",{'foo':1});
|
||||
}).then(function(newUser) {
|
||||
equal(Parse.User.current(), newUser);
|
||||
sessionToken = newUser.getSessionToken();
|
||||
ok(sessionToken);
|
||||
newUser.set('foo',2);
|
||||
return newUser.save();
|
||||
}).then(function() {
|
||||
return Parse.User.become(sessionToken);
|
||||
}).then(function(newUser) {
|
||||
equal(newUser.get('foo'), 2);
|
||||
done();
|
||||
}, function(e) {
|
||||
fail('The session should still be valid');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -100,6 +100,25 @@ describe('rest create', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('handles no anonymous users config', (done) => {
|
||||
var NoAnnonConfig = Object.assign({}, config, {enableAnonymousUsers: false});
|
||||
var data1 = {
|
||||
authData: {
|
||||
anonymous: {
|
||||
id: '00000000-0000-0000-0000-000000000001'
|
||||
}
|
||||
}
|
||||
};
|
||||
rest.create(NoAnnonConfig, auth.nobody(NoAnnonConfig), '_User', data1).then(() => {
|
||||
fail("Should throw an error");
|
||||
done();
|
||||
}, (err) => {
|
||||
expect(err.code).toEqual(Parse.Error.UNSUPPORTED_SERVICE);
|
||||
expect(err.message).toEqual('This authentication method is unsupported.');
|
||||
done();
|
||||
})
|
||||
});
|
||||
|
||||
it('test facebook signup and login', (done) => {
|
||||
var data = {
|
||||
|
||||
@@ -162,6 +162,9 @@ describe('Schema', () => {
|
||||
foo: 'string',
|
||||
})
|
||||
done();
|
||||
})
|
||||
.catch(error => {
|
||||
fail('Error creating class: ' + JSON.stringify(error));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -570,4 +573,32 @@ describe('Schema', () => {
|
||||
Parse.Object.enableSingleInstance();
|
||||
});
|
||||
});
|
||||
|
||||
it('can merge schemas', done => {
|
||||
expect(Schema.buildMergedSchemaObject({
|
||||
_id: 'SomeClass',
|
||||
someType: 'number'
|
||||
}, {
|
||||
newType: {type: 'Number'}
|
||||
})).toEqual({
|
||||
someType: {type: 'Number'},
|
||||
newType: {type: 'Number'},
|
||||
});
|
||||
done();
|
||||
});
|
||||
|
||||
it('can merge deletions', done => {
|
||||
expect(Schema.buildMergedSchemaObject({
|
||||
_id: 'SomeClass',
|
||||
someType: 'number',
|
||||
outDatedType: 'string',
|
||||
},{
|
||||
newType: {type: 'GeoPoint'},
|
||||
outDatedType: {__op: 'Delete'},
|
||||
})).toEqual({
|
||||
someType: {type: 'Number'},
|
||||
newType: {type: 'GeoPoint'},
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,7 +5,7 @@ jasmine.DEFAULT_TIMEOUT_INTERVAL = 2000;
|
||||
var cache = require('../src/cache');
|
||||
var DatabaseAdapter = require('../src/DatabaseAdapter');
|
||||
var express = require('express');
|
||||
var facebook = require('../src/facebook');
|
||||
var facebook = require('../src/oauth/facebook');
|
||||
var ParseServer = require('../src/index').ParseServer;
|
||||
|
||||
var databaseURI = process.env.DATABASE_URI;
|
||||
@@ -22,7 +22,13 @@ var api = new ParseServer({
|
||||
restAPIKey: 'rest',
|
||||
masterKey: 'test',
|
||||
collectionPrefix: 'test_',
|
||||
fileKey: 'test'
|
||||
fileKey: 'test',
|
||||
oauth: { // Override the facebook provider
|
||||
facebook: mockFacebook(),
|
||||
myoauth: {
|
||||
module: "../spec/myoauth" // relative path as it's run from src
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
var app = express();
|
||||
@@ -40,7 +46,6 @@ Parse.Promise.disableAPlusCompliant();
|
||||
|
||||
beforeEach(function(done) {
|
||||
Parse.initialize('test', 'test', 'test');
|
||||
mockFacebook();
|
||||
Parse.User.enableUnsafeCurrentUser();
|
||||
done();
|
||||
});
|
||||
@@ -175,18 +180,20 @@ function range(n) {
|
||||
}
|
||||
|
||||
function mockFacebook() {
|
||||
facebook.validateUserId = function(userId, accessToken) {
|
||||
if (userId === '8675309' && accessToken === 'jenny') {
|
||||
var facebook = {};
|
||||
facebook.validateAuthData = function(authData) {
|
||||
if (authData.id === '8675309' && authData.access_token === 'jenny') {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject();
|
||||
};
|
||||
facebook.validateAppId = function(appId, accessToken) {
|
||||
if (accessToken === 'jenny') {
|
||||
facebook.validateAppId = function(appId, authData) {
|
||||
if (authData.access_token === 'jenny') {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject();
|
||||
};
|
||||
return facebook;
|
||||
}
|
||||
|
||||
function clearData() {
|
||||
|
||||
17
spec/myoauth.js
Normal file
17
spec/myoauth.js
Normal file
@@ -0,0 +1,17 @@
|
||||
// Custom oauth provider by module
|
||||
|
||||
// Returns a promise that fulfills iff this user id is valid.
|
||||
function validateAuthData(authData) {
|
||||
if (authData.id == "12345" && authData.access_token == "12345") {
|
||||
return Promise.resolve();
|
||||
}
|
||||
return Promise.reject();
|
||||
}
|
||||
function validateAppId() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
validateAppId: validateAppId,
|
||||
validateAuthData: validateAuthData
|
||||
};
|
||||
@@ -94,7 +94,7 @@ describe('schemas', () => {
|
||||
headers: restKeyHeaders,
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(body.error).toEqual('unauthorized');
|
||||
expect(body.error).toEqual('master key not specified');
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -318,4 +318,319 @@ describe('schemas', () => {
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('requires the master key to modify schemas', done => {
|
||||
request.post({
|
||||
url: 'http://localhost:8378/1/schemas/NewClass',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {},
|
||||
}, (error, response, body) => {
|
||||
request.put({
|
||||
url: 'http://localhost:8378/1/schemas/NewClass',
|
||||
headers: noAuthHeaders,
|
||||
json: true,
|
||||
body: {},
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(403);
|
||||
expect(body.error).toEqual('unauthorized');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('rejects class name mis-matches in put', done => {
|
||||
request.put({
|
||||
url: 'http://localhost:8378/1/schemas/NewClass',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {className: 'WrongClassName'}
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
||||
expect(body.error).toEqual('class name mismatch between WrongClassName and NewClass');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('refuses to add fields to non-existent classes', done => {
|
||||
request.put({
|
||||
url: 'http://localhost:8378/1/schemas/NoClass',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
fields: {
|
||||
newField: {type: 'String'}
|
||||
}
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body.code).toEqual(Parse.Error.INVALID_CLASS_NAME);
|
||||
expect(body.error).toEqual('class NoClass does not exist');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it('refuses to put to existing fields, even if it would not be a change', done => {
|
||||
var obj = hasAllPODobject();
|
||||
obj.save()
|
||||
.then(() => {
|
||||
request.put({
|
||||
url: 'http://localhost:8378/1/schemas/HasAllPOD',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
fields: {
|
||||
aString: {type: 'String'}
|
||||
}
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body.code).toEqual(255);
|
||||
expect(body.error).toEqual('field aString exists, cannot update');
|
||||
done();
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
it('refuses to delete non-existant fields', done => {
|
||||
var obj = hasAllPODobject();
|
||||
obj.save()
|
||||
.then(() => {
|
||||
request.put({
|
||||
url: 'http://localhost:8378/1/schemas/HasAllPOD',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
fields: {
|
||||
nonExistantKey: {__op: "Delete"},
|
||||
}
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body.code).toEqual(255);
|
||||
expect(body.error).toEqual('field nonExistantKey does not exist, cannot delete');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('refuses to add a geopoint to a class that already has one', done => {
|
||||
var obj = hasAllPODobject();
|
||||
obj.save()
|
||||
.then(() => {
|
||||
request.put({
|
||||
url: 'http://localhost:8378/1/schemas/HasAllPOD',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
fields: {
|
||||
newGeo: {type: 'GeoPoint'}
|
||||
}
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body.code).toEqual(Parse.Error.INCORRECT_TYPE);
|
||||
expect(body.error).toEqual('currently, only one GeoPoint field may exist in an object. Adding newGeo when aGeoPoint already exists.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('refuses to add two geopoints', done => {
|
||||
var obj = new Parse.Object('NewClass');
|
||||
obj.set('aString', 'aString');
|
||||
obj.save()
|
||||
.then(() => {
|
||||
request.put({
|
||||
url: 'http://localhost:8378/1/schemas/NewClass',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
fields: {
|
||||
newGeo1: {type: 'GeoPoint'},
|
||||
newGeo2: {type: 'GeoPoint'},
|
||||
}
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(body.code).toEqual(Parse.Error.INCORRECT_TYPE);
|
||||
expect(body.error).toEqual('currently, only one GeoPoint field may exist in an object. Adding newGeo2 when newGeo1 already exists.');
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('allows you to delete and add a geopoint in the same request', done => {
|
||||
var obj = new Parse.Object('NewClass');
|
||||
obj.set('geo1', new Parse.GeoPoint({latitude: 0, longitude: 0}));
|
||||
obj.save()
|
||||
.then(() => {
|
||||
request.put({
|
||||
url: 'http://localhost:8378/1/schemas/NewClass',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
fields: {
|
||||
geo2: {type: 'GeoPoint'},
|
||||
geo1: {__op: 'Delete'}
|
||||
}
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(dd(body, {
|
||||
"className": "NewClass",
|
||||
"fields": {
|
||||
"ACL": {"type": "ACL"},
|
||||
"createdAt": {"type": "Date"},
|
||||
"objectId": {"type": "String"},
|
||||
"updatedAt": {"type": "Date"},
|
||||
"geo2": {"type": "GeoPoint"},
|
||||
}
|
||||
})).toEqual(undefined);
|
||||
done();
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
it('put with no modifications returns all fields', done => {
|
||||
var obj = hasAllPODobject();
|
||||
obj.save()
|
||||
.then(() => {
|
||||
request.put({
|
||||
url: 'http://localhost:8378/1/schemas/HasAllPOD',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {},
|
||||
}, (error, response, body) => {
|
||||
expect(body).toEqual(plainOldDataSchema);
|
||||
done();
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
it('lets you add fields', done => {
|
||||
request.post({
|
||||
url: 'http://localhost:8378/1/schemas/NewClass',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {},
|
||||
}, (error, response, body) => {
|
||||
request.put({
|
||||
url: 'http://localhost:8378/1/schemas/NewClass',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
fields: {
|
||||
newField: {type: 'String'}
|
||||
}
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(dd(body, {
|
||||
className: 'NewClass',
|
||||
fields: {
|
||||
"ACL": {"type": "ACL"},
|
||||
"createdAt": {"type": "Date"},
|
||||
"objectId": {"type": "String"},
|
||||
"updatedAt": {"type": "Date"},
|
||||
"newField": {"type": "String"},
|
||||
},
|
||||
})).toEqual(undefined);
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/schemas/NewClass',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
}, (error, response, body) => {
|
||||
expect(body).toEqual({
|
||||
className: 'NewClass',
|
||||
fields: {
|
||||
ACL: {type: 'ACL'},
|
||||
createdAt: {type: 'Date'},
|
||||
updatedAt: {type: 'Date'},
|
||||
objectId: {type: 'String'},
|
||||
newField: {type: 'String'},
|
||||
}
|
||||
});
|
||||
done();
|
||||
});
|
||||
});
|
||||
})
|
||||
});
|
||||
|
||||
it('lets you delete multiple fields and add fields', done => {
|
||||
var obj1 = hasAllPODobject();
|
||||
obj1.save()
|
||||
.then(() => {
|
||||
request.put({
|
||||
url: 'http://localhost:8378/1/schemas/HasAllPOD',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
fields: {
|
||||
aString: {__op: 'Delete'},
|
||||
aNumber: {__op: 'Delete'},
|
||||
aNewString: {type: 'String'},
|
||||
aNewNumber: {type: 'Number'},
|
||||
aNewRelation: {type: 'Relation', targetClass: 'HasAllPOD'},
|
||||
aNewPointer: {type: 'Pointer', targetClass: 'HasAllPOD'},
|
||||
}
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(body).toEqual({
|
||||
className: 'HasAllPOD',
|
||||
fields: {
|
||||
//Default fields
|
||||
ACL: {type: 'ACL'},
|
||||
createdAt: {type: 'Date'},
|
||||
updatedAt: {type: 'Date'},
|
||||
objectId: {type: 'String'},
|
||||
//Custom fields
|
||||
aBool: {type: 'Boolean'},
|
||||
aDate: {type: 'Date'},
|
||||
aObject: {type: 'Object'},
|
||||
aArray: {type: 'Array'},
|
||||
aGeoPoint: {type: 'GeoPoint'},
|
||||
aFile: {type: 'File'},
|
||||
aNewNumber: {type: 'Number'},
|
||||
aNewString: {type: 'String'},
|
||||
aNewPointer: {type: 'Pointer', targetClass: 'HasAllPOD'},
|
||||
aNewRelation: {type: 'Relation', targetClass: 'HasAllPOD'},
|
||||
}
|
||||
});
|
||||
var obj2 = new Parse.Object('HasAllPOD');
|
||||
obj2.set('aNewPointer', obj1);
|
||||
var relation = obj2.relation('aNewRelation');
|
||||
relation.add(obj1);
|
||||
obj2.save().then(done); //Just need to make sure saving works on the new object.
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('will not delete any fields if the additions are invalid', done => {
|
||||
var obj = hasAllPODobject();
|
||||
obj.save()
|
||||
.then(() => {
|
||||
request.put({
|
||||
url: 'http://localhost:8378/1/schemas/HasAllPOD',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
body: {
|
||||
fields: {
|
||||
fakeNewField: {type: 'fake type'},
|
||||
aString: {__op: 'Delete'}
|
||||
}
|
||||
}
|
||||
}, (error, response, body) => {
|
||||
expect(body.code).toEqual(Parse.Error.INCORRECT_TYPE);
|
||||
expect(body.error).toEqual('invalid field type: fake type');
|
||||
request.get({
|
||||
url: 'http://localhost:8378/1/schemas/HasAllPOD',
|
||||
headers: masterKeyHeaders,
|
||||
json: true,
|
||||
}, (error, response, body) => {
|
||||
expect(response.body).toEqual(plainOldDataSchema);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user