build: release
This commit is contained in:
@@ -326,22 +326,24 @@ describe('AudiencesRouter', () => {
|
||||
{ name: 'My Audience', query: JSON.stringify({ deviceType: 'ios' }) },
|
||||
{ useMasterKey: true }
|
||||
).then(audience => {
|
||||
database.collection('test__Audience').updateOne(
|
||||
{ _id: audience.objectId },
|
||||
{
|
||||
$set: {
|
||||
times_used: 1,
|
||||
_last_used: now,
|
||||
},
|
||||
},
|
||||
{},
|
||||
error => {
|
||||
expect(error).toEqual(null);
|
||||
database
|
||||
.collection('test__Audience')
|
||||
.updateOne(
|
||||
{ _id: audience.objectId },
|
||||
{
|
||||
$set: {
|
||||
times_used: 1,
|
||||
_last_used: now,
|
||||
},
|
||||
}
|
||||
)
|
||||
.then(result => {
|
||||
expect(result).toBeTruthy();
|
||||
database
|
||||
.collection('test__Audience')
|
||||
.find({ _id: audience.objectId })
|
||||
.toArray((error, rows) => {
|
||||
expect(error).toEqual(null);
|
||||
expect(error).toEqual(undefined);
|
||||
expect(rows[0]['times_used']).toEqual(1);
|
||||
expect(rows[0]['_last_used']).toEqual(now);
|
||||
Parse._request(
|
||||
@@ -361,8 +363,7 @@ describe('AudiencesRouter', () => {
|
||||
done.fail(error);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -1707,6 +1707,26 @@ describe('Apple Game Center Auth adapter', () => {
|
||||
expect(e.message).toBe('Apple Game Center - invalid publicKeyUrl: invalid.com');
|
||||
}
|
||||
});
|
||||
|
||||
it('validateAuthData invalid public key http url', async () => {
|
||||
const authData = {
|
||||
id: 'G:1965586982',
|
||||
publicKeyUrl: 'http://static.gc.apple.com/public-key/gc-prod-4.cer',
|
||||
timestamp: 1565257031287,
|
||||
signature: '1234',
|
||||
salt: 'DzqqrQ==',
|
||||
bundleId: 'cloud.xtralife.gamecenterauth',
|
||||
};
|
||||
|
||||
try {
|
||||
await gcenter.validateAuthData(authData);
|
||||
fail();
|
||||
} catch (e) {
|
||||
expect(e.message).toBe(
|
||||
'Apple Game Center - invalid publicKeyUrl: http://static.gc.apple.com/public-key/gc-prod-4.cer'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe('phant auth adapter', () => {
|
||||
|
||||
@@ -1999,6 +1999,25 @@ describe('beforeFind hooks', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should have object found with nested relational data query', async () => {
|
||||
const obj1 = Parse.Object.extend('TestObject');
|
||||
const obj2 = Parse.Object.extend('TestObject2');
|
||||
let item2 = new obj2();
|
||||
item2 = await item2.save();
|
||||
let item1 = new obj1();
|
||||
const relation = item1.relation('rel');
|
||||
relation.add(item2);
|
||||
item1 = await item1.save();
|
||||
Parse.Cloud.beforeFind('TestObject', req => {
|
||||
const additionalQ = new Parse.Query('TestObject');
|
||||
additionalQ.equalTo('rel', item2);
|
||||
return Parse.Query.and(req.query, additionalQ);
|
||||
});
|
||||
const q = new Parse.Query('TestObject');
|
||||
const res = await q.first();
|
||||
expect(res.id).toEqual(item1.id);
|
||||
});
|
||||
|
||||
it('should use the modified exclude query', async () => {
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
const q = req.query;
|
||||
@@ -3205,6 +3224,21 @@ describe('afterLogin hook', () => {
|
||||
const query = new Parse.Query(TestObject);
|
||||
await query.find({ context: { a: 'a' } });
|
||||
});
|
||||
|
||||
it('beforeFind and afterFind should have access to context while making fetch call', async () => {
|
||||
Parse.Cloud.beforeFind('TestObject', req => {
|
||||
expect(req.context.a).toEqual('a');
|
||||
expect(req.context.b).toBeUndefined();
|
||||
req.context.b = 'b';
|
||||
});
|
||||
Parse.Cloud.afterFind('TestObject', req => {
|
||||
expect(req.context.a).toEqual('a');
|
||||
expect(req.context.b).toEqual('b');
|
||||
});
|
||||
const obj = new TestObject();
|
||||
await obj.save();
|
||||
await obj.fetch({ context: { a: 'a' } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('saveFile hooks', () => {
|
||||
@@ -3516,23 +3550,4 @@ describe('sendEmail', () => {
|
||||
'Failed to send email because no mail adapter is configured for Parse Server.'
|
||||
);
|
||||
});
|
||||
|
||||
it('should have object found with nested relational data query', async () => {
|
||||
const obj1 = Parse.Object.extend('TestObject');
|
||||
const obj2 = Parse.Object.extend('TestObject2');
|
||||
let item2 = new obj2();
|
||||
item2 = await item2.save();
|
||||
let item1 = new obj1();
|
||||
const relation = item1.relation('rel');
|
||||
relation.add(item2);
|
||||
item1 = await item1.save();
|
||||
Parse.Cloud.beforeFind('TestObject', req => {
|
||||
const additionalQ = new Parse.Query('TestObject');
|
||||
additionalQ.equalTo('rel', item2);
|
||||
return Parse.Query.and(req.query, additionalQ);
|
||||
});
|
||||
const q = new Parse.Query('TestObject');
|
||||
const res = await q.first();
|
||||
expect(res.id).toEqual(item1.id);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -3,7 +3,6 @@ const WinstonLoggerAdapter = require('../lib/Adapters/Logger/WinstonLoggerAdapte
|
||||
.WinstonLoggerAdapter;
|
||||
const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter')
|
||||
.GridFSBucketAdapter;
|
||||
const GridStoreAdapter = require('../lib/Adapters/Files/GridStoreAdapter').GridStoreAdapter;
|
||||
const Config = require('../lib/Config');
|
||||
const FilesController = require('../lib/Controllers/FilesController').default;
|
||||
const databaseURI = 'mongodb://localhost:27017/parse';
|
||||
@@ -24,8 +23,8 @@ const mockAdapter = {
|
||||
describe('FilesController', () => {
|
||||
it('should properly expand objects', done => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const gridStoreAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
const filesController = new FilesController(gridStoreAdapter);
|
||||
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
const filesController = new FilesController(gridFSAdapter);
|
||||
const result = filesController.expandFilesInObject(config, function () {});
|
||||
|
||||
expect(result).toBeUndefined();
|
||||
@@ -88,19 +87,19 @@ describe('FilesController', () => {
|
||||
|
||||
it('should add a unique hash to the file name when the preserveFileName option is false', done => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const gridStoreAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
spyOn(gridStoreAdapter, 'createFile');
|
||||
gridStoreAdapter.createFile.and.returnValue(Promise.resolve());
|
||||
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
spyOn(gridFSAdapter, 'createFile');
|
||||
gridFSAdapter.createFile.and.returnValue(Promise.resolve());
|
||||
const fileName = 'randomFileName.pdf';
|
||||
const regexEscapedFileName = fileName.replace(/\./g, '\\$&');
|
||||
const filesController = new FilesController(gridStoreAdapter, null, {
|
||||
const filesController = new FilesController(gridFSAdapter, null, {
|
||||
preserveFileName: false,
|
||||
});
|
||||
|
||||
filesController.createFile(config, fileName);
|
||||
|
||||
expect(gridStoreAdapter.createFile).toHaveBeenCalledTimes(1);
|
||||
expect(gridStoreAdapter.createFile.calls.mostRecent().args[0]).toMatch(
|
||||
expect(gridFSAdapter.createFile).toHaveBeenCalledTimes(1);
|
||||
expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toMatch(
|
||||
`^.{32}_${regexEscapedFileName}$`
|
||||
);
|
||||
|
||||
@@ -109,42 +108,42 @@ describe('FilesController', () => {
|
||||
|
||||
it('should not add a unique hash to the file name when the preserveFileName option is true', done => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const gridStoreAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
spyOn(gridStoreAdapter, 'createFile');
|
||||
gridStoreAdapter.createFile.and.returnValue(Promise.resolve());
|
||||
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
spyOn(gridFSAdapter, 'createFile');
|
||||
gridFSAdapter.createFile.and.returnValue(Promise.resolve());
|
||||
const fileName = 'randomFileName.pdf';
|
||||
const filesController = new FilesController(gridStoreAdapter, null, {
|
||||
const filesController = new FilesController(gridFSAdapter, null, {
|
||||
preserveFileName: true,
|
||||
});
|
||||
|
||||
filesController.createFile(config, fileName);
|
||||
|
||||
expect(gridStoreAdapter.createFile).toHaveBeenCalledTimes(1);
|
||||
expect(gridStoreAdapter.createFile.calls.mostRecent().args[0]).toEqual(fileName);
|
||||
expect(gridFSAdapter.createFile).toHaveBeenCalledTimes(1);
|
||||
expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toEqual(fileName);
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should handle adapter without getMetadata', async () => {
|
||||
const gridStoreAdapter = new GridFSBucketAdapter(databaseURI);
|
||||
gridStoreAdapter.getMetadata = null;
|
||||
const filesController = new FilesController(gridStoreAdapter);
|
||||
const gridFSAdapter = new GridFSBucketAdapter(databaseURI);
|
||||
gridFSAdapter.getMetadata = null;
|
||||
const filesController = new FilesController(gridFSAdapter);
|
||||
|
||||
const result = await filesController.getMetadata();
|
||||
expect(result).toEqual({});
|
||||
});
|
||||
|
||||
it('should reject slashes in file names', done => {
|
||||
const gridStoreAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
const fileName = 'foo/randomFileName.pdf';
|
||||
expect(gridStoreAdapter.validateFilename(fileName)).not.toBe(null);
|
||||
expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null);
|
||||
done();
|
||||
});
|
||||
|
||||
it('should also reject slashes in file names', done => {
|
||||
const gridStoreAdapter = new GridStoreAdapter('mongodb://localhost:27017/parse');
|
||||
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
|
||||
const fileName = 'foo/randomFileName.pdf';
|
||||
expect(gridStoreAdapter.validateFilename(fileName)).not.toBe(null);
|
||||
expect(gridFSAdapter.validateFilename(fileName)).not.toBe(null);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
const GridStoreAdapter = require('../lib/Adapters/Files/GridStoreAdapter').GridStoreAdapter;
|
||||
const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter')
|
||||
.GridFSBucketAdapter;
|
||||
const { randomString } = require('../lib/cryptoUtils');
|
||||
@@ -14,25 +13,13 @@ async function expectMissingFile(gfsAdapter, name) {
|
||||
}
|
||||
}
|
||||
|
||||
describe_only_db('mongo')('GridFSBucket and GridStore interop', () => {
|
||||
describe_only_db('mongo')('GridFSBucket', () => {
|
||||
beforeEach(async () => {
|
||||
const gsAdapter = new GridStoreAdapter(databaseURI);
|
||||
const gsAdapter = new GridFSBucketAdapter(databaseURI);
|
||||
const db = await gsAdapter._connect();
|
||||
await db.dropDatabase();
|
||||
});
|
||||
|
||||
it('a file created in GridStore should be available in GridFS', async () => {
|
||||
const gsAdapter = new GridStoreAdapter(databaseURI);
|
||||
const gfsAdapter = new GridFSBucketAdapter(databaseURI);
|
||||
await expectMissingFile(gfsAdapter, 'myFileName');
|
||||
const originalString = 'abcdefghi';
|
||||
await gsAdapter.createFile('myFileName', originalString);
|
||||
const gsResult = await gsAdapter.getFileData('myFileName');
|
||||
expect(gsResult.toString('utf8')).toBe(originalString);
|
||||
const gfsResult = await gfsAdapter.getFileData('myFileName');
|
||||
expect(gfsResult.toString('utf8')).toBe(originalString);
|
||||
});
|
||||
|
||||
it('should save an encrypted file that can only be decrypted by a GridFS adapter with the encryptionKey', async () => {
|
||||
const unencryptedAdapter = new GridFSBucketAdapter(databaseURI);
|
||||
const encryptedAdapter = new GridFSBucketAdapter(
|
||||
@@ -451,7 +438,7 @@ describe_only_db('mongo')('GridFSBucket and GridStore interop', () => {
|
||||
await db.admin().serverStatus();
|
||||
expect(false).toBe(true);
|
||||
} catch (e) {
|
||||
expect(e.message).toEqual('topology was destroyed');
|
||||
expect(e.message).toEqual('MongoClient must be connected to perform this operation');
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
const MongoClient = require('mongodb').MongoClient;
|
||||
const GridStore = require('mongodb').GridStore;
|
||||
|
||||
const GridStoreAdapter = require('../lib/Adapters/Files/GridStoreAdapter').GridStoreAdapter;
|
||||
const Config = require('../lib/Config');
|
||||
const FilesController = require('../lib/Controllers/FilesController').default;
|
||||
|
||||
// Small additional tests to improve overall coverage
|
||||
describe_only_db('mongo')('GridStoreAdapter', () => {
|
||||
it('should properly instanciate the GridStore when deleting a file', async done => {
|
||||
const databaseURI = 'mongodb://localhost:27017/parse';
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const gridStoreAdapter = new GridStoreAdapter(databaseURI);
|
||||
const db = await gridStoreAdapter._connect();
|
||||
await db.dropDatabase();
|
||||
const filesController = new FilesController(gridStoreAdapter, Parse.applicationId, {});
|
||||
|
||||
// save original unlink before redefinition
|
||||
const originalUnlink = GridStore.prototype.unlink;
|
||||
|
||||
let gridStoreMode;
|
||||
|
||||
// new unlink method that will capture the mode in which GridStore was opened
|
||||
GridStore.prototype.unlink = function () {
|
||||
// restore original unlink during first call
|
||||
GridStore.prototype.unlink = originalUnlink;
|
||||
|
||||
gridStoreMode = this.mode;
|
||||
|
||||
return originalUnlink.call(this);
|
||||
};
|
||||
|
||||
filesController
|
||||
.createFile(config, 'myFilename.txt', 'my file content', 'text/plain')
|
||||
.then(myFile => {
|
||||
return MongoClient.connect(databaseURI)
|
||||
.then(client => {
|
||||
const database = client.db(client.s.options.dbName);
|
||||
// Verify the existance of the fs.files document
|
||||
return database
|
||||
.collection('fs.files')
|
||||
.count()
|
||||
.then(count => {
|
||||
expect(count).toEqual(1);
|
||||
return { database, client };
|
||||
});
|
||||
})
|
||||
.then(({ database, client }) => {
|
||||
// Verify the existance of the fs.files document
|
||||
return database
|
||||
.collection('fs.chunks')
|
||||
.count()
|
||||
.then(count => {
|
||||
expect(count).toEqual(1);
|
||||
return client.close();
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return filesController.deleteFile(config, myFile.name);
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
return MongoClient.connect(databaseURI)
|
||||
.then(client => {
|
||||
const database = client.db(client.s.options.dbName);
|
||||
// Verify the existance of the fs.files document
|
||||
return database
|
||||
.collection('fs.files')
|
||||
.count()
|
||||
.then(count => {
|
||||
expect(count).toEqual(0);
|
||||
return { database, client };
|
||||
});
|
||||
})
|
||||
.then(({ database, client }) => {
|
||||
// Verify the existance of the fs.files document
|
||||
return database
|
||||
.collection('fs.chunks')
|
||||
.count()
|
||||
.then(count => {
|
||||
expect(count).toEqual(0);
|
||||
return client.close();
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(() => {
|
||||
// Verify that gridStore was opened in read only mode
|
||||
expect(gridStoreMode).toEqual('r');
|
||||
|
||||
done();
|
||||
})
|
||||
.catch(fail);
|
||||
});
|
||||
|
||||
it('handleShutdown, close connection', async () => {
|
||||
const databaseURI = 'mongodb://localhost:27017/parse';
|
||||
const gridStoreAdapter = new GridStoreAdapter(databaseURI);
|
||||
|
||||
const db = await gridStoreAdapter._connect();
|
||||
const status = await db.admin().serverStatus();
|
||||
expect(status.connections.current > 0).toEqual(true);
|
||||
|
||||
await gridStoreAdapter.handleShutdown();
|
||||
try {
|
||||
await db.admin().serverStatus();
|
||||
expect(false).toBe(true);
|
||||
} catch (e) {
|
||||
expect(e.message).toEqual('topology was destroyed');
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -6,11 +6,14 @@ const rest = require('../lib/rest');
|
||||
const auth = require('../lib/Auth');
|
||||
const uuid = require('uuid');
|
||||
|
||||
describe_only_db('mongo')('Idempotency', () => {
|
||||
describe('Idempotency', () => {
|
||||
// Parameters
|
||||
/** Enable TTL expiration simulated by removing entry instead of waiting for MongoDB TTL monitor which
|
||||
runs only every 60s, so it can take up to 119s until entry removal - ain't nobody got time for that */
|
||||
const SIMULATE_TTL = true;
|
||||
const ttl = 2;
|
||||
const maxTimeOut = 4000;
|
||||
|
||||
// Helpers
|
||||
async function deleteRequestEntry(reqId) {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
@@ -38,9 +41,10 @@ describe_only_db('mongo')('Idempotency', () => {
|
||||
}
|
||||
await setup({
|
||||
paths: ['functions/.*', 'jobs/.*', 'classes/.*', 'users', 'installations'],
|
||||
ttl: 30,
|
||||
ttl: ttl,
|
||||
});
|
||||
});
|
||||
|
||||
// Tests
|
||||
it('should enforce idempotency for cloud code function', async () => {
|
||||
let counter = 0;
|
||||
@@ -56,7 +60,7 @@ describe_only_db('mongo')('Idempotency', () => {
|
||||
'X-Parse-Request-Id': 'abc-123',
|
||||
},
|
||||
};
|
||||
expect(Config.get(Parse.applicationId).idempotencyOptions.ttl).toBe(30);
|
||||
expect(Config.get(Parse.applicationId).idempotencyOptions.ttl).toBe(ttl);
|
||||
await request(params);
|
||||
await request(params).then(fail, e => {
|
||||
expect(e.status).toEqual(400);
|
||||
@@ -83,12 +87,38 @@ describe_only_db('mongo')('Idempotency', () => {
|
||||
if (SIMULATE_TTL) {
|
||||
await deleteRequestEntry('abc-123');
|
||||
} else {
|
||||
await new Promise(resolve => setTimeout(resolve, 130000));
|
||||
await new Promise(resolve => setTimeout(resolve, maxTimeOut));
|
||||
}
|
||||
await expectAsync(request(params)).toBeResolved();
|
||||
expect(counter).toBe(2);
|
||||
});
|
||||
|
||||
it_only_db('postgres')(
|
||||
'should delete request entry when postgress ttl function is called',
|
||||
async () => {
|
||||
const client = Config.get(Parse.applicationId).database.adapter._client;
|
||||
let counter = 0;
|
||||
Parse.Cloud.define('myFunction', () => {
|
||||
counter++;
|
||||
});
|
||||
const params = {
|
||||
method: 'POST',
|
||||
url: 'http://localhost:8378/1/functions/myFunction',
|
||||
headers: {
|
||||
'X-Parse-Application-Id': Parse.applicationId,
|
||||
'X-Parse-Master-Key': Parse.masterKey,
|
||||
'X-Parse-Request-Id': 'abc-123',
|
||||
},
|
||||
};
|
||||
await expectAsync(request(params)).toBeResolved();
|
||||
await expectAsync(request(params)).toBeRejected();
|
||||
await new Promise(resolve => setTimeout(resolve, maxTimeOut));
|
||||
await client.one('SELECT idempotency_delete_expired_records()');
|
||||
await expectAsync(request(params)).toBeResolved();
|
||||
expect(counter).toBe(2);
|
||||
}
|
||||
);
|
||||
|
||||
it('should enforce idempotency for cloud code jobs', async () => {
|
||||
let counter = 0;
|
||||
Parse.Cloud.job('myJob', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
'use strict';
|
||||
|
||||
const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default;
|
||||
const { MongoClient } = require('mongodb');
|
||||
const { MongoClient, Collection } = require('mongodb');
|
||||
const databaseURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
|
||||
const request = require('../lib/request');
|
||||
const Config = require('../lib/Config');
|
||||
@@ -101,7 +101,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
|
||||
done.fail('Find succeeded despite taking too long!');
|
||||
},
|
||||
err => {
|
||||
expect(err.name).toEqual('MongoError');
|
||||
expect(err.name).toEqual('MongoServerError');
|
||||
expect(err.code).toEqual(50);
|
||||
expect(err.message).toMatch('operation exceeded time limit');
|
||||
done();
|
||||
@@ -283,7 +283,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
|
||||
await adapter.database.admin().serverStatus();
|
||||
expect(false).toBe(true);
|
||||
} catch (e) {
|
||||
expect(e.message).toEqual('topology was destroyed');
|
||||
expect(e.message).toEqual('MongoClient must be connected to perform this operation');
|
||||
}
|
||||
});
|
||||
|
||||
@@ -392,8 +392,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
|
||||
const myObject = new Parse.Object('MyObject');
|
||||
await myObject.save();
|
||||
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'command').and.callThrough();
|
||||
spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
|
||||
|
||||
await request({
|
||||
method: 'POST',
|
||||
@@ -412,9 +411,9 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
|
||||
});
|
||||
|
||||
let found = false;
|
||||
databaseAdapter.database.serverConfig.command.calls.all().forEach(call => {
|
||||
Collection.prototype.findOneAndUpdate.calls.all().forEach(call => {
|
||||
found = true;
|
||||
expect(call.args[2].session.transaction.state).not.toBe('NO_TRANSACTION');
|
||||
expect(call.args[2].session.transaction.state).toBe('TRANSACTION_COMMITTED');
|
||||
});
|
||||
expect(found).toBe(true);
|
||||
});
|
||||
@@ -423,8 +422,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
|
||||
const myObject = new Parse.Object('MyObject');
|
||||
await myObject.save();
|
||||
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'command').and.callThrough();
|
||||
spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
|
||||
|
||||
await request({
|
||||
method: 'POST',
|
||||
@@ -443,9 +441,9 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
|
||||
});
|
||||
|
||||
let found = false;
|
||||
databaseAdapter.database.serverConfig.command.calls.all().forEach(call => {
|
||||
Collection.prototype.findOneAndUpdate.calls.all().forEach(call => {
|
||||
found = true;
|
||||
expect(call.args[2].session).toBe(undefined);
|
||||
expect(call.args[2].session).toBeFalsy();
|
||||
});
|
||||
expect(found).toBe(true);
|
||||
});
|
||||
@@ -454,8 +452,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
|
||||
const myObject = new Parse.Object('MyObject');
|
||||
await myObject.save();
|
||||
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'command').and.callThrough();
|
||||
spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
|
||||
|
||||
await request({
|
||||
method: 'POST',
|
||||
@@ -473,9 +470,9 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
|
||||
});
|
||||
|
||||
let found = false;
|
||||
databaseAdapter.database.serverConfig.command.calls.all().forEach(call => {
|
||||
Collection.prototype.findOneAndUpdate.calls.all().forEach(call => {
|
||||
found = true;
|
||||
expect(call.args[2].session).toBe(undefined);
|
||||
expect(call.args[2].session).toBeFalsy();
|
||||
});
|
||||
expect(found).toBe(true);
|
||||
});
|
||||
@@ -484,8 +481,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
|
||||
const myObject = new Parse.Object('MyObject');
|
||||
await myObject.save();
|
||||
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'command').and.callThrough();
|
||||
spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
|
||||
|
||||
await request({
|
||||
method: 'PUT',
|
||||
@@ -495,30 +491,28 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
|
||||
});
|
||||
|
||||
let found = false;
|
||||
databaseAdapter.database.serverConfig.command.calls.all().forEach(call => {
|
||||
Collection.prototype.findOneAndUpdate.calls.all().forEach(call => {
|
||||
found = true;
|
||||
expect(call.args[2].session).toBe(undefined);
|
||||
expect(call.args[2].session).toBeFalsy();
|
||||
});
|
||||
expect(found).toBe(true);
|
||||
});
|
||||
|
||||
it('should not use transactions when using SDK insert', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'insert').and.callThrough();
|
||||
spyOn(Collection.prototype, 'insertOne').and.callThrough();
|
||||
|
||||
const myObject = new Parse.Object('MyObject');
|
||||
await myObject.save();
|
||||
|
||||
const calls = databaseAdapter.database.serverConfig.insert.calls.all();
|
||||
const calls = Collection.prototype.insertOne.calls.all();
|
||||
expect(calls.length).toBeGreaterThan(0);
|
||||
calls.forEach(call => {
|
||||
expect(call.args[2].session.transaction.state).toBe('NO_TRANSACTION');
|
||||
expect(call.args[1].session).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not use transactions when using SDK update', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'update').and.callThrough();
|
||||
spyOn(Collection.prototype, 'findOneAndUpdate').and.callThrough();
|
||||
|
||||
const myObject = new Parse.Object('MyObject');
|
||||
await myObject.save();
|
||||
@@ -526,26 +520,25 @@ describe_only_db('mongo')('MongoStorageAdapter', () => {
|
||||
myObject.set('myAttribute', 'myValue');
|
||||
await myObject.save();
|
||||
|
||||
const calls = databaseAdapter.database.serverConfig.update.calls.all();
|
||||
const calls = Collection.prototype.findOneAndUpdate.calls.all();
|
||||
expect(calls.length).toBeGreaterThan(0);
|
||||
calls.forEach(call => {
|
||||
expect(call.args[2].session.transaction.state).toBe('NO_TRANSACTION');
|
||||
expect(call.args[2].session).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
||||
it('should not use transactions when using SDK delete', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'remove').and.callThrough();
|
||||
spyOn(Collection.prototype, 'deleteMany').and.callThrough();
|
||||
|
||||
const myObject = new Parse.Object('MyObject');
|
||||
await myObject.save();
|
||||
|
||||
await myObject.destroy();
|
||||
|
||||
const calls = databaseAdapter.database.serverConfig.remove.calls.all();
|
||||
const calls = Collection.prototype.deleteMany.calls.all();
|
||||
expect(calls.length).toBeGreaterThan(0);
|
||||
calls.forEach(call => {
|
||||
expect(call.args[2].session.transaction.state).toBe('NO_TRANSACTION');
|
||||
expect(call.args[1].session).toBeFalsy();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
const transform = require('../lib/Adapters/Storage/Mongo/MongoTransform');
|
||||
const dd = require('deep-diff');
|
||||
const mongodb = require('mongodb');
|
||||
const Utils = require('../lib/Utils');
|
||||
|
||||
describe('parseObjectToMongoObjectForCreate', () => {
|
||||
it('a basic number', done => {
|
||||
@@ -592,7 +593,7 @@ describe('relativeTimeToDate', () => {
|
||||
describe('In the future', () => {
|
||||
it('should parse valid natural time', () => {
|
||||
const text = 'in 1 year 2 weeks 12 days 10 hours 24 minutes 30 seconds';
|
||||
const { result, status, info } = transform.relativeTimeToDate(text, now);
|
||||
const { result, status, info } = Utils.relativeTimeToDate(text, now);
|
||||
expect(result.toISOString()).toBe('2018-10-22T23:52:46.617Z');
|
||||
expect(status).toBe('success');
|
||||
expect(info).toBe('future');
|
||||
@@ -602,7 +603,7 @@ describe('relativeTimeToDate', () => {
|
||||
describe('In the past', () => {
|
||||
it('should parse valid natural time', () => {
|
||||
const text = '2 days 12 hours 1 minute 12 seconds ago';
|
||||
const { result, status, info } = transform.relativeTimeToDate(text, now);
|
||||
const { result, status, info } = Utils.relativeTimeToDate(text, now);
|
||||
expect(result.toISOString()).toBe('2017-09-24T01:27:04.617Z');
|
||||
expect(status).toBe('success');
|
||||
expect(info).toBe('past');
|
||||
@@ -612,7 +613,7 @@ describe('relativeTimeToDate', () => {
|
||||
describe('From now', () => {
|
||||
it('should equal current time', () => {
|
||||
const text = 'now';
|
||||
const { result, status, info } = transform.relativeTimeToDate(text, now);
|
||||
const { result, status, info } = Utils.relativeTimeToDate(text, now);
|
||||
expect(result.toISOString()).toBe('2017-09-26T13:28:16.617Z');
|
||||
expect(status).toBe('success');
|
||||
expect(info).toBe('present');
|
||||
@@ -621,54 +622,54 @@ describe('relativeTimeToDate', () => {
|
||||
|
||||
describe('Error cases', () => {
|
||||
it('should error if string is completely gibberish', () => {
|
||||
expect(transform.relativeTimeToDate('gibberishasdnklasdnjklasndkl123j123')).toEqual({
|
||||
expect(Utils.relativeTimeToDate('gibberishasdnklasdnjklasndkl123j123')).toEqual({
|
||||
status: 'error',
|
||||
info: "Time should either start with 'in' or end with 'ago'",
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if string contains neither `ago` nor `in`', () => {
|
||||
expect(transform.relativeTimeToDate('12 hours 1 minute')).toEqual({
|
||||
expect(Utils.relativeTimeToDate('12 hours 1 minute')).toEqual({
|
||||
status: 'error',
|
||||
info: "Time should either start with 'in' or end with 'ago'",
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if there are missing units or numbers', () => {
|
||||
expect(transform.relativeTimeToDate('in 12 hours 1')).toEqual({
|
||||
expect(Utils.relativeTimeToDate('in 12 hours 1')).toEqual({
|
||||
status: 'error',
|
||||
info: 'Invalid time string. Dangling unit or number.',
|
||||
});
|
||||
|
||||
expect(transform.relativeTimeToDate('12 hours minute ago')).toEqual({
|
||||
expect(Utils.relativeTimeToDate('12 hours minute ago')).toEqual({
|
||||
status: 'error',
|
||||
info: 'Invalid time string. Dangling unit or number.',
|
||||
});
|
||||
});
|
||||
|
||||
it('should error on floating point numbers', () => {
|
||||
expect(transform.relativeTimeToDate('in 12.3 hours')).toEqual({
|
||||
expect(Utils.relativeTimeToDate('in 12.3 hours')).toEqual({
|
||||
status: 'error',
|
||||
info: "'12.3' is not an integer.",
|
||||
});
|
||||
});
|
||||
|
||||
it('should error if numbers are invalid', () => {
|
||||
expect(transform.relativeTimeToDate('12 hours 123a minute ago')).toEqual({
|
||||
expect(Utils.relativeTimeToDate('12 hours 123a minute ago')).toEqual({
|
||||
status: 'error',
|
||||
info: "'123a' is not an integer.",
|
||||
});
|
||||
});
|
||||
|
||||
it('should error on invalid interval units', () => {
|
||||
expect(transform.relativeTimeToDate('4 score 7 years ago')).toEqual({
|
||||
expect(Utils.relativeTimeToDate('4 score 7 years ago')).toEqual({
|
||||
status: 'error',
|
||||
info: "Invalid interval: 'score'",
|
||||
});
|
||||
});
|
||||
|
||||
it("should error when string contains 'ago' and 'in'", () => {
|
||||
expect(transform.relativeTimeToDate('in 1 day 2 minutes ago')).toEqual({
|
||||
expect(Utils.relativeTimeToDate('in 1 day 2 minutes ago')).toEqual({
|
||||
status: 'error',
|
||||
info: "Time cannot have both 'in' and 'ago'",
|
||||
});
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const http = require('http');
|
||||
const express = require('express');
|
||||
const req = require('../lib/request');
|
||||
const fetch = require('node-fetch');
|
||||
const fetch = (...args) => import('node-fetch').then(({ default: fetch }) => fetch(...args));
|
||||
const FormData = require('form-data');
|
||||
const ws = require('ws');
|
||||
require('./helper');
|
||||
@@ -9,13 +9,16 @@ const { updateCLP } = require('./support/dev');
|
||||
|
||||
const pluralize = require('pluralize');
|
||||
const { getMainDefinition } = require('apollo-utilities');
|
||||
const { ApolloLink, split } = require('apollo-link');
|
||||
const { createHttpLink } = require('apollo-link-http');
|
||||
const { InMemoryCache } = require('apollo-cache-inmemory');
|
||||
const { createUploadLink } = require('apollo-upload-client');
|
||||
const { SubscriptionClient } = require('subscriptions-transport-ws');
|
||||
const { WebSocketLink } = require('apollo-link-ws');
|
||||
const ApolloClient = require('apollo-client').default;
|
||||
const { WebSocketLink } = require('@apollo/client/link/ws');
|
||||
const {
|
||||
ApolloClient,
|
||||
InMemoryCache,
|
||||
ApolloLink,
|
||||
split,
|
||||
createHttpLink,
|
||||
} = require('@apollo/client/core');
|
||||
const gql = require('graphql-tag');
|
||||
const { toGlobalId } = require('graphql-relay');
|
||||
const {
|
||||
@@ -29,7 +32,7 @@ const {
|
||||
} = require('graphql');
|
||||
const { ParseServer } = require('../');
|
||||
const { ParseGraphQLServer } = require('../lib/GraphQL/ParseGraphQLServer');
|
||||
const ReadPreference = require('mongodb').ReadPreference;
|
||||
const { ReadPreference, Collection } = require('mongodb');
|
||||
const { v4: uuidv4 } = require('uuid');
|
||||
|
||||
function handleError(e) {
|
||||
@@ -2597,11 +2600,23 @@ describe('ParseGraphQLServer', () => {
|
||||
// "SecondaryObject:bBRgmzIRRM" < "SecondaryObject:nTMcuVbATY" true
|
||||
// base64("SecondaryObject:bBRgmzIRRM"") < base64(""SecondaryObject:nTMcuVbATY"") false
|
||||
// "U2Vjb25kYXJ5T2JqZWN0OmJCUmdteklSUk0=" < "U2Vjb25kYXJ5T2JqZWN0Om5UTWN1VmJBVFk=" false
|
||||
const originalIds = [
|
||||
getSecondaryObjectsResult.data.secondaryObject2.objectId,
|
||||
getSecondaryObjectsResult.data.secondaryObject4.objectId,
|
||||
];
|
||||
expect(
|
||||
findSecondaryObjectsResult.data.secondaryObjects.edges[0].node.objectId
|
||||
).toBeLessThan(
|
||||
findSecondaryObjectsResult.data.secondaryObjects.edges[1].node.objectId
|
||||
);
|
||||
).not.toBe(findSecondaryObjectsResult.data.secondaryObjects.edges[1].node.objectId);
|
||||
expect(
|
||||
originalIds.includes(
|
||||
findSecondaryObjectsResult.data.secondaryObjects.edges[0].node.objectId
|
||||
)
|
||||
).toBeTrue();
|
||||
expect(
|
||||
originalIds.includes(
|
||||
findSecondaryObjectsResult.data.secondaryObjects.edges[1].node.objectId
|
||||
)
|
||||
).toBeTrue();
|
||||
|
||||
const createPrimaryObjectResult = await apolloClient.mutate({
|
||||
mutation: gql`
|
||||
@@ -4458,8 +4473,7 @@ describe('ParseGraphQLServer', () => {
|
||||
|
||||
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
|
||||
|
||||
const databaseAdapter = parseServer.config.databaseController.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
await apolloClient.query({
|
||||
query: gql`
|
||||
@@ -4483,13 +4497,13 @@ describe('ParseGraphQLServer', () => {
|
||||
|
||||
let foundGraphQLClassReadPreference = false;
|
||||
let foundUserClassReadPreference = false;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) {
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) {
|
||||
foundGraphQLClassReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY);
|
||||
} else if (call.args[0].ns.collection.indexOf('_User') >= 0) {
|
||||
expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY);
|
||||
} else if (call.object.s.namespace.collection.indexOf('_User') >= 0) {
|
||||
foundUserClassReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY);
|
||||
expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4505,8 +4519,7 @@ describe('ParseGraphQLServer', () => {
|
||||
|
||||
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
|
||||
|
||||
const databaseAdapter = parseServer.config.databaseController.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
await apolloClient.query({
|
||||
query: gql`
|
||||
@@ -4530,13 +4543,13 @@ describe('ParseGraphQLServer', () => {
|
||||
|
||||
let foundGraphQLClassReadPreference = false;
|
||||
let foundUserClassReadPreference = false;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) {
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) {
|
||||
foundGraphQLClassReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY);
|
||||
} else if (call.args[0].ns.collection.indexOf('_User') >= 0) {
|
||||
expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY);
|
||||
} else if (call.object.s.namespace.collection.indexOf('_User') >= 0) {
|
||||
foundUserClassReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY);
|
||||
expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -4549,8 +4562,7 @@ describe('ParseGraphQLServer', () => {
|
||||
|
||||
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
|
||||
|
||||
const databaseAdapter = parseServer.config.databaseController.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
await apolloClient.query({
|
||||
query: gql`
|
||||
@@ -4577,13 +4589,13 @@ describe('ParseGraphQLServer', () => {
|
||||
|
||||
let foundGraphQLClassReadPreference = false;
|
||||
let foundUserClassReadPreference = false;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) {
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) {
|
||||
foundGraphQLClassReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY);
|
||||
} else if (call.args[0].ns.collection.indexOf('_User') >= 0) {
|
||||
expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY);
|
||||
} else if (call.object.s.namespace.collection.indexOf('_User') >= 0) {
|
||||
foundUserClassReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.NEAREST);
|
||||
expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5441,8 +5453,7 @@ describe('ParseGraphQLServer', () => {
|
||||
|
||||
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
|
||||
|
||||
const databaseAdapter = parseServer.config.databaseController.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
await apolloClient.query({
|
||||
query: gql`
|
||||
@@ -5467,13 +5478,13 @@ describe('ParseGraphQLServer', () => {
|
||||
|
||||
let foundGraphQLClassReadPreference = false;
|
||||
let foundUserClassReadPreference = false;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) {
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) {
|
||||
foundGraphQLClassReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY);
|
||||
} else if (call.args[0].ns.collection.indexOf('_User') >= 0) {
|
||||
expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY);
|
||||
} else if (call.object.s.namespace.collection.indexOf('_User') >= 0) {
|
||||
foundUserClassReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY);
|
||||
expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5486,8 +5497,7 @@ describe('ParseGraphQLServer', () => {
|
||||
|
||||
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
|
||||
|
||||
const databaseAdapter = parseServer.config.databaseController.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
await apolloClient.query({
|
||||
query: gql`
|
||||
@@ -5512,13 +5522,13 @@ describe('ParseGraphQLServer', () => {
|
||||
|
||||
let foundGraphQLClassReadPreference = false;
|
||||
let foundUserClassReadPreference = false;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) {
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) {
|
||||
foundGraphQLClassReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY);
|
||||
} else if (call.args[0].ns.collection.indexOf('_User') >= 0) {
|
||||
expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY);
|
||||
} else if (call.object.s.namespace.collection.indexOf('_User') >= 0) {
|
||||
foundUserClassReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY);
|
||||
expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5531,8 +5541,7 @@ describe('ParseGraphQLServer', () => {
|
||||
|
||||
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
|
||||
|
||||
const databaseAdapter = parseServer.config.databaseController.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
await apolloClient.query({
|
||||
query: gql`
|
||||
@@ -5559,13 +5568,13 @@ describe('ParseGraphQLServer', () => {
|
||||
|
||||
let foundGraphQLClassReadPreference = false;
|
||||
let foundUserClassReadPreference = false;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) {
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) {
|
||||
foundGraphQLClassReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY);
|
||||
} else if (call.args[0].ns.collection.indexOf('_User') >= 0) {
|
||||
expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY);
|
||||
} else if (call.object.s.namespace.collection.indexOf('_User') >= 0) {
|
||||
foundUserClassReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.NEAREST);
|
||||
expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -5579,8 +5588,7 @@ describe('ParseGraphQLServer', () => {
|
||||
|
||||
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
|
||||
|
||||
const databaseAdapter = parseServer.config.databaseController.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
await apolloClient.query({
|
||||
query: gql`
|
||||
@@ -5617,13 +5625,13 @@ describe('ParseGraphQLServer', () => {
|
||||
|
||||
let foundGraphQLClassReadPreference = false;
|
||||
let foundUserClassReadPreference = false;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('GraphQLClass') >= 0) {
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('GraphQLClass') >= 0) {
|
||||
foundGraphQLClassReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.SECONDARY);
|
||||
} else if (call.args[0].ns.collection.indexOf('_User') >= 0) {
|
||||
expect(call.args[1].readPreference).toBe(ReadPreference.SECONDARY);
|
||||
} else if (call.object.s.namespace.collection.indexOf('_User') >= 0) {
|
||||
foundUserClassReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.NEAREST);
|
||||
expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -4766,7 +4766,7 @@ describe('Parse.Query testing', () => {
|
||||
.catch(done.fail);
|
||||
});
|
||||
|
||||
it_only_db('mongo')('should handle relative times correctly', function (done) {
|
||||
it('should handle relative times correctly', async () => {
|
||||
const now = Date.now();
|
||||
const obj1 = new Parse.Object('MyCustomObject', {
|
||||
name: 'obj1',
|
||||
@@ -4777,94 +4777,75 @@ describe('Parse.Query testing', () => {
|
||||
ttl: new Date(now - 2 * 24 * 60 * 60 * 1000), // 2 days ago
|
||||
});
|
||||
|
||||
Parse.Object.saveAll([obj1, obj2])
|
||||
.then(() => {
|
||||
const q = new Parse.Query('MyCustomObject');
|
||||
q.greaterThan('ttl', { $relativeTime: 'in 1 day' });
|
||||
return q.find({ useMasterKey: true });
|
||||
})
|
||||
.then(results => {
|
||||
expect(results.length).toBe(1);
|
||||
})
|
||||
.then(() => {
|
||||
const q = new Parse.Query('MyCustomObject');
|
||||
q.greaterThan('ttl', { $relativeTime: '1 day ago' });
|
||||
return q.find({ useMasterKey: true });
|
||||
})
|
||||
.then(results => {
|
||||
expect(results.length).toBe(1);
|
||||
})
|
||||
.then(() => {
|
||||
const q = new Parse.Query('MyCustomObject');
|
||||
q.lessThan('ttl', { $relativeTime: '5 days ago' });
|
||||
return q.find({ useMasterKey: true });
|
||||
})
|
||||
.then(results => {
|
||||
expect(results.length).toBe(0);
|
||||
})
|
||||
.then(() => {
|
||||
const q = new Parse.Query('MyCustomObject');
|
||||
q.greaterThan('ttl', { $relativeTime: '3 days ago' });
|
||||
return q.find({ useMasterKey: true });
|
||||
})
|
||||
.then(results => {
|
||||
expect(results.length).toBe(2);
|
||||
})
|
||||
.then(() => {
|
||||
const q = new Parse.Query('MyCustomObject');
|
||||
q.greaterThan('ttl', { $relativeTime: 'now' });
|
||||
return q.find({ useMasterKey: true });
|
||||
})
|
||||
.then(results => {
|
||||
expect(results.length).toBe(1);
|
||||
})
|
||||
.then(() => {
|
||||
const q = new Parse.Query('MyCustomObject');
|
||||
q.greaterThan('ttl', { $relativeTime: 'now' });
|
||||
q.lessThan('ttl', { $relativeTime: 'in 1 day' });
|
||||
return q.find({ useMasterKey: true });
|
||||
})
|
||||
.then(results => {
|
||||
expect(results.length).toBe(0);
|
||||
})
|
||||
.then(() => {
|
||||
const q = new Parse.Query('MyCustomObject');
|
||||
q.greaterThan('ttl', { $relativeTime: '1 year 3 weeks ago' });
|
||||
return q.find({ useMasterKey: true });
|
||||
})
|
||||
.then(results => {
|
||||
expect(results.length).toBe(2);
|
||||
})
|
||||
.then(done, done.fail);
|
||||
await Parse.Object.saveAll([obj1, obj2]);
|
||||
const q1 = new Parse.Query('MyCustomObject');
|
||||
q1.greaterThan('ttl', { $relativeTime: 'in 1 day' });
|
||||
const results1 = await q1.find({ useMasterKey: true });
|
||||
expect(results1.length).toBe(1);
|
||||
|
||||
const q2 = new Parse.Query('MyCustomObject');
|
||||
q2.greaterThan('ttl', { $relativeTime: '1 day ago' });
|
||||
const results2 = await q2.find({ useMasterKey: true });
|
||||
expect(results2.length).toBe(1);
|
||||
|
||||
const q3 = new Parse.Query('MyCustomObject');
|
||||
q3.lessThan('ttl', { $relativeTime: '5 days ago' });
|
||||
const results3 = await q3.find({ useMasterKey: true });
|
||||
expect(results3.length).toBe(0);
|
||||
|
||||
const q4 = new Parse.Query('MyCustomObject');
|
||||
q4.greaterThan('ttl', { $relativeTime: '3 days ago' });
|
||||
const results4 = await q4.find({ useMasterKey: true });
|
||||
expect(results4.length).toBe(2);
|
||||
|
||||
const q5 = new Parse.Query('MyCustomObject');
|
||||
q5.greaterThan('ttl', { $relativeTime: 'now' });
|
||||
const results5 = await q5.find({ useMasterKey: true });
|
||||
expect(results5.length).toBe(1);
|
||||
|
||||
const q6 = new Parse.Query('MyCustomObject');
|
||||
q6.greaterThan('ttl', { $relativeTime: 'now' });
|
||||
q6.lessThan('ttl', { $relativeTime: 'in 1 day' });
|
||||
const results6 = await q6.find({ useMasterKey: true });
|
||||
expect(results6.length).toBe(0);
|
||||
|
||||
const q7 = new Parse.Query('MyCustomObject');
|
||||
q7.greaterThan('ttl', { $relativeTime: '1 year 3 weeks ago' });
|
||||
const results7 = await q7.find({ useMasterKey: true });
|
||||
expect(results7.length).toBe(2);
|
||||
});
|
||||
|
||||
it_only_db('mongo')('should error on invalid relative time', function (done) {
|
||||
it('should error on invalid relative time', async () => {
|
||||
const obj1 = new Parse.Object('MyCustomObject', {
|
||||
name: 'obj1',
|
||||
ttl: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000), // 2 days from now
|
||||
});
|
||||
|
||||
await obj1.save({ useMasterKey: true });
|
||||
const q = new Parse.Query('MyCustomObject');
|
||||
q.greaterThan('ttl', { $relativeTime: '-12 bananas ago' });
|
||||
obj1
|
||||
.save({ useMasterKey: true })
|
||||
.then(() => q.find({ useMasterKey: true }))
|
||||
.then(done.fail, () => done());
|
||||
try {
|
||||
await q.find({ useMasterKey: true });
|
||||
fail('Should have thrown error');
|
||||
} catch (error) {
|
||||
expect(error.code).toBe(Parse.Error.INVALID_JSON);
|
||||
}
|
||||
});
|
||||
|
||||
it_only_db('mongo')('should error when using $relativeTime on non-Date field', function (done) {
|
||||
it('should error when using $relativeTime on non-Date field', async () => {
|
||||
const obj1 = new Parse.Object('MyCustomObject', {
|
||||
name: 'obj1',
|
||||
nonDateField: 'abcd',
|
||||
ttl: new Date(Date.now() + 2 * 24 * 60 * 60 * 1000), // 2 days from now
|
||||
});
|
||||
|
||||
await obj1.save({ useMasterKey: true });
|
||||
const q = new Parse.Query('MyCustomObject');
|
||||
q.greaterThan('nonDateField', { $relativeTime: '1 day ago' });
|
||||
obj1
|
||||
.save({ useMasterKey: true })
|
||||
.then(() => q.find({ useMasterKey: true }))
|
||||
.then(done.fail, () => done());
|
||||
try {
|
||||
await q.find({ useMasterKey: true });
|
||||
fail('Should have thrown error');
|
||||
} catch (error) {
|
||||
expect(error.code).toBe(Parse.Error.INVALID_JSON);
|
||||
}
|
||||
});
|
||||
|
||||
it('should match complex structure with dot notation when using matchesKeyInQuery', function (done) {
|
||||
|
||||
@@ -15,35 +15,18 @@ describe('ParseServerRESTController', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('should handle a get request', done => {
|
||||
RESTController.request('GET', '/classes/MyObject').then(
|
||||
res => {
|
||||
expect(res.results.length).toBe(0);
|
||||
done();
|
||||
},
|
||||
err => {
|
||||
console.log(err);
|
||||
jfail(err);
|
||||
done();
|
||||
}
|
||||
);
|
||||
it('should handle a get request', async () => {
|
||||
const res = await RESTController.request('GET', '/classes/MyObject');
|
||||
expect(res.results.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle a get request with full serverURL mount path', done => {
|
||||
RESTController.request('GET', '/1/classes/MyObject').then(
|
||||
res => {
|
||||
expect(res.results.length).toBe(0);
|
||||
done();
|
||||
},
|
||||
err => {
|
||||
jfail(err);
|
||||
done();
|
||||
}
|
||||
);
|
||||
it('should handle a get request with full serverURL mount path', async () => {
|
||||
const res = await RESTController.request('GET', '/1/classes/MyObject');
|
||||
expect(res.results.length).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle a POST batch without transaction', done => {
|
||||
RESTController.request('POST', 'batch', {
|
||||
it('should handle a POST batch without transaction', async () => {
|
||||
const res = await RESTController.request('POST', 'batch', {
|
||||
requests: [
|
||||
{
|
||||
method: 'GET',
|
||||
@@ -59,20 +42,12 @@ describe('ParseServerRESTController', () => {
|
||||
path: '/classes/MyObject',
|
||||
},
|
||||
],
|
||||
}).then(
|
||||
res => {
|
||||
expect(res.length).toBe(3);
|
||||
done();
|
||||
},
|
||||
err => {
|
||||
jfail(err);
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
expect(res.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should handle a POST batch with transaction=false', done => {
|
||||
RESTController.request('POST', 'batch', {
|
||||
it('should handle a POST batch with transaction=false', async () => {
|
||||
const res = await RESTController.request('POST', 'batch', {
|
||||
requests: [
|
||||
{
|
||||
method: 'GET',
|
||||
@@ -89,16 +64,8 @@ describe('ParseServerRESTController', () => {
|
||||
},
|
||||
],
|
||||
transaction: false,
|
||||
}).then(
|
||||
res => {
|
||||
expect(res.length).toBe(3);
|
||||
done();
|
||||
},
|
||||
err => {
|
||||
jfail(err);
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
expect(res.length).toBe(3);
|
||||
});
|
||||
|
||||
it('should handle response status', async () => {
|
||||
@@ -186,54 +153,40 @@ describe('ParseServerRESTController', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle a batch request with transaction = true', async done => {
|
||||
await reconfigureServer();
|
||||
it('should handle a batch request with transaction = true', async () => {
|
||||
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||
myObject
|
||||
.save()
|
||||
.then(() => {
|
||||
return myObject.destroy();
|
||||
})
|
||||
.then(() => {
|
||||
spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
|
||||
return RESTController.request('POST', 'batch', {
|
||||
requests: [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/1/classes/MyObject',
|
||||
body: { key: 'value1' },
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/1/classes/MyObject',
|
||||
body: { key: 'value2' },
|
||||
},
|
||||
],
|
||||
transaction: true,
|
||||
}).then(response => {
|
||||
expect(response.length).toEqual(2);
|
||||
expect(response[0].success.objectId).toBeDefined();
|
||||
expect(response[0].success.createdAt).toBeDefined();
|
||||
expect(response[1].success.objectId).toBeDefined();
|
||||
expect(response[1].success.createdAt).toBeDefined();
|
||||
const query = new Parse.Query('MyObject');
|
||||
return query.find().then(results => {
|
||||
expect(databaseAdapter.createObject.calls.count() % 2).toBe(0);
|
||||
for (let i = 0; i + 1 < databaseAdapter.createObject.calls.length; i = i + 2) {
|
||||
expect(databaseAdapter.createObject.calls.argsFor(i)[3]).toBe(
|
||||
databaseAdapter.createObject.calls.argsFor(i + 1)[3]
|
||||
);
|
||||
}
|
||||
expect(results.map(result => result.get('key')).sort()).toEqual([
|
||||
'value1',
|
||||
'value2',
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
})
|
||||
.catch(done.fail);
|
||||
await myObject.save();
|
||||
await myObject.destroy();
|
||||
spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
const response = await RESTController.request('POST', 'batch', {
|
||||
requests: [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/1/classes/MyObject',
|
||||
body: { key: 'value1' },
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/1/classes/MyObject',
|
||||
body: { key: 'value2' },
|
||||
},
|
||||
],
|
||||
transaction: true,
|
||||
});
|
||||
expect(response.length).toEqual(2);
|
||||
expect(response[0].success.objectId).toBeDefined();
|
||||
expect(response[0].success.createdAt).toBeDefined();
|
||||
expect(response[1].success.objectId).toBeDefined();
|
||||
expect(response[1].success.createdAt).toBeDefined();
|
||||
const query = new Parse.Query('MyObject');
|
||||
const results = await query.find();
|
||||
expect(databaseAdapter.createObject.calls.count() % 2).toBe(0);
|
||||
for (let i = 0; i + 1 < databaseAdapter.createObject.calls.length; i = i + 2) {
|
||||
expect(databaseAdapter.createObject.calls.argsFor(i)[3]).toBe(
|
||||
databaseAdapter.createObject.calls.argsFor(i + 1)[3]
|
||||
);
|
||||
}
|
||||
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
|
||||
});
|
||||
|
||||
it('should not save anything when one operation fails in a transaction', async () => {
|
||||
@@ -560,21 +513,11 @@ describe('ParseServerRESTController', () => {
|
||||
});
|
||||
}
|
||||
|
||||
it('should handle a POST request', done => {
|
||||
RESTController.request('POST', '/classes/MyObject', { key: 'value' })
|
||||
.then(() => {
|
||||
return RESTController.request('GET', '/classes/MyObject');
|
||||
})
|
||||
.then(res => {
|
||||
expect(res.results.length).toBe(1);
|
||||
expect(res.results[0].key).toEqual('value');
|
||||
done();
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
jfail(err);
|
||||
done();
|
||||
});
|
||||
it('should handle a POST request', async () => {
|
||||
await RESTController.request('POST', '/classes/MyObject', { key: 'value' });
|
||||
const res = await RESTController.request('GET', '/classes/MyObject');
|
||||
expect(res.results.length).toBe(1);
|
||||
expect(res.results[0].key).toEqual('value');
|
||||
});
|
||||
|
||||
it('should handle a POST request with context', async () => {
|
||||
@@ -593,125 +536,76 @@ describe('ParseServerRESTController', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('ensures sessionTokens are properly handled', done => {
|
||||
let userId;
|
||||
Parse.User.signUp('user', 'pass')
|
||||
.then(user => {
|
||||
userId = user.id;
|
||||
const sessionToken = user.getSessionToken();
|
||||
return RESTController.request('GET', '/users/me', undefined, {
|
||||
sessionToken,
|
||||
});
|
||||
})
|
||||
.then(res => {
|
||||
// Result is in JSON format
|
||||
expect(res.objectId).toEqual(userId);
|
||||
done();
|
||||
})
|
||||
.catch(err => {
|
||||
console.log(err);
|
||||
jfail(err);
|
||||
done();
|
||||
it('ensures sessionTokens are properly handled', async () => {
|
||||
const user = await Parse.User.signUp('user', 'pass');
|
||||
const sessionToken = user.getSessionToken();
|
||||
const res = await RESTController.request('GET', '/users/me', undefined, {
|
||||
sessionToken,
|
||||
});
|
||||
// Result is in JSON format
|
||||
expect(res.objectId).toEqual(user.id);
|
||||
});
|
||||
|
||||
it('ensures masterKey is properly handled', async () => {
|
||||
const user = await Parse.User.signUp('user', 'pass');
|
||||
const userId = user.id;
|
||||
await Parse.User.logOut();
|
||||
const res = await RESTController.request('GET', '/classes/_User', undefined, {
|
||||
useMasterKey: true,
|
||||
});
|
||||
expect(res.results.length).toBe(1);
|
||||
expect(res.results[0].objectId).toEqual(userId);
|
||||
});
|
||||
|
||||
it('ensures no user is created when passing an empty username', async () => {
|
||||
try {
|
||||
await RESTController.request('POST', '/classes/_User', {
|
||||
username: '',
|
||||
password: 'world',
|
||||
});
|
||||
fail('Success callback should not be called when passing an empty username.');
|
||||
} catch (err) {
|
||||
expect(err.code).toBe(Parse.Error.USERNAME_MISSING);
|
||||
expect(err.message).toBe('bad or missing username');
|
||||
}
|
||||
});
|
||||
|
||||
it('ensures masterKey is properly handled', done => {
|
||||
let userId;
|
||||
Parse.User.signUp('user', 'pass')
|
||||
.then(user => {
|
||||
userId = user.id;
|
||||
return Parse.User.logOut().then(() => {
|
||||
return RESTController.request('GET', '/classes/_User', undefined, {
|
||||
useMasterKey: true,
|
||||
});
|
||||
});
|
||||
})
|
||||
.then(
|
||||
res => {
|
||||
expect(res.results.length).toBe(1);
|
||||
expect(res.results[0].objectId).toEqual(userId);
|
||||
done();
|
||||
},
|
||||
err => {
|
||||
jfail(err);
|
||||
done();
|
||||
}
|
||||
);
|
||||
it('ensures no user is created when passing an empty password', async () => {
|
||||
try {
|
||||
await RESTController.request('POST', '/classes/_User', {
|
||||
username: 'hello',
|
||||
password: '',
|
||||
});
|
||||
fail('Success callback should not be called when passing an empty password.');
|
||||
} catch (err) {
|
||||
expect(err.code).toBe(Parse.Error.PASSWORD_MISSING);
|
||||
expect(err.message).toBe('password is required');
|
||||
}
|
||||
});
|
||||
|
||||
it('ensures no user is created when passing an empty username', done => {
|
||||
RESTController.request('POST', '/classes/_User', {
|
||||
username: '',
|
||||
password: 'world',
|
||||
}).then(
|
||||
() => {
|
||||
jfail(new Error('Success callback should not be called when passing an empty username.'));
|
||||
done();
|
||||
},
|
||||
err => {
|
||||
expect(err.code).toBe(Parse.Error.USERNAME_MISSING);
|
||||
expect(err.message).toBe('bad or missing username');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('ensures no user is created when passing an empty password', done => {
|
||||
RESTController.request('POST', '/classes/_User', {
|
||||
username: 'hello',
|
||||
password: '',
|
||||
}).then(
|
||||
() => {
|
||||
jfail(new Error('Success callback should not be called when passing an empty password.'));
|
||||
done();
|
||||
},
|
||||
err => {
|
||||
expect(err.code).toBe(Parse.Error.PASSWORD_MISSING);
|
||||
expect(err.message).toBe('password is required');
|
||||
done();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
it('ensures no session token is created on creating users', done => {
|
||||
RESTController.request('POST', '/classes/_User', {
|
||||
it('ensures no session token is created on creating users', async () => {
|
||||
const user = await RESTController.request('POST', '/classes/_User', {
|
||||
username: 'hello',
|
||||
password: 'world',
|
||||
})
|
||||
.then(user => {
|
||||
expect(user.sessionToken).toBeUndefined();
|
||||
const query = new Parse.Query('_Session');
|
||||
return query.find({ useMasterKey: true });
|
||||
})
|
||||
.then(sessions => {
|
||||
expect(sessions.length).toBe(0);
|
||||
done();
|
||||
}, done.fail);
|
||||
});
|
||||
expect(user.sessionToken).toBeUndefined();
|
||||
const query = new Parse.Query('_Session');
|
||||
const sessions = await query.find({ useMasterKey: true });
|
||||
expect(sessions.length).toBe(0);
|
||||
});
|
||||
|
||||
it('ensures a session token is created when passing installationId != cloud', done => {
|
||||
RESTController.request(
|
||||
it('ensures a session token is created when passing installationId != cloud', async () => {
|
||||
const user = await RESTController.request(
|
||||
'POST',
|
||||
'/classes/_User',
|
||||
{ username: 'hello', password: 'world' },
|
||||
{ installationId: 'my-installation' }
|
||||
)
|
||||
.then(user => {
|
||||
expect(user.sessionToken).not.toBeUndefined();
|
||||
const query = new Parse.Query('_Session');
|
||||
return query.find({ useMasterKey: true });
|
||||
})
|
||||
.then(
|
||||
sessions => {
|
||||
expect(sessions.length).toBe(1);
|
||||
expect(sessions[0].get('installationId')).toBe('my-installation');
|
||||
done();
|
||||
},
|
||||
err => {
|
||||
jfail(err);
|
||||
done();
|
||||
}
|
||||
);
|
||||
);
|
||||
expect(user.sessionToken).not.toBeUndefined();
|
||||
const query = new Parse.Query('_Session');
|
||||
const sessions = await query.find({ useMasterKey: true });
|
||||
expect(sessions.length).toBe(1);
|
||||
expect(sessions[0].get('installationId')).toBe('my-installation');
|
||||
});
|
||||
|
||||
it('ensures logIn is saved with installationId', async () => {
|
||||
|
||||
@@ -4,8 +4,7 @@ const PostgresStorageAdapter = require('../lib/Adapters/Storage/Postgres/Postgre
|
||||
const postgresURI =
|
||||
process.env.PARSE_SERVER_TEST_DATABASE_URI ||
|
||||
'postgres://localhost:5432/parse_server_postgres_adapter_test_database';
|
||||
const ParseServer = require('../lib/index');
|
||||
const express = require('express');
|
||||
|
||||
//public schema
|
||||
const databaseOptions1 = {
|
||||
initOptions: {
|
||||
@@ -24,72 +23,56 @@ const GameScore = Parse.Object.extend({
|
||||
className: 'GameScore',
|
||||
});
|
||||
|
||||
function createParseServer(options) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const parseServer = new ParseServer.default(
|
||||
Object.assign({}, defaultConfiguration, options, {
|
||||
serverURL: 'http://localhost:12668/parse',
|
||||
serverStartComplete: error => {
|
||||
if (error) {
|
||||
reject(error);
|
||||
} else {
|
||||
expect(Parse.applicationId).toEqual('test');
|
||||
const app = express();
|
||||
app.use('/parse', parseServer.app);
|
||||
|
||||
const server = app.listen(12668);
|
||||
Parse.serverURL = 'http://localhost:12668/parse';
|
||||
resolve(server);
|
||||
}
|
||||
},
|
||||
})
|
||||
);
|
||||
});
|
||||
}
|
||||
|
||||
describe_only_db('postgres')('Postgres database init options', () => {
|
||||
let server;
|
||||
|
||||
afterAll(done => {
|
||||
if (server) {
|
||||
Parse.serverURL = 'http://localhost:8378/1';
|
||||
server.close(done);
|
||||
}
|
||||
});
|
||||
|
||||
it('should create server with public schema databaseOptions', done => {
|
||||
it('should create server with public schema databaseOptions', async () => {
|
||||
const adapter = new PostgresStorageAdapter({
|
||||
uri: postgresURI,
|
||||
collectionPrefix: 'test_',
|
||||
databaseOptions: databaseOptions1,
|
||||
});
|
||||
|
||||
createParseServer({ databaseAdapter: adapter })
|
||||
.then(newServer => {
|
||||
server = newServer;
|
||||
const score = new GameScore({
|
||||
score: 1337,
|
||||
playerName: 'Sean Plott',
|
||||
cheatMode: false,
|
||||
});
|
||||
return score.save();
|
||||
})
|
||||
.then(async () => {
|
||||
await reconfigureServer();
|
||||
done();
|
||||
}, done.fail);
|
||||
await reconfigureServer({
|
||||
databaseAdapter: adapter,
|
||||
});
|
||||
const score = new GameScore({
|
||||
score: 1337,
|
||||
playerName: 'Sean Plott',
|
||||
cheatMode: false,
|
||||
});
|
||||
await score.save();
|
||||
});
|
||||
|
||||
it('should fail to create server if schema databaseOptions does not exist', done => {
|
||||
it('should create server using postgresql uri with public schema databaseOptions', async () => {
|
||||
const postgresURI2 = new URL(postgresURI);
|
||||
postgresURI2.protocol = 'postgresql:';
|
||||
const adapter = new PostgresStorageAdapter({
|
||||
uri: postgresURI2.toString(),
|
||||
collectionPrefix: 'test_',
|
||||
databaseOptions: databaseOptions1,
|
||||
});
|
||||
await reconfigureServer({
|
||||
databaseAdapter: adapter,
|
||||
});
|
||||
const score = new GameScore({
|
||||
score: 1337,
|
||||
playerName: 'Sean Plott',
|
||||
cheatMode: false,
|
||||
});
|
||||
await score.save();
|
||||
});
|
||||
|
||||
it('should fail to create server if schema databaseOptions does not exist', async () => {
|
||||
const adapter = new PostgresStorageAdapter({
|
||||
uri: postgresURI,
|
||||
collectionPrefix: 'test_',
|
||||
databaseOptions: databaseOptions2,
|
||||
});
|
||||
|
||||
createParseServer({ databaseAdapter: adapter }).then(done.fail, async () => {
|
||||
await reconfigureServer();
|
||||
done();
|
||||
});
|
||||
try {
|
||||
await reconfigureServer({
|
||||
databaseAdapter: adapter,
|
||||
});
|
||||
fail('Should have thrown error');
|
||||
} catch (error) {
|
||||
expect(error).toBeDefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
@@ -149,6 +149,135 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => {
|
||||
await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith(undefined);
|
||||
});
|
||||
|
||||
it('$relativeTime should error on $eq', async () => {
|
||||
const tableName = '_User';
|
||||
const schema = {
|
||||
fields: {
|
||||
objectId: { type: 'String' },
|
||||
username: { type: 'String' },
|
||||
email: { type: 'String' },
|
||||
emailVerified: { type: 'Boolean' },
|
||||
createdAt: { type: 'Date' },
|
||||
updatedAt: { type: 'Date' },
|
||||
authData: { type: 'Object' },
|
||||
},
|
||||
};
|
||||
const client = adapter._client;
|
||||
await adapter.createTable(tableName, schema);
|
||||
await client.none('INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', [
|
||||
tableName,
|
||||
'objectId',
|
||||
'username',
|
||||
'Bugs',
|
||||
'Bunny',
|
||||
]);
|
||||
const database = Config.get(Parse.applicationId).database;
|
||||
await database.loadSchema({ clearCache: true });
|
||||
try {
|
||||
await database.find(
|
||||
tableName,
|
||||
{
|
||||
createdAt: {
|
||||
$eq: {
|
||||
$relativeTime: '12 days ago',
|
||||
},
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
fail('Should have thrown error');
|
||||
} catch (error) {
|
||||
expect(error.code).toBe(Parse.Error.INVALID_JSON);
|
||||
}
|
||||
await dropTable(client, tableName);
|
||||
});
|
||||
|
||||
it('$relativeTime should error on $ne', async () => {
|
||||
const tableName = '_User';
|
||||
const schema = {
|
||||
fields: {
|
||||
objectId: { type: 'String' },
|
||||
username: { type: 'String' },
|
||||
email: { type: 'String' },
|
||||
emailVerified: { type: 'Boolean' },
|
||||
createdAt: { type: 'Date' },
|
||||
updatedAt: { type: 'Date' },
|
||||
authData: { type: 'Object' },
|
||||
},
|
||||
};
|
||||
const client = adapter._client;
|
||||
await adapter.createTable(tableName, schema);
|
||||
await client.none('INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', [
|
||||
tableName,
|
||||
'objectId',
|
||||
'username',
|
||||
'Bugs',
|
||||
'Bunny',
|
||||
]);
|
||||
const database = Config.get(Parse.applicationId).database;
|
||||
await database.loadSchema({ clearCache: true });
|
||||
try {
|
||||
await database.find(
|
||||
tableName,
|
||||
{
|
||||
createdAt: {
|
||||
$ne: {
|
||||
$relativeTime: '12 days ago',
|
||||
},
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
fail('Should have thrown error');
|
||||
} catch (error) {
|
||||
expect(error.code).toBe(Parse.Error.INVALID_JSON);
|
||||
}
|
||||
await dropTable(client, tableName);
|
||||
});
|
||||
|
||||
it('$relativeTime should error on $exists', async () => {
|
||||
const tableName = '_User';
|
||||
const schema = {
|
||||
fields: {
|
||||
objectId: { type: 'String' },
|
||||
username: { type: 'String' },
|
||||
email: { type: 'String' },
|
||||
emailVerified: { type: 'Boolean' },
|
||||
createdAt: { type: 'Date' },
|
||||
updatedAt: { type: 'Date' },
|
||||
authData: { type: 'Object' },
|
||||
},
|
||||
};
|
||||
const client = adapter._client;
|
||||
await adapter.createTable(tableName, schema);
|
||||
await client.none('INSERT INTO $1:name ($2:name, $3:name) VALUES ($4, $5)', [
|
||||
tableName,
|
||||
'objectId',
|
||||
'username',
|
||||
'Bugs',
|
||||
'Bunny',
|
||||
]);
|
||||
const database = Config.get(Parse.applicationId).database;
|
||||
await database.loadSchema({ clearCache: true });
|
||||
try {
|
||||
await database.find(
|
||||
tableName,
|
||||
{
|
||||
createdAt: {
|
||||
$exists: {
|
||||
$relativeTime: '12 days ago',
|
||||
},
|
||||
},
|
||||
},
|
||||
{}
|
||||
);
|
||||
fail('Should have thrown error');
|
||||
} catch (error) {
|
||||
expect(error.code).toBe(Parse.Error.INVALID_JSON);
|
||||
}
|
||||
await dropTable(client, tableName);
|
||||
});
|
||||
|
||||
it('should use index for caseInsensitive query using Postgres', async () => {
|
||||
const tableName = '_User';
|
||||
const schema = {
|
||||
@@ -426,9 +555,21 @@ describe_only_db('postgres')('PostgresStorageAdapter', () => {
|
||||
},
|
||||
classLevelPermissions: undefined,
|
||||
});
|
||||
await new Promise(resolve => setTimeout(resolve, 500));
|
||||
await new Promise(resolve => setTimeout(resolve, 2000));
|
||||
expect(adapter._onchange).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Idempotency class should have function', async () => {
|
||||
await reconfigureServer();
|
||||
const adapter = Config.get('test').database.adapter;
|
||||
const client = adapter._client;
|
||||
const qs =
|
||||
"SELECT format('%I.%I(%s)', ns.nspname, p.proname, oidvectortypes(p.proargtypes)) FROM pg_proc p INNER JOIN pg_namespace ns ON (p.pronamespace = ns.oid) WHERE p.proname = 'idempotency_delete_expired_records'";
|
||||
const foundFunction = await client.one(qs);
|
||||
expect(foundFunction.format).toBe('public.idempotency_delete_expired_records()');
|
||||
await adapter.deleteIdempotencyFunction();
|
||||
await client.none(qs);
|
||||
});
|
||||
});
|
||||
|
||||
describe_only_db('postgres')('PostgresStorageAdapter shutdown', () => {
|
||||
@@ -438,4 +579,13 @@ describe_only_db('postgres')('PostgresStorageAdapter shutdown', () => {
|
||||
adapter.handleShutdown();
|
||||
expect(adapter._client.$pool.ending).toEqual(true);
|
||||
});
|
||||
|
||||
it('handleShutdown, close connection of postgresql uri', () => {
|
||||
const databaseURI2 = new URL(databaseURI);
|
||||
databaseURI2.protocol = 'postgresql:';
|
||||
const adapter = new PostgresStorageAdapter({ uri: databaseURI2.toString() });
|
||||
expect(adapter._client.$pool.ending).toEqual(false);
|
||||
adapter.handleShutdown();
|
||||
expect(adapter._client.$pool.ending).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -562,11 +562,7 @@ describe('PushController', () => {
|
||||
});
|
||||
const pushStatusId = await sendPush(payload, {}, config, auth);
|
||||
// it is enqueued so it can take time
|
||||
await new Promise(resolve => {
|
||||
setTimeout(() => {
|
||||
resolve();
|
||||
}, 1000);
|
||||
});
|
||||
await sleep(1000);
|
||||
Parse.serverURL = 'http://localhost:8378/1'; // GOOD url
|
||||
const result = await Parse.Push.getPushStatus(pushStatusId);
|
||||
expect(result).toBeDefined();
|
||||
@@ -767,7 +763,7 @@ describe('PushController', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('should not schedule push when not configured', done => {
|
||||
it('should not schedule push when not configured', async () => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
@@ -800,33 +796,20 @@ describe('PushController', () => {
|
||||
installations.push(installation);
|
||||
}
|
||||
|
||||
reconfigureServer({
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
})
|
||||
.then(() => {
|
||||
return Parse.Object.saveAll(installations)
|
||||
.then(() => {
|
||||
return pushController.sendPush(payload, {}, config, auth);
|
||||
})
|
||||
.then(() => new Promise(resolve => setTimeout(resolve, 300)));
|
||||
})
|
||||
.then(() => {
|
||||
const query = new Parse.Query('_PushStatus');
|
||||
return query.find({ useMasterKey: true }).then(results => {
|
||||
expect(results.length).toBe(1);
|
||||
const pushStatus = results[0];
|
||||
expect(pushStatus.get('status')).not.toBe('scheduled');
|
||||
done();
|
||||
});
|
||||
})
|
||||
.catch(err => {
|
||||
console.error(err);
|
||||
fail('should not fail');
|
||||
done();
|
||||
});
|
||||
});
|
||||
await Parse.Object.saveAll(installations);
|
||||
await pushController.sendPush(payload, {}, config, auth);
|
||||
await sleep(1000);
|
||||
const query = new Parse.Query('_PushStatus');
|
||||
const results = await query.find({ useMasterKey: true });
|
||||
expect(results.length).toBe(1);
|
||||
const pushStatus = results[0];
|
||||
expect(pushStatus.get('status')).not.toBe('scheduled');
|
||||
});
|
||||
|
||||
it('should schedule push when configured', done => {
|
||||
it('should schedule push when configured', async () => {
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
};
|
||||
@@ -866,28 +849,19 @@ describe('PushController', () => {
|
||||
installation.set('deviceType', 'ios');
|
||||
installations.push(installation);
|
||||
}
|
||||
reconfigureServer({
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
scheduledPush: true,
|
||||
})
|
||||
.then(() => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
return Parse.Object.saveAll(installations)
|
||||
.then(() => {
|
||||
return pushController.sendPush(payload, {}, config, auth);
|
||||
})
|
||||
.then(() => new Promise(resolve => setTimeout(resolve, 300)));
|
||||
})
|
||||
.then(() => {
|
||||
const query = new Parse.Query('_PushStatus');
|
||||
return query.find({ useMasterKey: true }).then(results => {
|
||||
expect(results.length).toBe(1);
|
||||
const pushStatus = results[0];
|
||||
expect(pushStatus.get('status')).toBe('scheduled');
|
||||
});
|
||||
})
|
||||
.then(done)
|
||||
.catch(done.err);
|
||||
});
|
||||
const config = Config.get(Parse.applicationId);
|
||||
await Parse.Object.saveAll(installations);
|
||||
await pushController.sendPush(payload, {}, config, auth);
|
||||
await sleep(1000);
|
||||
const query = new Parse.Query('_PushStatus');
|
||||
const results = await query.find({ useMasterKey: true });
|
||||
expect(results.length).toBe(1);
|
||||
const pushStatus = results[0];
|
||||
expect(pushStatus.get('status')).toBe('scheduled');
|
||||
});
|
||||
|
||||
it('should not enqueue push when device token is not set', async () => {
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
const Parse = require('parse/node');
|
||||
const ReadPreference = require('mongodb').ReadPreference;
|
||||
const { ReadPreference, Collection } = require('mongodb');
|
||||
const request = require('../lib/request');
|
||||
const Config = require('../lib/Config');
|
||||
|
||||
function waitForReplication() {
|
||||
return new Promise(function (resolve) {
|
||||
@@ -13,8 +12,6 @@ function waitForReplication() {
|
||||
|
||||
describe_only_db('mongo')('Read preference option', () => {
|
||||
it('should find in primary by default', done => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
@@ -22,7 +19,7 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
|
||||
Parse.Object.saveAll([obj0, obj1])
|
||||
.then(() => {
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
const query = new Parse.Query('MyObject');
|
||||
query.equalTo('boolKey', false);
|
||||
@@ -31,10 +28,10 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(results.length).toBe(1);
|
||||
expect(results[0].get('boolKey')).toBe(false);
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.PRIMARY);
|
||||
expect(call.object.s.readPreference.mode).toBe(ReadPreference.PRIMARY);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -58,15 +55,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
databaseAdapter: new MongoStorageAdapter(adapterOptions),
|
||||
});
|
||||
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
const query = new Parse.Query('MyObject');
|
||||
query.equalTo('boolKey', false);
|
||||
@@ -76,10 +71,10 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(results[0].get('boolKey')).toBe(false);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = true;
|
||||
expect(call.args[0].options.readPreference.mode).toBe(ReadPreference.NEAREST);
|
||||
expect(call.args[1].readPreference).toBe(ReadPreference.NEAREST);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -87,15 +82,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change read preference in the beforeFind trigger', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
req.readPreference = 'SECONDARY';
|
||||
@@ -110,9 +103,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(results[0].get('boolKey')).toBe(false);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -120,15 +113,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should check read preference as case insensitive', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
req.readPreference = 'sEcOnDarY';
|
||||
@@ -144,9 +135,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(results[0].get('boolKey')).toBe(false);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -154,15 +145,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change read preference in the beforeFind trigger even changing query', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
req.query.equalTo('boolKey', true);
|
||||
@@ -178,9 +167,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(results[0].get('boolKey')).toBe(true);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -188,15 +177,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change read preference in the beforeFind trigger even returning query', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
req.readPreference = 'SECONDARY';
|
||||
@@ -216,9 +203,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(results[0].get('boolKey')).toBe(true);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -226,15 +213,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change read preference in the beforeFind trigger even returning promise', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
req.readPreference = 'SECONDARY';
|
||||
@@ -253,9 +238,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(results[0].get('boolKey')).toBe(true);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -263,15 +248,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change read preference to PRIMARY_PREFERRED', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
req.readPreference = 'PRIMARY_PREFERRED';
|
||||
@@ -286,9 +269,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(results[0].get('boolKey')).toBe(false);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -296,15 +279,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change read preference to SECONDARY_PREFERRED', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
req.readPreference = 'SECONDARY_PREFERRED';
|
||||
@@ -319,9 +300,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(results[0].get('boolKey')).toBe(false);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -329,15 +310,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change read preference to NEAREST', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
req.readPreference = 'NEAREST';
|
||||
@@ -352,9 +331,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(results[0].get('boolKey')).toBe(false);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -362,15 +341,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change read preference for GET', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
req.readPreference = 'SECONDARY';
|
||||
@@ -383,9 +360,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(result.get('boolKey')).toBe(false);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -393,15 +370,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change read preference for GET using API', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
req.readPreference = 'SECONDARY';
|
||||
@@ -421,9 +396,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(body.boolKey).toBe(false);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -431,15 +406,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change read preference for GET directly from API', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
await waitForReplication();
|
||||
|
||||
const response = await request({
|
||||
@@ -454,9 +427,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(response.data.boolKey).toBe(false);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -464,15 +437,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change read preference for GET using API through the beforeFind overriding API option', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
req.readPreference = 'SECONDARY_PREFERRED';
|
||||
@@ -491,9 +462,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(response.data.boolKey).toBe(false);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -501,15 +472,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change read preference for FIND using API through beforeFind trigger', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
req.readPreference = 'SECONDARY';
|
||||
@@ -528,9 +497,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(response.data.results.length).toEqual(2);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -538,15 +507,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change read preference for FIND directly from API', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
await waitForReplication();
|
||||
|
||||
const response = await request({
|
||||
@@ -561,9 +528,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(response.data.results.length).toEqual(2);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -571,15 +538,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change read preference for FIND using API through the beforeFind overriding API option', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
req.readPreference = 'SECONDARY_PREFERRED';
|
||||
@@ -598,9 +563,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(response.data.results.length).toEqual(2);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -608,15 +573,13 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
xit('should change read preference for count', done => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject');
|
||||
obj1.set('boolKey', true);
|
||||
|
||||
Parse.Object.saveAll([obj0, obj1]).then(() => {
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject', req => {
|
||||
req.readPreference = 'SECONDARY';
|
||||
@@ -631,9 +594,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
expect(result).toBe(1);
|
||||
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -659,17 +622,16 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
await waitForReplication();
|
||||
|
||||
// Spy on DB adapter
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'startSession').and.callThrough();
|
||||
spyOn(Collection.prototype, 'aggregate').and.callThrough();
|
||||
// Query
|
||||
const query = new Parse.Query('MyObject');
|
||||
const results = await query.aggregate([{ match: { boolKey: false } }]);
|
||||
// Validate
|
||||
expect(results.length).toBe(1);
|
||||
let readPreference = null;
|
||||
databaseAdapter.database.serverConfig.startSession.calls.all().forEach(call => {
|
||||
if (call.args[0].owner.ns.indexOf('MyObject') > -1) {
|
||||
readPreference = call.args[0].owner.operation.readPreference.mode;
|
||||
Collection.prototype.aggregate.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') > -1) {
|
||||
readPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
expect(readPreference).toEqual(ReadPreference.SECONDARY);
|
||||
@@ -685,8 +647,7 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
await waitForReplication();
|
||||
|
||||
// Spy on DB adapter
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
// Query
|
||||
const query = new Parse.Query('MyObject');
|
||||
query.equalTo('boolKey', false);
|
||||
@@ -695,9 +656,9 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
// Validate
|
||||
expect(results.length).toBe(1);
|
||||
let myObjectReadPreference = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') >= 0) {
|
||||
myObjectReadPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
expect(myObjectReadPreference).toEqual(ReadPreference.SECONDARY);
|
||||
@@ -713,8 +674,7 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
await waitForReplication();
|
||||
|
||||
// Spy on DB adapter
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
spyOn(databaseAdapter.database.serverConfig, 'startSession').and.callThrough();
|
||||
spyOn(Collection.prototype, 'aggregate').and.callThrough();
|
||||
// Query
|
||||
const query = new Parse.Query('MyObject');
|
||||
query.readPreference('SECONDARY');
|
||||
@@ -722,17 +682,15 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
// Validate
|
||||
expect(results.length).toBe(1);
|
||||
let readPreference = null;
|
||||
databaseAdapter.database.serverConfig.startSession.calls.all().forEach(call => {
|
||||
if (call.args[0].owner.ns.indexOf('MyObject') > -1) {
|
||||
readPreference = call.args[0].owner.operation.readPreference.mode;
|
||||
Collection.prototype.aggregate.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject') > -1) {
|
||||
readPreference = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
expect(readPreference).toEqual(ReadPreference.SECONDARY);
|
||||
});
|
||||
|
||||
it('should find includes in same replica of readPreference by default', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject0');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject1');
|
||||
@@ -743,7 +701,7 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
obj2.set('myObject1', obj1);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1, obj2]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject2', req => {
|
||||
req.readPreference = 'SECONDARY';
|
||||
@@ -765,15 +723,15 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
let myObjectReadPreference0 = null;
|
||||
let myObjectReadPreference1 = null;
|
||||
let myObjectReadPreference2 = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -783,8 +741,6 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change includes read preference', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject0');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject1');
|
||||
@@ -795,7 +751,7 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
obj2.set('myObject1', obj1);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1, obj2]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject2', req => {
|
||||
req.readPreference = 'SECONDARY_PREFERRED';
|
||||
@@ -818,15 +774,15 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
let myObjectReadPreference0 = null;
|
||||
let myObjectReadPreference1 = null;
|
||||
let myObjectReadPreference2 = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -836,8 +792,6 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change includes read preference when finding through API', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject0');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject1');
|
||||
@@ -848,7 +802,7 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
obj2.set('myObject1', obj1);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1, obj2]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
await waitForReplication();
|
||||
|
||||
const response = await request({
|
||||
@@ -873,15 +827,15 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
let myObjectReadPreference0 = null;
|
||||
let myObjectReadPreference1 = null;
|
||||
let myObjectReadPreference2 = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -891,8 +845,6 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change includes read preference when getting through API', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject0');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject1');
|
||||
@@ -903,7 +855,7 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
obj2.set('myObject1', obj1);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1, obj2]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
await waitForReplication();
|
||||
|
||||
const response = await request({
|
||||
@@ -929,15 +881,15 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
let myObjectReadPreference0 = null;
|
||||
let myObjectReadPreference1 = null;
|
||||
let myObjectReadPreference2 = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -947,8 +899,6 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should find subqueries in same replica of readPreference by default', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject0');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject1');
|
||||
@@ -959,7 +909,7 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
obj2.set('myObject1', obj1);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1, obj2]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject2', req => {
|
||||
req.readPreference = 'SECONDARY';
|
||||
@@ -982,15 +932,15 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
let myObjectReadPreference0 = null;
|
||||
let myObjectReadPreference1 = null;
|
||||
let myObjectReadPreference2 = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1000,8 +950,6 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change subqueries read preference when using matchesQuery', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject0');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject1');
|
||||
@@ -1012,7 +960,7 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
obj2.set('myObject1', obj1);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1, obj2]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject2', req => {
|
||||
req.readPreference = 'SECONDARY_PREFERRED';
|
||||
@@ -1036,15 +984,15 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
let myObjectReadPreference0 = null;
|
||||
let myObjectReadPreference1 = null;
|
||||
let myObjectReadPreference2 = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1054,8 +1002,6 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change subqueries read preference when using doesNotMatchQuery', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject0');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject1');
|
||||
@@ -1066,7 +1012,7 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
obj2.set('myObject1', obj1);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1, obj2]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject2', req => {
|
||||
req.readPreference = 'SECONDARY_PREFERRED';
|
||||
@@ -1090,15 +1036,15 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
let myObjectReadPreference0 = null;
|
||||
let myObjectReadPreference1 = null;
|
||||
let myObjectReadPreference2 = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1108,8 +1054,6 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject0');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject1');
|
||||
@@ -1120,7 +1064,7 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
obj2.set('myObject1', obj1);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1, obj2]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
|
||||
Parse.Cloud.beforeFind('MyObject2', req => {
|
||||
req.readPreference = 'SECONDARY_PREFERRED';
|
||||
@@ -1145,15 +1089,15 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
let myObjectReadPreference0 = null;
|
||||
let myObjectReadPreference1 = null;
|
||||
let myObjectReadPreference2 = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1163,8 +1107,6 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
});
|
||||
|
||||
it('should change subqueries read preference when using matchesKeyInQuery and doesNotMatchKeyInQuery to find through API', async () => {
|
||||
const databaseAdapter = Config.get(Parse.applicationId).database.adapter;
|
||||
|
||||
const obj0 = new Parse.Object('MyObject0');
|
||||
obj0.set('boolKey', false);
|
||||
const obj1 = new Parse.Object('MyObject1');
|
||||
@@ -1175,7 +1117,7 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
obj2.set('myObject1', obj1);
|
||||
|
||||
await Parse.Object.saveAll([obj0, obj1, obj2]);
|
||||
spyOn(databaseAdapter.database.serverConfig, 'cursor').and.callThrough();
|
||||
spyOn(Collection.prototype, 'find').and.callThrough();
|
||||
await waitForReplication();
|
||||
|
||||
const whereString = JSON.stringify({
|
||||
@@ -1215,15 +1157,15 @@ describe_only_db('mongo')('Read preference option', () => {
|
||||
let myObjectReadPreference0 = null;
|
||||
let myObjectReadPreference1 = null;
|
||||
let myObjectReadPreference2 = null;
|
||||
databaseAdapter.database.serverConfig.cursor.calls.all().forEach(call => {
|
||||
if (call.args[0].ns.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[0].options.readPreference.mode;
|
||||
Collection.prototype.find.calls.all().forEach(call => {
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject0') >= 0) {
|
||||
myObjectReadPreference0 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject1') >= 0) {
|
||||
myObjectReadPreference1 = call.args[1].readPreference;
|
||||
}
|
||||
if (call.args[0].ns.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[0].options.readPreference.mode;
|
||||
if (call.object.s.namespace.collection.indexOf('MyObject2') >= 0) {
|
||||
myObjectReadPreference2 = call.args[1].readPreference;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -89,10 +89,54 @@ describe('batch', () => {
|
||||
expect(internalURL).toEqual('/classes/Object');
|
||||
});
|
||||
|
||||
it('should handle a batch request without transaction', async done => {
|
||||
it('should return the proper url with no url provided', () => {
|
||||
const originalURL = '/parse/batch';
|
||||
const internalURL = batch.makeBatchRoutingPathFunction(
|
||||
originalURL,
|
||||
undefined,
|
||||
publicServerURL
|
||||
)('/parse/classes/Object');
|
||||
|
||||
expect(internalURL).toEqual('/classes/Object');
|
||||
});
|
||||
|
||||
it('should return the proper url with no public url provided', () => {
|
||||
const originalURL = '/parse/batch';
|
||||
const internalURL = batch.makeBatchRoutingPathFunction(
|
||||
originalURL,
|
||||
serverURLNaked,
|
||||
undefined
|
||||
)('/parse/classes/Object');
|
||||
|
||||
expect(internalURL).toEqual('/classes/Object');
|
||||
});
|
||||
|
||||
it('should return the proper url with bad url provided', () => {
|
||||
const originalURL = '/parse/batch';
|
||||
const internalURL = batch.makeBatchRoutingPathFunction(
|
||||
originalURL,
|
||||
'badurl.com',
|
||||
publicServerURL
|
||||
)('/parse/classes/Object');
|
||||
|
||||
expect(internalURL).toEqual('/classes/Object');
|
||||
});
|
||||
|
||||
it('should return the proper url with bad public url provided', () => {
|
||||
const originalURL = '/parse/batch';
|
||||
const internalURL = batch.makeBatchRoutingPathFunction(
|
||||
originalURL,
|
||||
serverURLNaked,
|
||||
'badurl.com'
|
||||
)('/parse/classes/Object');
|
||||
|
||||
expect(internalURL).toEqual('/classes/Object');
|
||||
});
|
||||
|
||||
it('should handle a batch request without transaction', async () => {
|
||||
spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
|
||||
request({
|
||||
const response = await request({
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/batch',
|
||||
@@ -110,28 +154,25 @@ describe('batch', () => {
|
||||
},
|
||||
],
|
||||
}),
|
||||
}).then(response => {
|
||||
expect(response.data.length).toEqual(2);
|
||||
expect(response.data[0].success.objectId).toBeDefined();
|
||||
expect(response.data[0].success.createdAt).toBeDefined();
|
||||
expect(response.data[1].success.objectId).toBeDefined();
|
||||
expect(response.data[1].success.createdAt).toBeDefined();
|
||||
const query = new Parse.Query('MyObject');
|
||||
query.find().then(results => {
|
||||
expect(databaseAdapter.createObject.calls.count()).toBe(2);
|
||||
expect(databaseAdapter.createObject.calls.argsFor(0)[3]).toEqual(null);
|
||||
expect(databaseAdapter.createObject.calls.argsFor(1)[3]).toEqual(null);
|
||||
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
expect(response.data.length).toEqual(2);
|
||||
expect(response.data[0].success.objectId).toBeDefined();
|
||||
expect(response.data[0].success.createdAt).toBeDefined();
|
||||
expect(response.data[1].success.objectId).toBeDefined();
|
||||
expect(response.data[1].success.createdAt).toBeDefined();
|
||||
const query = new Parse.Query('MyObject');
|
||||
const results = await query.find();
|
||||
expect(databaseAdapter.createObject.calls.count()).toBe(2);
|
||||
expect(databaseAdapter.createObject.calls.argsFor(0)[3]).toEqual(null);
|
||||
expect(databaseAdapter.createObject.calls.argsFor(1)[3]).toEqual(null);
|
||||
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
|
||||
});
|
||||
|
||||
it('should handle a batch request with transaction = false', async done => {
|
||||
await reconfigureServer();
|
||||
it('should handle a batch request with transaction = false', async () => {
|
||||
spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
|
||||
request({
|
||||
const response = await request({
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/batch',
|
||||
@@ -150,21 +191,18 @@ describe('batch', () => {
|
||||
],
|
||||
transaction: false,
|
||||
}),
|
||||
}).then(response => {
|
||||
expect(response.data.length).toEqual(2);
|
||||
expect(response.data[0].success.objectId).toBeDefined();
|
||||
expect(response.data[0].success.createdAt).toBeDefined();
|
||||
expect(response.data[1].success.objectId).toBeDefined();
|
||||
expect(response.data[1].success.createdAt).toBeDefined();
|
||||
const query = new Parse.Query('MyObject');
|
||||
query.find().then(results => {
|
||||
expect(databaseAdapter.createObject.calls.count()).toBe(2);
|
||||
expect(databaseAdapter.createObject.calls.argsFor(0)[3]).toEqual(null);
|
||||
expect(databaseAdapter.createObject.calls.argsFor(1)[3]).toEqual(null);
|
||||
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
|
||||
done();
|
||||
});
|
||||
});
|
||||
expect(response.data.length).toEqual(2);
|
||||
expect(response.data[0].success.objectId).toBeDefined();
|
||||
expect(response.data[0].success.createdAt).toBeDefined();
|
||||
expect(response.data[1].success.objectId).toBeDefined();
|
||||
expect(response.data[1].success.createdAt).toBeDefined();
|
||||
const query = new Parse.Query('MyObject');
|
||||
const results = await query.find();
|
||||
expect(databaseAdapter.createObject.calls.count()).toBe(2);
|
||||
expect(databaseAdapter.createObject.calls.argsFor(0)[3]).toEqual(null);
|
||||
expect(databaseAdapter.createObject.calls.argsFor(1)[3]).toEqual(null);
|
||||
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
|
||||
});
|
||||
|
||||
if (
|
||||
@@ -191,58 +229,45 @@ describe('batch', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('should handle a batch request with transaction = true', async done => {
|
||||
await reconfigureServer();
|
||||
it('should handle a batch request with transaction = true', async () => {
|
||||
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||
myObject
|
||||
.save()
|
||||
.then(() => {
|
||||
return myObject.destroy();
|
||||
})
|
||||
.then(() => {
|
||||
spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
|
||||
request({
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/batch',
|
||||
body: JSON.stringify({
|
||||
requests: [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/1/classes/MyObject',
|
||||
body: { key: 'value1' },
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/1/classes/MyObject',
|
||||
body: { key: 'value2' },
|
||||
},
|
||||
],
|
||||
transaction: true,
|
||||
}),
|
||||
}).then(response => {
|
||||
expect(response.data.length).toEqual(2);
|
||||
expect(response.data[0].success.objectId).toBeDefined();
|
||||
expect(response.data[0].success.createdAt).toBeDefined();
|
||||
expect(response.data[1].success.objectId).toBeDefined();
|
||||
expect(response.data[1].success.createdAt).toBeDefined();
|
||||
const query = new Parse.Query('MyObject');
|
||||
query.find().then(results => {
|
||||
expect(databaseAdapter.createObject.calls.count() % 2).toBe(0);
|
||||
for (let i = 0; i + 1 < databaseAdapter.createObject.calls.length; i = i + 2) {
|
||||
expect(databaseAdapter.createObject.calls.argsFor(i)[3]).toBe(
|
||||
databaseAdapter.createObject.calls.argsFor(i + 1)[3]
|
||||
);
|
||||
}
|
||||
expect(results.map(result => result.get('key')).sort()).toEqual([
|
||||
'value1',
|
||||
'value2',
|
||||
]);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
await myObject.save();
|
||||
await myObject.destroy();
|
||||
spyOn(databaseAdapter, 'createObject').and.callThrough();
|
||||
const response = await request({
|
||||
method: 'POST',
|
||||
headers: headers,
|
||||
url: 'http://localhost:8378/1/batch',
|
||||
body: JSON.stringify({
|
||||
requests: [
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/1/classes/MyObject',
|
||||
body: { key: 'value1' },
|
||||
},
|
||||
{
|
||||
method: 'POST',
|
||||
path: '/1/classes/MyObject',
|
||||
body: { key: 'value2' },
|
||||
},
|
||||
],
|
||||
transaction: true,
|
||||
}),
|
||||
});
|
||||
expect(response.data.length).toEqual(2);
|
||||
expect(response.data[0].success.objectId).toBeDefined();
|
||||
expect(response.data[0].success.createdAt).toBeDefined();
|
||||
expect(response.data[1].success.objectId).toBeDefined();
|
||||
expect(response.data[1].success.createdAt).toBeDefined();
|
||||
const query = new Parse.Query('MyObject');
|
||||
const results = await query.find();
|
||||
expect(databaseAdapter.createObject.calls.count() % 2).toBe(0);
|
||||
for (let i = 0; i + 1 < databaseAdapter.createObject.calls.length; i = i + 2) {
|
||||
expect(databaseAdapter.createObject.calls.argsFor(i)[3]).toBe(
|
||||
databaseAdapter.createObject.calls.argsFor(i + 1)[3]
|
||||
);
|
||||
}
|
||||
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
|
||||
});
|
||||
|
||||
it('should not save anything when one operation fails in a transaction', async () => {
|
||||
@@ -350,6 +375,7 @@ describe('batch', () => {
|
||||
transaction: true,
|
||||
}),
|
||||
});
|
||||
fail();
|
||||
} catch (error) {
|
||||
expect(error).toBeDefined();
|
||||
const query = new Parse.Query('MyObject');
|
||||
|
||||
@@ -1866,6 +1866,34 @@ describe('schemas', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Nested documents', () => {
|
||||
beforeAll(async () => {
|
||||
const testSchema = new Parse.Schema('test_7371');
|
||||
testSchema.setCLP({
|
||||
create: { ['*']: true },
|
||||
update: { ['*']: true },
|
||||
addField: {},
|
||||
});
|
||||
testSchema.addObject('a');
|
||||
await testSchema.save();
|
||||
});
|
||||
|
||||
it('addField permission not required for adding a nested property', async () => {
|
||||
const obj = new Parse.Object('test_7371');
|
||||
obj.set('a', {});
|
||||
await obj.save();
|
||||
obj.set('a.b', 2);
|
||||
await obj.save();
|
||||
});
|
||||
it('addField permission not required for modifying a nested property', async () => {
|
||||
const obj = new Parse.Object('test_7371');
|
||||
obj.set('a', { b: 1 });
|
||||
await obj.save();
|
||||
obj.set('a.b', 2);
|
||||
await obj.save();
|
||||
});
|
||||
});
|
||||
|
||||
it('should aceept class-level permission with userid of any length', async done => {
|
||||
await global.reconfigureServer({
|
||||
customIdSize: 11,
|
||||
|
||||
Reference in New Issue
Block a user