[WIP] Enable test suite to be randomized (#7265)

* initial run

* Update ParseGraphQLServer.spec.js

* temporarily enable reporter

* Bump retry limit

* fix undefined database

* try to catch error

* Handle LiveQueryServers

* Update Config.js

* fast-fail false

* Remove usage of AppCache

* oops

* Update contributing guide

* enable debugger, try network retry attempt 1

* Fix ldap unbinding

* move non specs to support

* add missing mock adapter

* fix Parse.Push

* RestController should match batch.spec.js

* Remove request attempt limit

* handle index.spec.js

* Update CHANGELOG.md

* Handle error: tuple concurrently updated

* test transactions

* Clear RedisCache after every test

* LoggerController.spec.js

* Update schemas.spec.js

* finally fix transactions

* fix geopoint deadlock

* transaction with clean database

* batch.spec.js
This commit is contained in:
Diamond Lewis
2021-03-15 02:04:09 -05:00
committed by GitHub
parent 9563793303
commit 1666c3e382
36 changed files with 688 additions and 700 deletions

View File

@@ -6,7 +6,7 @@ const Config = require('../lib/Config');
describe('AdapterLoader', () => {
it('should instantiate an adapter from string in object', done => {
const adapterPath = require('path').resolve('./spec/MockAdapter');
const adapterPath = require('path').resolve('./spec/support/MockAdapter');
const adapter = loadAdapter({
adapter: adapterPath,
@@ -23,7 +23,7 @@ describe('AdapterLoader', () => {
});
it('should instantiate an adapter from string', done => {
const adapterPath = require('path').resolve('./spec/MockAdapter');
const adapterPath = require('path').resolve('./spec/support/MockAdapter');
const adapter = loadAdapter(adapterPath);
expect(adapter instanceof Object).toBe(true);
@@ -119,7 +119,7 @@ describe('AdapterLoader', () => {
});
it('should load custom push adapter from string (#3544)', done => {
const adapterPath = require('path').resolve('./spec/MockPushAdapter');
const adapterPath = require('path').resolve('./spec/support/MockPushAdapter');
const options = {
ios: {
bundleId: 'bundle.id',

View File

@@ -227,6 +227,8 @@ describe('execution', () => {
'test',
'--databaseURI',
'mongodb://localhost/test',
'--port',
'1339',
]);
childProcess.stdout.on('data', data => {
data = data.toString();
@@ -247,6 +249,8 @@ describe('execution', () => {
'test',
'--databaseURI',
'mongodb://localhost/test',
'--port',
'1340',
'--mountGraphQL',
]);
let output = '';
@@ -271,6 +275,8 @@ describe('execution', () => {
'test',
'--databaseURI',
'mongodb://localhost/test',
'--port',
'1341',
'--mountGraphQL',
'--mountPlayground',
]);

View File

@@ -80,6 +80,9 @@ describe('FilesController', () => {
expect(typeof error).toBe('object');
expect(error.message.indexOf('biscuit')).toBe(13);
expect(error.code).toBe(Parse.Error.INVALID_FILE_NAME);
mockAdapter.validateFilename = () => {
return null;
};
done();
});

View File

@@ -4,7 +4,6 @@ const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter')
const { randomString } = require('../lib/cryptoUtils');
const databaseURI = 'mongodb://localhost:27017/parse';
const request = require('../lib/request');
const Config = require('../lib/Config');
async function expectMissingFile(gfsAdapter, name) {
try {
@@ -395,8 +394,9 @@ describe_only_db('mongo')('GridFSBucket and GridStore interop', () => {
});
it('should handle getMetadata error', async () => {
const config = Config.get('test');
config.filesController.getMetadata = () => Promise.reject();
const gfsAdapter = new GridFSBucketAdapter(databaseURI);
await reconfigureServer({ filesAdapter: gfsAdapter });
gfsAdapter.getMetadata = () => Promise.reject();
const headers = {
'X-Parse-Application-Id': 'test',

View File

@@ -1,5 +1,5 @@
const ldap = require('../lib/Adapters/Auth/ldap');
const mockLdapServer = require('./MockLdapServer');
const mockLdapServer = require('./support/MockLdapServer');
const fs = require('fs');
const port = 12345;
const sslport = 12346;
@@ -19,243 +19,194 @@ describe('Ldap Auth', () => {
ldap.validateAppId().then(done).catch(done.fail);
});
it('Should succeed with right credentials', done => {
mockLdapServer(port, 'uid=testuser, o=example').then(server => {
const options = {
suffix: 'o=example',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
};
ldap
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
.then(done)
.catch(done.fail)
.finally(() => server.close());
});
it('Should succeed with right credentials', async done => {
const server = await mockLdapServer(port, 'uid=testuser, o=example');
const options = {
suffix: 'o=example',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
};
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
server.close(done);
});
it('Should succeed with right credentials when LDAPS is used and certifcate is not checked', done => {
mockLdapServer(sslport, 'uid=testuser, o=example', false, true).then(server => {
const options = {
suffix: 'o=example',
url: `ldaps://localhost:${sslport}`,
dn: 'uid={{id}}, o=example',
tlsOptions: { rejectUnauthorized: false },
};
ldap
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
.then(done)
.catch(done.fail)
.finally(() => server.close());
});
it('Should succeed with right credentials when LDAPS is used and certifcate is not checked', async done => {
const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true);
const options = {
suffix: 'o=example',
url: `ldaps://localhost:${sslport}`,
dn: 'uid={{id}}, o=example',
tlsOptions: { rejectUnauthorized: false },
};
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
server.close(done);
});
it('Should succeed when LDAPS is used and the presented certificate is the expected certificate', done => {
mockLdapServer(sslport, 'uid=testuser, o=example', false, true).then(server => {
const options = {
suffix: 'o=example',
url: `ldaps://localhost:${sslport}`,
dn: 'uid={{id}}, o=example',
tlsOptions: {
ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'),
rejectUnauthorized: true,
},
};
ldap
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
.then(done)
.catch(done.fail)
.finally(() => server.close());
});
it('Should succeed when LDAPS is used and the presented certificate is the expected certificate', async done => {
const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true);
const options = {
suffix: 'o=example',
url: `ldaps://localhost:${sslport}`,
dn: 'uid={{id}}, o=example',
tlsOptions: {
ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'),
rejectUnauthorized: true,
},
};
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
server.close(done);
});
it('Should fail when LDAPS is used and the presented certificate is not the expected certificate', done => {
mockLdapServer(sslport, 'uid=testuser, o=example', false, true).then(server => {
const options = {
suffix: 'o=example',
url: `ldaps://localhost:${sslport}`,
dn: 'uid={{id}}, o=example',
tlsOptions: {
ca: fs.readFileSync(__dirname + '/support/cert/anothercert.pem'),
rejectUnauthorized: true,
},
};
ldap
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
.then(done.fail)
.catch(err => {
jequal(err.message, 'LDAPS: Certificate mismatch');
done();
})
.finally(() => server.close());
});
it('Should fail when LDAPS is used and the presented certificate is not the expected certificate', async done => {
const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true);
const options = {
suffix: 'o=example',
url: `ldaps://localhost:${sslport}`,
dn: 'uid={{id}}, o=example',
tlsOptions: {
ca: fs.readFileSync(__dirname + '/support/cert/anothercert.pem'),
rejectUnauthorized: true,
},
};
try {
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
fail();
} catch (err) {
expect(err.message).toBe('LDAPS: Certificate mismatch');
}
server.close(done);
});
it('Should fail when LDAPS is used certifcate matches but credentials are wrong', done => {
mockLdapServer(sslport, 'uid=testuser, o=example', false, true).then(server => {
const options = {
suffix: 'o=example',
url: `ldaps://localhost:${sslport}`,
dn: 'uid={{id}}, o=example',
tlsOptions: {
ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'),
rejectUnauthorized: true,
},
};
ldap
.validateAuthData({ id: 'testuser', password: 'wrong!' }, options)
.then(done.fail)
.catch(err => {
jequal(err.message, 'LDAP: Wrong username or password');
done();
})
.finally(() => server.close());
});
it('Should fail when LDAPS is used certifcate matches but credentials are wrong', async done => {
const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true);
const options = {
suffix: 'o=example',
url: `ldaps://localhost:${sslport}`,
dn: 'uid={{id}}, o=example',
tlsOptions: {
ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'),
rejectUnauthorized: true,
},
};
try {
await ldap.validateAuthData({ id: 'testuser', password: 'wrong!' }, options);
fail();
} catch (err) {
expect(err.message).toBe('LDAP: Wrong username or password');
}
server.close(done);
});
it('Should fail with wrong credentials', done => {
mockLdapServer(port, 'uid=testuser, o=example').then(server => {
const options = {
suffix: 'o=example',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
};
ldap
.validateAuthData({ id: 'testuser', password: 'wrong!' }, options)
.then(done.fail)
.catch(err => {
jequal(err.message, 'LDAP: Wrong username or password');
done();
})
.finally(() => server.close());
});
it('Should fail with wrong credentials', async done => {
const server = await mockLdapServer(port, 'uid=testuser, o=example');
const options = {
suffix: 'o=example',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
};
try {
await ldap.validateAuthData({ id: 'testuser', password: 'wrong!' }, options);
fail();
} catch (err) {
expect(err.message).toBe('LDAP: Wrong username or password');
}
server.close(done);
});
it('Should succeed if user is in given group', done => {
mockLdapServer(port, 'uid=testuser, o=example').then(server => {
const options = {
suffix: 'o=example',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
groupCn: 'powerusers',
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
};
ldap
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
.then(done)
.catch(done.fail)
.finally(() => server.close());
});
it('Should succeed if user is in given group', async done => {
const server = await mockLdapServer(port, 'uid=testuser, o=example');
const options = {
suffix: 'o=example',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
groupCn: 'powerusers',
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
};
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
server.close(done);
});
it('Should fail if user is not in given group', done => {
mockLdapServer(port, 'uid=testuser, o=example').then(server => {
const options = {
suffix: 'o=example',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
groupCn: 'groupTheUserIsNotIn',
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
};
ldap
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
.then(done.fail)
.catch(err => {
jequal(err.message, 'LDAP: User not in group');
done();
})
.finally(() => server.close());
});
it('Should fail if user is not in given group', async done => {
const server = await mockLdapServer(port, 'uid=testuser, o=example');
const options = {
suffix: 'o=example',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
groupCn: 'groupTheUserIsNotIn',
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
};
try {
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
fail();
} catch (err) {
expect(err.message).toBe('LDAP: User not in group');
}
server.close(done);
});
it('Should fail if the LDAP server does not allow searching inside the provided suffix', done => {
mockLdapServer(port, 'uid=testuser, o=example').then(server => {
const options = {
suffix: 'o=invalid',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
groupCn: 'powerusers',
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
};
ldap
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
.then(done.fail)
.catch(err => {
jequal(err.message, 'LDAP group search failed');
done();
})
.finally(() => server.close());
});
it('Should fail if the LDAP server does not allow searching inside the provided suffix', async done => {
const server = await mockLdapServer(port, 'uid=testuser, o=example');
const options = {
suffix: 'o=invalid',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
groupCn: 'powerusers',
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
};
try {
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
fail();
} catch (err) {
expect(err.message).toBe('LDAP group search failed');
}
server.close(done);
});
it('Should fail if the LDAP server encounters an error while searching', done => {
mockLdapServer(port, 'uid=testuser, o=example', true).then(server => {
const options = {
suffix: 'o=example',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
groupCn: 'powerusers',
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
};
ldap
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
.then(done.fail)
.catch(err => {
jequal(err.message, 'LDAP group search failed');
done();
})
.finally(() => server.close());
});
it('Should fail if the LDAP server encounters an error while searching', async done => {
const server = await mockLdapServer(port, 'uid=testuser, o=example', true);
const options = {
suffix: 'o=example',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
groupCn: 'powerusers',
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
};
try {
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
fail();
} catch (err) {
expect(err.message).toBe('LDAP group search failed');
}
server.close(done);
});
it('Should delete the password from authData after validation', done => {
mockLdapServer(port, 'uid=testuser, o=example', true).then(server => {
const options = {
suffix: 'o=example',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
};
const authData = { id: 'testuser', password: 'secret' };
ldap
.validateAuthData(authData, options)
.then(() => {
expect(authData).toEqual({ id: 'testuser' });
done();
})
.catch(done.fail)
.finally(() => server.close());
});
it('Should delete the password from authData after validation', async done => {
const server = await mockLdapServer(port, 'uid=testuser, o=example', true);
const options = {
suffix: 'o=example',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
};
const authData = { id: 'testuser', password: 'secret' };
await ldap.validateAuthData(authData, options);
expect(authData).toEqual({ id: 'testuser' });
server.close(done);
});
it('Should not save the password in the user record after authentication', done => {
mockLdapServer(port, 'uid=testuser, o=example', true).then(server => {
const options = {
suffix: 'o=example',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
};
reconfigureServer({ auth: { ldap: options } }).then(() => {
const authData = { authData: { id: 'testuser', password: 'secret' } };
Parse.User.logInWith('ldap', authData).then(returnedUser => {
const query = new Parse.Query('User');
query
.equalTo('objectId', returnedUser.id)
.first({ useMasterKey: true })
.then(user => {
expect(user.get('authData')).toEqual({ ldap: { id: 'testuser' } });
expect(user.get('authData').ldap.password).toBeUndefined();
done();
})
.catch(done.fail)
.finally(() => server.close());
});
});
});
it('Should not save the password in the user record after authentication', async done => {
const server = await mockLdapServer(port, 'uid=testuser, o=example', true);
const options = {
suffix: 'o=example',
url: `ldap://localhost:${port}`,
dn: 'uid={{id}}, o=example',
};
await reconfigureServer({ auth: { ldap: options } });
const authData = { authData: { id: 'testuser', password: 'secret' } };
const returnedUser = await Parse.User.logInWith('ldap', authData);
const query = new Parse.Query('User');
const user = await query.equalTo('objectId', returnedUser.id).first({ useMasterKey: true });
expect(user.get('authData')).toEqual({ ldap: { id: 'testuser' } });
expect(user.get('authData').ldap.password).toBeUndefined();
server.close(done);
});
});

View File

@@ -70,6 +70,7 @@ describe('LoggerController', () => {
};
const loggerController = new LoggerController(new WinstonLoggerAdapter());
loggerController.error('can process an ascending query without throwing');
expect(() => {
loggerController
@@ -115,6 +116,7 @@ describe('LoggerController', () => {
};
const loggerController = new LoggerController(new WinstonLoggerAdapter());
loggerController.error('can process a descending query without throwing');
expect(() => {
loggerController

View File

@@ -2,10 +2,15 @@
const request = require('../lib/request');
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const pushCompleted = async pushId => {
let result = await Parse.Push.getPushStatus(pushId);
const query = new Parse.Query('_PushStatus');
query.equalTo('objectId', pushId);
let result = await query.first({ useMasterKey: true });
while (!(result && result.get('status') === 'succeeded')) {
result = await Parse.Push.getPushStatus(pushId);
await sleep(100);
result = await query.first({ useMasterKey: true });
}
};

View File

@@ -5,7 +5,7 @@ const fetch = require('node-fetch');
const FormData = require('form-data');
const ws = require('ws');
require('./helper');
const { updateCLP } = require('./dev');
const { updateCLP } = require('./support/dev');
const pluralize = require('pluralize');
const { getMainDefinition } = require('apollo-utilities');
@@ -9033,7 +9033,7 @@ describe('ParseGraphQLServer', () => {
it('should support object values', async () => {
try {
const someFieldValue = {
const someObjectFieldValue = {
foo: { bar: 'baz' },
number: 10,
};
@@ -9048,7 +9048,7 @@ describe('ParseGraphQLServer', () => {
`,
variables: {
schemaFields: {
addObjects: [{ name: 'someField' }],
addObjects: [{ name: 'someObjectField' }],
},
},
context: {
@@ -9057,11 +9057,10 @@ describe('ParseGraphQLServer', () => {
},
},
});
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
const schema = await new Parse.Schema('SomeClass').get();
expect(schema.fields.someField.type).toEqual('Object');
expect(schema.fields.someObjectField.type).toEqual('Object');
const createResult = await apolloClient.mutate({
mutation: gql`
@@ -9075,13 +9074,13 @@ describe('ParseGraphQLServer', () => {
`,
variables: {
fields: {
someField: someFieldValue,
someObjectField: someObjectFieldValue,
},
},
});
const where = {
someField: {
someObjectField: {
equalTo: { key: 'foo.bar', value: 'baz' },
notEqualTo: { key: 'foo.bar', value: 'bat' },
greaterThan: { key: 'number', value: 9 },
@@ -9093,13 +9092,13 @@ describe('ParseGraphQLServer', () => {
query GetSomeObject($id: ID!, $where: SomeClassWhereInput) {
someClass(id: $id) {
id
someField
someObjectField
}
someClasses(where: $where) {
edges {
node {
id
someField
someObjectField
}
}
}
@@ -9113,13 +9112,13 @@ describe('ParseGraphQLServer', () => {
const { someClass: getResult, someClasses } = queryResult.data;
const { someField } = getResult;
expect(typeof someField).toEqual('object');
expect(someField).toEqual(someFieldValue);
const { someObjectField } = getResult;
expect(typeof someObjectField).toEqual('object');
expect(someObjectField).toEqual(someObjectFieldValue);
// Checks class query results
expect(someClasses.edges.length).toEqual(1);
expect(someClasses.edges[0].node.someField).toEqual(someFieldValue);
expect(someClasses.edges[0].node.someObjectField).toEqual(someObjectFieldValue);
} catch (e) {
handleError(e);
}
@@ -9127,11 +9126,11 @@ describe('ParseGraphQLServer', () => {
it('should support object composed queries', async () => {
try {
const someFieldValue = {
const someObjectFieldValue1 = {
lorem: 'ipsum',
number: 10,
};
const someFieldValue2 = {
const someObjectFieldValue2 = {
foo: {
test: 'bar',
},
@@ -9144,7 +9143,7 @@ describe('ParseGraphQLServer', () => {
createClass(
input: {
name: "SomeClass"
schemaFields: { addObjects: [{ name: "someField" }] }
schemaFields: { addObjects: [{ name: "someObjectField" }] }
}
) {
clientMutationId
@@ -9180,10 +9179,10 @@ describe('ParseGraphQLServer', () => {
`,
variables: {
fields1: {
someField: someFieldValue,
someObjectField: someObjectFieldValue1,
},
fields2: {
someField: someFieldValue2,
someObjectField: someObjectFieldValue2,
},
},
});
@@ -9191,24 +9190,24 @@ describe('ParseGraphQLServer', () => {
const where = {
AND: [
{
someField: {
someObjectField: {
greaterThan: { key: 'number', value: 9 },
},
},
{
someField: {
someObjectField: {
lessThan: { key: 'number', value: 11 },
},
},
{
OR: [
{
someField: {
someObjectField: {
equalTo: { key: 'lorem', value: 'ipsum' },
},
},
{
someField: {
someObjectField: {
equalTo: { key: 'foo.test', value: 'bar' },
},
},
@@ -9223,7 +9222,7 @@ describe('ParseGraphQLServer', () => {
edges {
node {
id
someField
someObjectField
}
}
}
@@ -9241,11 +9240,11 @@ describe('ParseGraphQLServer', () => {
const { edges } = someClasses;
expect(edges.length).toEqual(2);
expect(
edges.find(result => result.node.id === create1.someClass.id).node.someField
).toEqual(someFieldValue);
edges.find(result => result.node.id === create1.someClass.id).node.someObjectField
).toEqual(someObjectFieldValue1);
expect(
edges.find(result => result.node.id === create2.someClass.id).node.someField
).toEqual(someFieldValue2);
edges.find(result => result.node.id === create2.someClass.id).node.someObjectField
).toEqual(someObjectFieldValue2);
} catch (e) {
handleError(e);
}
@@ -9253,7 +9252,7 @@ describe('ParseGraphQLServer', () => {
it('should support array values', async () => {
try {
const someFieldValue = [1, 'foo', ['bar'], { lorem: 'ipsum' }, true];
const someArrayFieldValue = [1, 'foo', ['bar'], { lorem: 'ipsum' }, true];
await apolloClient.mutate({
mutation: gql`
@@ -9265,7 +9264,7 @@ describe('ParseGraphQLServer', () => {
`,
variables: {
schemaFields: {
addArrays: [{ name: 'someField' }],
addArrays: [{ name: 'someArrayField' }],
},
},
context: {
@@ -9278,7 +9277,7 @@ describe('ParseGraphQLServer', () => {
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
const schema = await new Parse.Schema('SomeClass').get();
expect(schema.fields.someField.type).toEqual('Array');
expect(schema.fields.someArrayField.type).toEqual('Array');
const createResult = await apolloClient.mutate({
mutation: gql`
@@ -9292,7 +9291,7 @@ describe('ParseGraphQLServer', () => {
`,
variables: {
fields: {
someField: someFieldValue,
someArrayField: someArrayFieldValue,
},
},
});
@@ -9301,17 +9300,17 @@ describe('ParseGraphQLServer', () => {
query: gql`
query GetSomeObject($id: ID!) {
someClass(id: $id) {
someField {
someArrayField {
... on Element {
value
}
}
}
someClasses(where: { someField: { exists: true } }) {
someClasses(where: { someArrayField: { exists: true } }) {
edges {
node {
id
someField {
someArrayField {
... on Element {
value
}
@@ -9326,9 +9325,9 @@ describe('ParseGraphQLServer', () => {
},
});
const { someField } = getResult.data.someClass;
expect(Array.isArray(someField)).toBeTruthy();
expect(someField.map(element => element.value)).toEqual(someFieldValue);
const { someArrayField } = getResult.data.someClass;
expect(Array.isArray(someArrayField)).toBeTruthy();
expect(someArrayField.map(element => element.value)).toEqual(someArrayFieldValue);
expect(getResult.data.someClasses.edges.length).toEqual(1);
} catch (e) {
handleError(e);
@@ -10201,101 +10200,99 @@ describe('ParseGraphQLServer', () => {
let apolloClient;
beforeEach(async () => {
if (!httpServer) {
const expressApp = express();
httpServer = http.createServer(expressApp);
const TypeEnum = new GraphQLEnumType({
name: 'TypeEnum',
values: {
human: { value: 'human' },
robot: { value: 'robot' },
},
});
const SomeClassType = new GraphQLObjectType({
name: 'SomeClass',
fields: {
nameUpperCase: {
type: new GraphQLNonNull(GraphQLString),
resolve: p => p.name.toUpperCase(),
},
type: { type: TypeEnum },
language: {
type: new GraphQLEnumType({
name: 'LanguageEnum',
values: {
fr: { value: 'fr' },
en: { value: 'en' },
},
}),
resolve: () => 'fr',
},
const expressApp = express();
httpServer = http.createServer(expressApp);
const TypeEnum = new GraphQLEnumType({
name: 'TypeEnum',
values: {
human: { value: 'human' },
robot: { value: 'robot' },
},
});
const SomeClassType = new GraphQLObjectType({
name: 'SomeClass',
fields: {
nameUpperCase: {
type: new GraphQLNonNull(GraphQLString),
resolve: p => p.name.toUpperCase(),
},
}),
parseGraphQLServer = new ParseGraphQLServer(parseServer, {
graphQLPath: '/graphql',
graphQLCustomTypeDefs: new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
customQuery: {
type: new GraphQLNonNull(GraphQLString),
args: {
message: { type: new GraphQLNonNull(GraphQLString) },
},
resolve: (p, { message }) => message,
},
customQueryWithAutoTypeReturn: {
type: SomeClassType,
args: {
id: { type: new GraphQLNonNull(GraphQLString) },
},
resolve: async (p, { id }) => {
const obj = new Parse.Object('SomeClass');
obj.id = id;
await obj.fetch();
return obj.toJSON();
},
},
type: { type: TypeEnum },
language: {
type: new GraphQLEnumType({
name: 'LanguageEnum',
values: {
fr: { value: 'fr' },
en: { value: 'en' },
},
}),
types: [
new GraphQLInputObjectType({
name: 'CreateSomeClassFieldsInput',
fields: {
type: { type: TypeEnum },
},
}),
new GraphQLInputObjectType({
name: 'UpdateSomeClassFieldsInput',
fields: {
type: { type: TypeEnum },
},
}),
SomeClassType,
],
}),
});
parseGraphQLServer.applyGraphQL(expressApp);
await new Promise(resolve => httpServer.listen({ port: 13377 }, resolve));
const httpLink = createUploadLink({
uri: 'http://localhost:13377/graphql',
fetch,
headers,
});
apolloClient = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
defaultOptions: {
query: {
fetchPolicy: 'no-cache',
resolve: () => 'fr',
},
},
}),
parseGraphQLServer = new ParseGraphQLServer(parseServer, {
graphQLPath: '/graphql',
graphQLCustomTypeDefs: new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: {
customQuery: {
type: new GraphQLNonNull(GraphQLString),
args: {
message: { type: new GraphQLNonNull(GraphQLString) },
},
resolve: (p, { message }) => message,
},
customQueryWithAutoTypeReturn: {
type: SomeClassType,
args: {
id: { type: new GraphQLNonNull(GraphQLString) },
},
resolve: async (p, { id }) => {
const obj = new Parse.Object('SomeClass');
obj.id = id;
await obj.fetch();
return obj.toJSON();
},
},
},
}),
types: [
new GraphQLInputObjectType({
name: 'CreateSomeClassFieldsInput',
fields: {
type: { type: TypeEnum },
},
}),
new GraphQLInputObjectType({
name: 'UpdateSomeClassFieldsInput',
fields: {
type: { type: TypeEnum },
},
}),
SomeClassType,
],
}),
});
}
parseGraphQLServer.applyGraphQL(expressApp);
await new Promise(resolve => httpServer.listen({ port: 13377 }, resolve));
const httpLink = createUploadLink({
uri: 'http://localhost:13377/graphql',
fetch,
headers,
});
apolloClient = new ApolloClient({
link: httpLink,
cache: new InMemoryCache(),
defaultOptions: {
query: {
fetchPolicy: 'no-cache',
},
},
});
});
afterAll(async () => {
afterEach(async () => {
await httpServer.close();
});

View File

@@ -8,9 +8,8 @@ const bodyParser = require('body-parser');
const auth = require('../lib/Auth');
const Config = require('../lib/Config');
const port = 12345;
const port = 34567;
const hookServerURL = 'http://localhost:' + port;
const AppCache = require('../lib/cache').AppCache;
describe('Hooks', () => {
let server;
@@ -19,7 +18,7 @@ describe('Hooks', () => {
if (!app) {
app = express();
app.use(bodyParser.json({ type: '*/*' }));
server = app.listen(12345, undefined, done);
server = app.listen(port, undefined, done);
} else {
done();
}
@@ -383,7 +382,7 @@ describe('Hooks', () => {
}
const hooksController = new HooksController(
Parse.applicationId,
AppCache.get('test').databaseController
Config.get('test').database
);
return hooksController.load();
},

View File

@@ -127,7 +127,10 @@ describe('ParseLiveQueryServer', function () {
serverStartComplete: () => {
expect(parseServer.liveQueryServer).not.toBeUndefined();
expect(parseServer.liveQueryServer.server).toBe(parseServer.server);
parseServer.server.close(done);
parseServer.server.close(async () => {
await reconfigureServer();
done();
});
},
});
});
@@ -149,7 +152,10 @@ describe('ParseLiveQueryServer', function () {
expect(parseServer.liveQueryServer).not.toBeUndefined();
expect(parseServer.liveQueryServer.server).not.toBe(parseServer.server);
parseServer.liveQueryServer.server.close(
parseServer.server.close.bind(parseServer.server, done)
parseServer.server.close.bind(parseServer.server, async () => {
await reconfigureServer();
done();
})
);
},
});

View File

@@ -169,25 +169,25 @@ describe('ParseServerRESTController', () => {
process.env.PARSE_SERVER_TEST_DB === 'postgres'
) {
describe('transactions', () => {
let parseServer;
beforeEach(async () => {
await TestUtils.destroyAllDataPermanently(true);
if (
semver.satisfies(process.env.MONGODB_VERSION, '>=4.0.4') &&
process.env.MONGODB_TOPOLOGY === 'replicaset' &&
process.env.MONGODB_STORAGE_ENGINE === 'wiredTiger'
) {
if (!parseServer) {
parseServer = await reconfigureServer({
databaseAdapter: undefined,
databaseURI:
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset',
});
}
await TestUtils.destroyAllDataPermanently(true);
await reconfigureServer({
databaseAdapter: undefined,
databaseURI:
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset',
});
} else {
await reconfigureServer();
}
});
it('should handle a batch request with transaction = true', done => {
it('should handle a batch request with transaction = true', async done => {
await reconfigureServer();
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
myObject
.save()
@@ -236,117 +236,113 @@ describe('ParseServerRESTController', () => {
.catch(done.fail);
});
it('should not save anything when one operation fails in a transaction', done => {
it('should not save anything when one operation fails in a transaction', 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(() => {
RESTController.request('POST', 'batch', {
requests: [
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
],
transaction: true,
}).catch(error => {
expect(error).toBeDefined();
const query = new Parse.Query('MyObject');
query.find().then(results => {
expect(results.length).toBe(0);
done();
});
});
await myObject.save();
await myObject.destroy();
try {
await RESTController.request('POST', 'batch', {
requests: [
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
],
transaction: true,
});
fail();
} catch (error) {
expect(error).toBeDefined();
const query = new Parse.Query('MyObject');
const results = await query.find();
expect(results.length).toBe(0);
}
});
it('should generate separate session for each call', async () => {

View File

@@ -28,7 +28,7 @@ function createParseServer(options) {
return new Promise((resolve, reject) => {
const parseServer = new ParseServer.default(
Object.assign({}, defaultConfiguration, options, {
serverURL: 'http://localhost:12666/parse',
serverURL: 'http://localhost:12668/parse',
serverStartComplete: error => {
if (error) {
reject(error);
@@ -37,8 +37,8 @@ function createParseServer(options) {
const app = express();
app.use('/parse', parseServer.app);
const server = app.listen(12666);
Parse.serverURL = 'http://localhost:12666/parse';
const server = app.listen(12668);
Parse.serverURL = 'http://localhost:12668/parse';
resolve(server);
}
},

View File

@@ -1,7 +1,7 @@
const Config = require('../lib/Config');
const Parse = require('parse/node');
const request = require('../lib/request');
const { className, createRole, createUser, logIn, updateCLP } = require('./dev');
const { className, createRole, createUser, logIn, updateCLP } = require('./support/dev');
describe('ProtectedFields', function () {
it('should handle and empty protectedFields', async function () {

View File

@@ -26,10 +26,15 @@ const successfulIOS = function (body, installations) {
return Promise.all(promises);
};
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
const pushCompleted = async pushId => {
let result = await Parse.Push.getPushStatus(pushId);
const query = new Parse.Query('_PushStatus');
query.equalTo('objectId', pushId);
let result = await query.first({ useMasterKey: true });
while (!(result && result.get('status') === 'succeeded')) {
result = await Parse.Push.getPushStatus(pushId);
await sleep(100);
result = await query.first({ useMasterKey: true });
}
};
@@ -568,7 +573,7 @@ describe('PushController', () => {
await pushCompleted(pushStatusId);
});
it('should properly report failures in _PushStatus', done => {
it('should properly report failures in _PushStatus', async () => {
const pushAdapter = {
send: function (body, installations) {
return installations.map(installation => {
@@ -593,30 +598,27 @@ describe('PushController', () => {
badge: 1,
},
};
const config = Config.get(Parse.applicationId);
const auth = {
isMaster: true,
};
const pushController = new PushController();
reconfigureServer({
await reconfigureServer({
push: { adapter: pushAdapter },
})
.then(() => {
return pushController.sendPush(payload, where, config, auth);
})
.then(() => {
fail('should not succeed');
done();
})
.catch(() => {
const query = new Parse.Query('_PushStatus');
query.find({ useMasterKey: true }).then(results => {
expect(results.length).toBe(1);
const pushStatus = results[0];
expect(pushStatus.get('status')).toBe('failed');
done();
});
});
});
const config = Config.get(Parse.applicationId);
try {
await pushController.sendPush(payload, where, config, auth);
fail();
} catch (e) {
const query = new Parse.Query('_PushStatus');
let results = await query.find({ useMasterKey: true });
while (results.length === 0) {
results = await query.find({ useMasterKey: true });
}
expect(results.length).toBe(1);
const pushStatus = results[0];
expect(pushStatus.get('status')).toBe('failed');
}
});
it('should support full RESTQuery for increment', async () => {
@@ -1237,7 +1239,7 @@ describe('PushController', () => {
const auth = { isMaster: true };
const pushController = new PushController();
let config = Config.get(Parse.applicationId);
let config;
const pushes = [];
const pushAdapter = {

View File

@@ -1,6 +1,11 @@
const RedisCacheAdapter = require('../lib/Adapters/Cache/RedisCacheAdapter').default;
const Config = require('../lib/Config');
function wait(sleep) {
return new Promise(function (resolve) {
setTimeout(resolve, sleep);
});
}
/*
To run this test part of the complete suite
set PARSE_SERVER_TEST_CACHE='redis'
@@ -11,31 +16,30 @@ describe_only(() => {
})('RedisCacheAdapter', function () {
const KEY = 'hello';
const VALUE = 'world';
let cache;
function wait(sleep) {
return new Promise(function (resolve) {
setTimeout(resolve, sleep);
});
}
beforeEach(async () => {
cache = new RedisCacheAdapter(null, 100);
await cache.clear();
});
it('should get/set/clear', done => {
const cache = new RedisCacheAdapter({
const cacheNaN = new RedisCacheAdapter({
ttl: NaN,
});
cache
cacheNaN
.put(KEY, VALUE)
.then(() => cache.get(KEY))
.then(() => cacheNaN.get(KEY))
.then(value => expect(value).toEqual(VALUE))
.then(() => cache.clear())
.then(() => cache.get(KEY))
.then(() => cacheNaN.clear())
.then(() => cacheNaN.get(KEY))
.then(value => expect(value).toEqual(null))
.then(() => cacheNaN.clear())
.then(done);
});
it('should expire after ttl', done => {
const cache = new RedisCacheAdapter(null, 100);
cache
.put(KEY, VALUE)
.then(() => cache.get(KEY))
@@ -47,8 +51,6 @@ describe_only(() => {
});
it('should not store value for ttl=0', done => {
const cache = new RedisCacheAdapter(null, 100);
cache
.put(KEY, VALUE, 0)
.then(() => cache.get(KEY))
@@ -57,8 +59,6 @@ describe_only(() => {
});
it('should not expire when ttl=Infinity', done => {
const cache = new RedisCacheAdapter(null, 100);
cache
.put(KEY, VALUE, Infinity)
.then(() => cache.get(KEY))
@@ -70,7 +70,6 @@ describe_only(() => {
});
it('should fallback to default ttl', done => {
const cache = new RedisCacheAdapter(null, 100);
let promise = Promise.resolve();
[-100, null, undefined, 'not number', true].forEach(ttl => {
@@ -89,8 +88,6 @@ describe_only(() => {
});
it('should find un-expired records', done => {
const cache = new RedisCacheAdapter(null, 100);
cache
.put(KEY, VALUE)
.then(() => cache.get(KEY))
@@ -102,8 +99,6 @@ describe_only(() => {
});
it('handleShutdown, close connection', async () => {
const cache = new RedisCacheAdapter(null, 100);
await cache.handleShutdown();
setTimeout(() => {
expect(cache.client.connected).toBe(false);

View File

@@ -1,6 +1,5 @@
const UserController = require('../lib/Controllers/UserController').UserController;
const emailAdapter = require('./MockEmailAdapter');
const AppCache = require('../lib/cache').AppCache;
const emailAdapter = require('./support/MockEmailAdapter');
describe('UserController', () => {
const user = {
@@ -11,55 +10,45 @@ describe('UserController', () => {
describe('sendVerificationEmail', () => {
describe('parseFrameURL not provided', () => {
it('uses publicServerURL', done => {
AppCache.put(
defaultConfiguration.appId,
Object.assign({}, defaultConfiguration, {
publicServerURL: 'http://www.example.com',
customPages: {
parseFrameURL: undefined,
},
})
);
it('uses publicServerURL', async done => {
await reconfigureServer({
publicServerURL: 'http://www.example.com',
customPages: {
parseFrameURL: undefined,
},
});
emailAdapter.sendVerificationEmail = options => {
expect(options.link).toEqual(
'http://www.example.com/apps/test/verify_email?token=testToken&username=testUser'
);
emailAdapter.sendVerificationEmail = () => Promise.resolve();
done();
};
const userController = new UserController(emailAdapter, 'test', {
verifyUserEmails: true,
});
userController.sendVerificationEmail(user);
});
});
describe('parseFrameURL provided', () => {
it('uses parseFrameURL and includes the destination in the link parameter', done => {
AppCache.put(
defaultConfiguration.appId,
Object.assign({}, defaultConfiguration, {
publicServerURL: 'http://www.example.com',
customPages: {
parseFrameURL: 'http://someother.example.com/handle-parse-iframe',
},
})
);
it('uses parseFrameURL and includes the destination in the link parameter', async done => {
await reconfigureServer({
publicServerURL: 'http://www.example.com',
customPages: {
parseFrameURL: 'http://someother.example.com/handle-parse-iframe',
},
});
emailAdapter.sendVerificationEmail = options => {
expect(options.link).toEqual(
'http://someother.example.com/handle-parse-iframe?link=%2Fapps%2Ftest%2Fverify_email&token=testToken&username=testUser'
);
emailAdapter.sendVerificationEmail = () => Promise.resolve();
done();
};
const userController = new UserController(emailAdapter, 'test', {
verifyUserEmails: true,
});
userController.sendVerificationEmail(user);
});
});

View File

@@ -1,6 +1,6 @@
'use strict';
const MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions');
const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions');
const request = require('../lib/request');
const Config = require('../lib/Config');

View File

@@ -1,7 +1,7 @@
'use strict';
const request = require('../lib/request');
const MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions');
const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions');
const verifyPassword = function (login, password, isEmail = false) {
const body = !isEmail ? { username: login, password } : { email: login, password };

View File

@@ -175,6 +175,7 @@ describe('batch', () => {
) {
describe('transactions', () => {
beforeEach(async () => {
await TestUtils.destroyAllDataPermanently(true);
if (
semver.satisfies(process.env.MONGODB_VERSION, '>=4.0.4') &&
process.env.MONGODB_TOPOLOGY === 'replicaset' &&
@@ -185,7 +186,8 @@ describe('batch', () => {
databaseURI:
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset',
});
await TestUtils.destroyAllDataPermanently(true);
} else {
await reconfigureServer();
}
});
@@ -243,122 +245,117 @@ describe('batch', () => {
});
});
it('should not save anything when one operation fails in a transaction', done => {
it('should not save anything when one operation fails in a transaction', 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(() => {
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: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
],
transaction: true,
}),
}).catch(error => {
expect(error.data).toBeDefined();
const query = new Parse.Query('MyObject');
query.find().then(results => {
expect(results.length).toBe(0);
done();
});
});
await myObject.save();
await myObject.destroy();
try {
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: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 'value1' },
},
{
method: 'POST',
path: '/1/classes/MyObject',
body: { key: 10 },
},
],
transaction: true,
}),
});
} catch (error) {
expect(error).toBeDefined();
const query = new Parse.Query('MyObject');
const results = await query.find();
expect(results.length).toBe(0);
}
});
it('should generate separate session for each call', async () => {

View File

@@ -1,14 +1,12 @@
'use strict';
const semver = require('semver');
const CurrentSpecReporter = require('./support/CurrentSpecReporter.js');
const { SpecReporter } = require('jasmine-spec-reporter');
// Sets up a Parse API server for testing.
jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 10000;
jasmine.getEnv().addReporter(new CurrentSpecReporter());
if (process.env.PARSE_SERVER_LOG_LEVEL === 'debug') {
const { SpecReporter } = require('jasmine-spec-reporter');
jasmine.getEnv().addReporter(new SpecReporter());
}
jasmine.getEnv().addReporter(new SpecReporter());
global.on_db = (db, callback, elseCallback) => {
if (process.env.PARSE_SERVER_TEST_DB == db) {
@@ -28,6 +26,7 @@ if (global._babelPolyfill) {
process.noDeprecation = true;
const cache = require('../lib/cache').default;
const defaults = require('../lib/defaults').default;
const ParseServer = require('../lib/index').ParseServer;
const path = require('path');
const TestUtils = require('../lib/TestUtils');
@@ -113,7 +112,7 @@ const defaultConfiguration = {
custom: mockCustom(),
facebook: mockFacebook(),
myoauth: {
module: path.resolve(__dirname, 'myoauth'), // relative path as it's run from src
module: path.resolve(__dirname, 'support/myoauth'), // relative path as it's run from src
},
shortLivedAuth: mockShortLivedAuth(),
},
@@ -124,6 +123,16 @@ if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') {
}
const openConnections = {};
const destroyAliveConnections = function () {
for (const socketId in openConnections) {
try {
openConnections[socketId].destroy();
delete openConnections[socketId];
} catch (e) {
/* */
}
}
};
// Set up a default API server for testing with default configuration.
let server;
@@ -146,7 +155,6 @@ const reconfigureServer = (changedConfiguration = {}) => {
if (error) {
reject(error);
} else {
Parse.CoreManager.set('REQUEST_ATTEMPT_LIMIT', 1);
resolve(parseServer);
}
},
@@ -194,8 +202,9 @@ beforeAll(async () => {
afterEach(function (done) {
const afterLogOut = async () => {
if (Object.keys(openConnections).length > 0) {
fail('There were open connections to the server left after the test finished');
console.warn('There were open connections to the server left after the test finished');
}
destroyAliveConnections();
await TestUtils.destroyAllDataPermanently(true);
if (didChangeConfiguration) {
await reconfigureServer();
@@ -205,6 +214,7 @@ afterEach(function (done) {
done();
};
Parse.Cloud._removeAllHooks();
defaults.protectedFields = { _User: { '*': ['email'] } };
databaseAdapter
.getAllClasses()
.then(allSchemas => {

View File

@@ -1,7 +1,7 @@
'use strict';
const request = require('../lib/request');
const parseServerPackage = require('../package.json');
const MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions');
const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions');
const ParseServer = require('../lib/index');
const Config = require('../lib/Config');
const express = require('express');
@@ -317,10 +317,16 @@ describe('server', () => {
})
.then(obj => {
expect(obj.id).toEqual(objId);
server.close(done);
server.close(async () => {
await reconfigureServer();
done();
});
})
.catch(() => {
server.close(done);
server.close(async () => {
await reconfigureServer();
done();
});
});
},
})
@@ -354,12 +360,18 @@ describe('server', () => {
})
.then(obj => {
expect(obj.id).toEqual(objId);
server.close(done);
server.close(async () => {
await reconfigureServer();
done();
});
})
.catch(error => {
fail(JSON.stringify(error));
if (server) {
server.close(done);
server.close(async () => {
await reconfigureServer();
done();
});
} else {
done();
}

View File

@@ -1274,6 +1274,7 @@ describe('schemas', () => {
},
},
}).then(response => {
delete response.data.indexes;
expect(
dd(response.data, {
className: '_User',
@@ -1302,6 +1303,7 @@ describe('schemas', () => {
headers: masterKeyHeaders,
json: true,
}).then(response => {
delete response.data.indexes;
expect(
dd(response.data, {
className: '_User',

View File

@@ -2,8 +2,8 @@ const ldapjs = require('ldapjs');
const fs = require('fs');
const tlsOptions = {
key: fs.readFileSync(__dirname + '/support/cert/key.pem'),
certificate: fs.readFileSync(__dirname + '/support/cert/cert.pem'),
key: fs.readFileSync(__dirname + '/cert/key.pem'),
certificate: fs.readFileSync(__dirname + '/cert/cert.pem'),
};
function newServer(port, dn, provokeSearchError = false, ssl = false) {

View File

@@ -1,4 +1,4 @@
const Config = require('../lib/Config');
const Config = require('../../lib/Config');
const Parse = require('parse/node');
const className = 'AnObject';

View File

@@ -2,5 +2,5 @@
"spec_dir": "spec",
"spec_files": ["*spec.js"],
"helpers": ["helper.js"],
"random": false
"random": true
}