Files
kami-parse-server/spec/RestQuery.spec.js
Diamond Lewis cc6d474dcb Schema Cache Improvement 2 (#5616)
* schema hasClass improvement

* create object improvement

* destroy object

* update object

* hasClass test rewrite

* more tests

* improve signing up users
2019-05-30 11:14:05 -05:00

423 lines
12 KiB
JavaScript

'use strict';
// These tests check the "find" functionality of the REST API.
const auth = require('../lib/Auth');
const Config = require('../lib/Config');
const rest = require('../lib/rest');
const RestQuery = require('../lib/RestQuery');
const request = require('../lib/request');
const querystring = require('querystring');
let config;
let database;
const nobody = auth.nobody(config);
describe('rest query', () => {
beforeEach(() => {
config = Config.get('test');
database = config.database;
});
it('basic query', done => {
rest
.create(config, nobody, 'TestObject', {})
.then(() => {
return rest.find(config, nobody, 'TestObject', {});
})
.then(response => {
expect(response.results.length).toEqual(1);
done();
});
});
it('query with limit', done => {
rest
.create(config, nobody, 'TestObject', { foo: 'baz' })
.then(() => {
return rest.create(config, nobody, 'TestObject', { foo: 'qux' });
})
.then(() => {
return rest.find(config, nobody, 'TestObject', {}, { limit: 1 });
})
.then(response => {
expect(response.results.length).toEqual(1);
expect(response.results[0].foo).toBeTruthy();
done();
});
});
const data = {
username: 'blah',
password: 'pass',
sessionToken: 'abc123',
};
it_exclude_dbs(['postgres'])(
'query for user w/ legacy credentials without masterKey has them stripped from results',
done => {
database
.create('_User', data)
.then(() => {
return rest.find(config, nobody, '_User');
})
.then(result => {
const user = result.results[0];
expect(user.username).toEqual('blah');
expect(user.sessionToken).toBeUndefined();
expect(user.password).toBeUndefined();
done();
});
}
);
it_exclude_dbs(['postgres'])(
'query for user w/ legacy credentials with masterKey has them stripped from results',
done => {
database
.create('_User', data)
.then(() => {
return rest.find(config, { isMaster: true }, '_User');
})
.then(result => {
const user = result.results[0];
expect(user.username).toEqual('blah');
expect(user.sessionToken).toBeUndefined();
expect(user.password).toBeUndefined();
done();
});
}
);
// Created to test a scenario in AnyPic
it_exclude_dbs(['postgres'])('query with include', done => {
let photo = {
foo: 'bar',
};
let user = {
username: 'aUsername',
password: 'aPassword',
};
const activity = {
type: 'comment',
photo: {
__type: 'Pointer',
className: 'TestPhoto',
objectId: '',
},
fromUser: {
__type: 'Pointer',
className: '_User',
objectId: '',
},
};
const queryWhere = {
photo: {
__type: 'Pointer',
className: 'TestPhoto',
objectId: '',
},
type: 'comment',
};
const queryOptions = {
include: 'fromUser',
order: 'createdAt',
limit: 30,
};
rest
.create(config, nobody, 'TestPhoto', photo)
.then(p => {
photo = p;
return rest.create(config, nobody, '_User', user);
})
.then(u => {
user = u.response;
activity.photo.objectId = photo.objectId;
activity.fromUser.objectId = user.objectId;
return rest.create(config, nobody, 'TestActivity', activity);
})
.then(() => {
queryWhere.photo.objectId = photo.objectId;
return rest.find(
config,
nobody,
'TestActivity',
queryWhere,
queryOptions
);
})
.then(response => {
const results = response.results;
expect(results.length).toEqual(1);
expect(typeof results[0].objectId).toEqual('string');
expect(typeof results[0].photo).toEqual('object');
expect(typeof results[0].fromUser).toEqual('object');
expect(typeof results[0].fromUser.username).toEqual('string');
done();
})
.catch(error => {
console.log(error);
});
});
it('query non-existent class when disabled client class creation', done => {
const customConfig = Object.assign({}, config, {
allowClientClassCreation: false,
});
rest
.find(customConfig, auth.nobody(customConfig), 'ClientClassCreation', {})
.then(
() => {
fail('Should throw an error');
done();
},
err => {
expect(err.code).toEqual(Parse.Error.OPERATION_FORBIDDEN);
expect(err.message).toEqual(
'This user is not allowed to access ' +
'non-existent class: ClientClassCreation'
);
done();
}
);
});
it('query existent class when disabled client class creation', async () => {
const customConfig = Object.assign({}, config, {
allowClientClassCreation: false,
});
const schema = await config.database.loadSchema();
const actualSchema = await schema.addClassIfNotExists(
'ClientClassCreation',
{}
);
expect(actualSchema.className).toEqual('ClientClassCreation');
await schema.reloadData({ clearCache: true });
// Should not throw
const result = await rest.find(
customConfig,
auth.nobody(customConfig),
'ClientClassCreation',
{}
);
expect(result.results.length).toEqual(0);
});
it('query with wrongly encoded parameter', done => {
rest
.create(config, nobody, 'TestParameterEncode', { foo: 'bar' })
.then(() => {
return rest.create(config, nobody, 'TestParameterEncode', {
foo: 'baz',
});
})
.then(() => {
const headers = {
'X-Parse-Application-Id': 'test',
'X-Parse-REST-API-Key': 'rest',
};
const p0 = request({
headers: headers,
url:
'http://localhost:8378/1/classes/TestParameterEncode?' +
querystring
.stringify({
where: '{"foo":{"$ne": "baz"}}',
limit: 1,
})
.replace('=', '%3D'),
}).then(fail, response => {
const error = response.data;
expect(error.code).toEqual(Parse.Error.INVALID_QUERY);
});
const p1 = request({
headers: headers,
url:
'http://localhost:8378/1/classes/TestParameterEncode?' +
querystring
.stringify({
limit: 1,
})
.replace('=', '%3D'),
}).then(fail, response => {
const error = response.data;
expect(error.code).toEqual(Parse.Error.INVALID_QUERY);
});
return Promise.all([p0, p1]);
})
.then(done)
.catch(err => {
jfail(err);
fail('should not fail');
done();
});
});
it('query with limit = 0', done => {
rest
.create(config, nobody, 'TestObject', { foo: 'baz' })
.then(() => {
return rest.create(config, nobody, 'TestObject', { foo: 'qux' });
})
.then(() => {
return rest.find(config, nobody, 'TestObject', {}, { limit: 0 });
})
.then(response => {
expect(response.results.length).toEqual(0);
done();
});
});
it('query with limit = 0 and count = 1', done => {
rest
.create(config, nobody, 'TestObject', { foo: 'baz' })
.then(() => {
return rest.create(config, nobody, 'TestObject', { foo: 'qux' });
})
.then(() => {
return rest.find(
config,
nobody,
'TestObject',
{},
{ limit: 0, count: 1 }
);
})
.then(response => {
expect(response.results.length).toEqual(0);
expect(response.count).toEqual(2);
done();
});
});
it('makes sure null pointers are handed correctly #2189', done => {
const object = new Parse.Object('AnObject');
const anotherObject = new Parse.Object('AnotherObject');
anotherObject
.save()
.then(() => {
object.set('values', [null, null, anotherObject]);
return object.save();
})
.then(() => {
const query = new Parse.Query('AnObject');
query.include('values');
return query.first();
})
.then(
result => {
const values = result.get('values');
expect(values.length).toBe(3);
let anotherObjectFound = false;
let nullCounts = 0;
for (const value of values) {
if (value === null) {
nullCounts++;
} else if (value instanceof Parse.Object) {
anotherObjectFound = true;
}
}
expect(nullCounts).toBe(2);
expect(anotherObjectFound).toBeTruthy();
done();
},
err => {
console.error(err);
fail(err);
done();
}
);
});
});
describe('RestQuery.each', () => {
it('should run each', async () => {
const objects = [];
while (objects.length != 10) {
objects.push(new Parse.Object('Object', { value: objects.length }));
}
const config = Config.get('test');
await Parse.Object.saveAll(objects);
const query = new RestQuery(
config,
auth.master(config),
'Object',
{ value: { $gt: 2 } },
{ limit: 2 }
);
const spy = spyOn(query, 'execute').and.callThrough();
const classSpy = spyOn(RestQuery.prototype, 'execute').and.callThrough();
const results = [];
await query.each(result => {
expect(result.value).toBeGreaterThan(2);
results.push(result);
});
expect(spy.calls.count()).toBe(0);
expect(classSpy.calls.count()).toBe(4);
expect(results.length).toBe(7);
});
it('should work with query on relations', async () => {
const objectA = new Parse.Object('Letter', { value: 'A' });
const objectB = new Parse.Object('Letter', { value: 'B' });
const object1 = new Parse.Object('Number', { value: '1' });
const object2 = new Parse.Object('Number', { value: '2' });
const object3 = new Parse.Object('Number', { value: '3' });
const object4 = new Parse.Object('Number', { value: '4' });
await Parse.Object.saveAll([object1, object2, object3, object4]);
objectA.relation('numbers').add(object1);
objectB.relation('numbers').add(object2);
await Parse.Object.saveAll([objectA, objectB]);
const config = Config.get('test');
/**
* Two queries needed since objectId are sorted and we can't know which one
* going to be the first and then skip by the $gt added by each
*/
const queryOne = new RestQuery(
config,
auth.master(config),
'Letter',
{
numbers: {
__type: 'Pointer',
className: 'Number',
objectId: object1.id,
},
},
{ limit: 1 }
);
const queryTwo = new RestQuery(
config,
auth.master(config),
'Letter',
{
numbers: {
__type: 'Pointer',
className: 'Number',
objectId: object2.id,
},
},
{ limit: 1 }
);
const classSpy = spyOn(RestQuery.prototype, 'execute').and.callThrough();
const resultsOne = [];
const resultsTwo = [];
await queryOne.each(result => {
resultsOne.push(result);
});
await queryTwo.each(result => {
resultsTwo.push(result);
});
expect(classSpy.calls.count()).toBe(4);
expect(resultsOne.length).toBe(1);
expect(resultsTwo.length).toBe(1);
});
});