[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:
10
.github/workflows/ci.yml
vendored
10
.github/workflows/ci.yml
vendored
@@ -13,7 +13,7 @@ env:
|
|||||||
jobs:
|
jobs:
|
||||||
check-ci:
|
check-ci:
|
||||||
name: CI Self-Check
|
name: CI Self-Check
|
||||||
timeout-minutes: 30
|
timeout-minutes: 15
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -34,7 +34,7 @@ jobs:
|
|||||||
run: npm run ci:check
|
run: npm run ci:check
|
||||||
check-lint:
|
check-lint:
|
||||||
name: Lint
|
name: Lint
|
||||||
timeout-minutes: 30
|
timeout-minutes: 15
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
@@ -97,8 +97,9 @@ jobs:
|
|||||||
MONGODB_TOPOLOGY: standalone
|
MONGODB_TOPOLOGY: standalone
|
||||||
MONGODB_STORAGE_ENGINE: wiredTiger
|
MONGODB_STORAGE_ENGINE: wiredTiger
|
||||||
NODE_VERSION: 15.11.0
|
NODE_VERSION: 15.11.0
|
||||||
|
fail-fast: false
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
timeout-minutes: 30
|
timeout-minutes: 15
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
services:
|
services:
|
||||||
redis:
|
redis:
|
||||||
@@ -145,8 +146,9 @@ jobs:
|
|||||||
POSTGRES_IMAGE: postgis/postgis:12-3.0
|
POSTGRES_IMAGE: postgis/postgis:12-3.0
|
||||||
- name: Postgres 13, Postgis 3.1
|
- name: Postgres 13, Postgis 3.1
|
||||||
POSTGRES_IMAGE: postgis/postgis:13-3.1
|
POSTGRES_IMAGE: postgis/postgis:13-3.1
|
||||||
|
fail-fast: false
|
||||||
name: ${{ matrix.name }}
|
name: ${{ matrix.name }}
|
||||||
timeout-minutes: 30
|
timeout-minutes: 15
|
||||||
runs-on: ubuntu-18.04
|
runs-on: ubuntu-18.04
|
||||||
services:
|
services:
|
||||||
redis:
|
redis:
|
||||||
|
|||||||
@@ -114,6 +114,8 @@ ___
|
|||||||
- Allow Cloud Validator `options` to be async (dblythy) [#7155](https://github.com/parse-community/parse-server/pull/7155)
|
- Allow Cloud Validator `options` to be async (dblythy) [#7155](https://github.com/parse-community/parse-server/pull/7155)
|
||||||
- Optimize queries on classes with pointer permissions (Pedro Diaz) [#7061](https://github.com/parse-community/parse-server/pull/7061)
|
- Optimize queries on classes with pointer permissions (Pedro Diaz) [#7061](https://github.com/parse-community/parse-server/pull/7061)
|
||||||
- Test Parse Server continuously against all relevant Postgres versions (minor versions), added Postgres compatibility table to Parse Server docs (Corey Baker) [#7176](https://github.com/parse-community/parse-server/pull/7176)
|
- Test Parse Server continuously against all relevant Postgres versions (minor versions), added Postgres compatibility table to Parse Server docs (Corey Baker) [#7176](https://github.com/parse-community/parse-server/pull/7176)
|
||||||
|
- Randomize test suite (Diamond Lewis) [#7265](https://github.com/parse-community/parse-server/pull/7265)
|
||||||
|
- LDAP: Properly unbind client on group search error (Diamond Lewis) [#7265](https://github.com/parse-community/parse-server/pull/7265)
|
||||||
___
|
___
|
||||||
## 4.5.0
|
## 4.5.0
|
||||||
[Full Changelog](https://github.com/parse-community/parse-server/compare/4.4.0...4.5.0)
|
[Full Changelog](https://github.com/parse-community/parse-server/compare/4.4.0...4.5.0)
|
||||||
|
|||||||
@@ -84,6 +84,14 @@ Once you have babel running in watch mode, you can start making changes to parse
|
|||||||
* All the tests should point to sources in the `lib/` folder.
|
* All the tests should point to sources in the `lib/` folder.
|
||||||
* The `lib/` folder is produced by `babel` using either the `npm run build`, `npm run watch`, or the `npm run prepare` step.
|
* The `lib/` folder is produced by `babel` using either the `npm run build`, `npm run watch`, or the `npm run prepare` step.
|
||||||
* The `npm run prepare` step is automatically invoked when your package depends on forked parse-server installed via git for example using `npm install --save git+https://github.com/[username]/parse-server#[branch/commit]`.
|
* The `npm run prepare` step is automatically invoked when your package depends on forked parse-server installed via git for example using `npm install --save git+https://github.com/[username]/parse-server#[branch/commit]`.
|
||||||
|
* The tests are run against a single server instance. You can change the server configurations using `await reconfigureServer({ ... some configuration })` found in `spec/helper.js`.
|
||||||
|
* The tests are ran at random.
|
||||||
|
* Caches and Configurations are reset after every test.
|
||||||
|
* Users are logged out after every test.
|
||||||
|
* Cloud Code hooks are removed after every test.
|
||||||
|
* Database is deleted after every test (indexes are not removed for speed)
|
||||||
|
* Tests are located in the `spec` folder
|
||||||
|
* For better test reporting enable `PARSE_SERVER_LOG_LEVEL=debug`
|
||||||
|
|
||||||
### Troubleshooting
|
### Troubleshooting
|
||||||
|
|
||||||
@@ -108,6 +116,7 @@ Once you have babel running in watch mode, you can start making changes to parse
|
|||||||
* Run the tests for the whole project to make sure the code passes all tests. This can be done by running the test command for a single file but removing the test file argument. The results can be seen at *<PROJECT_ROOT>/coverage/lcov-report/index.html*.
|
* Run the tests for the whole project to make sure the code passes all tests. This can be done by running the test command for a single file but removing the test file argument. The results can be seen at *<PROJECT_ROOT>/coverage/lcov-report/index.html*.
|
||||||
* Lint your code by running `npm run lint` to make sure the code is not going to be rejected by the CI.
|
* Lint your code by running `npm run lint` to make sure the code is not going to be rejected by the CI.
|
||||||
* **Do not** publish the *lib* folder.
|
* **Do not** publish the *lib* folder.
|
||||||
|
* Mocks belong in the `spec/support` folder.
|
||||||
* Please consider if any changes to the [docs](http://docs.parseplatform.org) are needed or add additional sections in the case of an enhancement or feature.
|
* Please consider if any changes to the [docs](http://docs.parseplatform.org) are needed or add additional sections in the case of an enhancement or feature.
|
||||||
|
|
||||||
### Test against Postgres
|
### Test against Postgres
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ const Config = require('../lib/Config');
|
|||||||
|
|
||||||
describe('AdapterLoader', () => {
|
describe('AdapterLoader', () => {
|
||||||
it('should instantiate an adapter from string in object', done => {
|
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({
|
const adapter = loadAdapter({
|
||||||
adapter: adapterPath,
|
adapter: adapterPath,
|
||||||
@@ -23,7 +23,7 @@ describe('AdapterLoader', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should instantiate an adapter from string', done => {
|
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);
|
const adapter = loadAdapter(adapterPath);
|
||||||
|
|
||||||
expect(adapter instanceof Object).toBe(true);
|
expect(adapter instanceof Object).toBe(true);
|
||||||
@@ -119,7 +119,7 @@ describe('AdapterLoader', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should load custom push adapter from string (#3544)', done => {
|
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 = {
|
const options = {
|
||||||
ios: {
|
ios: {
|
||||||
bundleId: 'bundle.id',
|
bundleId: 'bundle.id',
|
||||||
|
|||||||
@@ -227,6 +227,8 @@ describe('execution', () => {
|
|||||||
'test',
|
'test',
|
||||||
'--databaseURI',
|
'--databaseURI',
|
||||||
'mongodb://localhost/test',
|
'mongodb://localhost/test',
|
||||||
|
'--port',
|
||||||
|
'1339',
|
||||||
]);
|
]);
|
||||||
childProcess.stdout.on('data', data => {
|
childProcess.stdout.on('data', data => {
|
||||||
data = data.toString();
|
data = data.toString();
|
||||||
@@ -247,6 +249,8 @@ describe('execution', () => {
|
|||||||
'test',
|
'test',
|
||||||
'--databaseURI',
|
'--databaseURI',
|
||||||
'mongodb://localhost/test',
|
'mongodb://localhost/test',
|
||||||
|
'--port',
|
||||||
|
'1340',
|
||||||
'--mountGraphQL',
|
'--mountGraphQL',
|
||||||
]);
|
]);
|
||||||
let output = '';
|
let output = '';
|
||||||
@@ -271,6 +275,8 @@ describe('execution', () => {
|
|||||||
'test',
|
'test',
|
||||||
'--databaseURI',
|
'--databaseURI',
|
||||||
'mongodb://localhost/test',
|
'mongodb://localhost/test',
|
||||||
|
'--port',
|
||||||
|
'1341',
|
||||||
'--mountGraphQL',
|
'--mountGraphQL',
|
||||||
'--mountPlayground',
|
'--mountPlayground',
|
||||||
]);
|
]);
|
||||||
|
|||||||
@@ -80,6 +80,9 @@ describe('FilesController', () => {
|
|||||||
expect(typeof error).toBe('object');
|
expect(typeof error).toBe('object');
|
||||||
expect(error.message.indexOf('biscuit')).toBe(13);
|
expect(error.message.indexOf('biscuit')).toBe(13);
|
||||||
expect(error.code).toBe(Parse.Error.INVALID_FILE_NAME);
|
expect(error.code).toBe(Parse.Error.INVALID_FILE_NAME);
|
||||||
|
mockAdapter.validateFilename = () => {
|
||||||
|
return null;
|
||||||
|
};
|
||||||
done();
|
done();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ const GridFSBucketAdapter = require('../lib/Adapters/Files/GridFSBucketAdapter')
|
|||||||
const { randomString } = require('../lib/cryptoUtils');
|
const { randomString } = require('../lib/cryptoUtils');
|
||||||
const databaseURI = 'mongodb://localhost:27017/parse';
|
const databaseURI = 'mongodb://localhost:27017/parse';
|
||||||
const request = require('../lib/request');
|
const request = require('../lib/request');
|
||||||
const Config = require('../lib/Config');
|
|
||||||
|
|
||||||
async function expectMissingFile(gfsAdapter, name) {
|
async function expectMissingFile(gfsAdapter, name) {
|
||||||
try {
|
try {
|
||||||
@@ -395,8 +394,9 @@ describe_only_db('mongo')('GridFSBucket and GridStore interop', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should handle getMetadata error', async () => {
|
it('should handle getMetadata error', async () => {
|
||||||
const config = Config.get('test');
|
const gfsAdapter = new GridFSBucketAdapter(databaseURI);
|
||||||
config.filesController.getMetadata = () => Promise.reject();
|
await reconfigureServer({ filesAdapter: gfsAdapter });
|
||||||
|
gfsAdapter.getMetadata = () => Promise.reject();
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
'X-Parse-Application-Id': 'test',
|
'X-Parse-Application-Id': 'test',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
const ldap = require('../lib/Adapters/Auth/ldap');
|
const ldap = require('../lib/Adapters/Auth/ldap');
|
||||||
const mockLdapServer = require('./MockLdapServer');
|
const mockLdapServer = require('./support/MockLdapServer');
|
||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
const port = 12345;
|
const port = 12345;
|
||||||
const sslport = 12346;
|
const sslport = 12346;
|
||||||
@@ -19,243 +19,194 @@ describe('Ldap Auth', () => {
|
|||||||
ldap.validateAppId().then(done).catch(done.fail);
|
ldap.validateAppId().then(done).catch(done.fail);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should succeed with right credentials', done => {
|
it('Should succeed with right credentials', async done => {
|
||||||
mockLdapServer(port, 'uid=testuser, o=example').then(server => {
|
const server = await mockLdapServer(port, 'uid=testuser, o=example');
|
||||||
const options = {
|
const options = {
|
||||||
suffix: 'o=example',
|
suffix: 'o=example',
|
||||||
url: `ldap://localhost:${port}`,
|
url: `ldap://localhost:${port}`,
|
||||||
dn: 'uid={{id}}, o=example',
|
dn: 'uid={{id}}, o=example',
|
||||||
};
|
};
|
||||||
ldap
|
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
|
||||||
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
|
server.close(done);
|
||||||
.then(done)
|
|
||||||
.catch(done.fail)
|
|
||||||
.finally(() => server.close());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should succeed with right credentials when LDAPS is used and certifcate is not checked', done => {
|
it('Should succeed with right credentials when LDAPS is used and certifcate is not checked', async done => {
|
||||||
mockLdapServer(sslport, 'uid=testuser, o=example', false, true).then(server => {
|
const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true);
|
||||||
const options = {
|
const options = {
|
||||||
suffix: 'o=example',
|
suffix: 'o=example',
|
||||||
url: `ldaps://localhost:${sslport}`,
|
url: `ldaps://localhost:${sslport}`,
|
||||||
dn: 'uid={{id}}, o=example',
|
dn: 'uid={{id}}, o=example',
|
||||||
tlsOptions: { rejectUnauthorized: false },
|
tlsOptions: { rejectUnauthorized: false },
|
||||||
};
|
};
|
||||||
ldap
|
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
|
||||||
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
|
server.close(done);
|
||||||
.then(done)
|
|
||||||
.catch(done.fail)
|
|
||||||
.finally(() => server.close());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should succeed when LDAPS is used and the presented certificate is the expected certificate', done => {
|
it('Should succeed when LDAPS is used and the presented certificate is the expected certificate', async done => {
|
||||||
mockLdapServer(sslport, 'uid=testuser, o=example', false, true).then(server => {
|
const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true);
|
||||||
const options = {
|
const options = {
|
||||||
suffix: 'o=example',
|
suffix: 'o=example',
|
||||||
url: `ldaps://localhost:${sslport}`,
|
url: `ldaps://localhost:${sslport}`,
|
||||||
dn: 'uid={{id}}, o=example',
|
dn: 'uid={{id}}, o=example',
|
||||||
tlsOptions: {
|
tlsOptions: {
|
||||||
ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'),
|
ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'),
|
||||||
rejectUnauthorized: true,
|
rejectUnauthorized: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
ldap
|
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
|
||||||
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
|
server.close(done);
|
||||||
.then(done)
|
|
||||||
.catch(done.fail)
|
|
||||||
.finally(() => server.close());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should fail when LDAPS is used and the presented certificate is not the expected certificate', done => {
|
it('Should fail when LDAPS is used and the presented certificate is not the expected certificate', async done => {
|
||||||
mockLdapServer(sslport, 'uid=testuser, o=example', false, true).then(server => {
|
const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true);
|
||||||
const options = {
|
const options = {
|
||||||
suffix: 'o=example',
|
suffix: 'o=example',
|
||||||
url: `ldaps://localhost:${sslport}`,
|
url: `ldaps://localhost:${sslport}`,
|
||||||
dn: 'uid={{id}}, o=example',
|
dn: 'uid={{id}}, o=example',
|
||||||
tlsOptions: {
|
tlsOptions: {
|
||||||
ca: fs.readFileSync(__dirname + '/support/cert/anothercert.pem'),
|
ca: fs.readFileSync(__dirname + '/support/cert/anothercert.pem'),
|
||||||
rejectUnauthorized: true,
|
rejectUnauthorized: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
ldap
|
try {
|
||||||
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
|
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
|
||||||
.then(done.fail)
|
fail();
|
||||||
.catch(err => {
|
} catch (err) {
|
||||||
jequal(err.message, 'LDAPS: Certificate mismatch');
|
expect(err.message).toBe('LDAPS: Certificate mismatch');
|
||||||
done();
|
}
|
||||||
})
|
server.close(done);
|
||||||
.finally(() => server.close());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should fail when LDAPS is used certifcate matches but credentials are wrong', done => {
|
it('Should fail when LDAPS is used certifcate matches but credentials are wrong', async done => {
|
||||||
mockLdapServer(sslport, 'uid=testuser, o=example', false, true).then(server => {
|
const server = await mockLdapServer(sslport, 'uid=testuser, o=example', false, true);
|
||||||
const options = {
|
const options = {
|
||||||
suffix: 'o=example',
|
suffix: 'o=example',
|
||||||
url: `ldaps://localhost:${sslport}`,
|
url: `ldaps://localhost:${sslport}`,
|
||||||
dn: 'uid={{id}}, o=example',
|
dn: 'uid={{id}}, o=example',
|
||||||
tlsOptions: {
|
tlsOptions: {
|
||||||
ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'),
|
ca: fs.readFileSync(__dirname + '/support/cert/cert.pem'),
|
||||||
rejectUnauthorized: true,
|
rejectUnauthorized: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
ldap
|
try {
|
||||||
.validateAuthData({ id: 'testuser', password: 'wrong!' }, options)
|
await ldap.validateAuthData({ id: 'testuser', password: 'wrong!' }, options);
|
||||||
.then(done.fail)
|
fail();
|
||||||
.catch(err => {
|
} catch (err) {
|
||||||
jequal(err.message, 'LDAP: Wrong username or password');
|
expect(err.message).toBe('LDAP: Wrong username or password');
|
||||||
done();
|
}
|
||||||
})
|
server.close(done);
|
||||||
.finally(() => server.close());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should fail with wrong credentials', done => {
|
it('Should fail with wrong credentials', async done => {
|
||||||
mockLdapServer(port, 'uid=testuser, o=example').then(server => {
|
const server = await mockLdapServer(port, 'uid=testuser, o=example');
|
||||||
const options = {
|
const options = {
|
||||||
suffix: 'o=example',
|
suffix: 'o=example',
|
||||||
url: `ldap://localhost:${port}`,
|
url: `ldap://localhost:${port}`,
|
||||||
dn: 'uid={{id}}, o=example',
|
dn: 'uid={{id}}, o=example',
|
||||||
};
|
};
|
||||||
ldap
|
try {
|
||||||
.validateAuthData({ id: 'testuser', password: 'wrong!' }, options)
|
await ldap.validateAuthData({ id: 'testuser', password: 'wrong!' }, options);
|
||||||
.then(done.fail)
|
fail();
|
||||||
.catch(err => {
|
} catch (err) {
|
||||||
jequal(err.message, 'LDAP: Wrong username or password');
|
expect(err.message).toBe('LDAP: Wrong username or password');
|
||||||
done();
|
}
|
||||||
})
|
server.close(done);
|
||||||
.finally(() => server.close());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should succeed if user is in given group', done => {
|
it('Should succeed if user is in given group', async done => {
|
||||||
mockLdapServer(port, 'uid=testuser, o=example').then(server => {
|
const server = await mockLdapServer(port, 'uid=testuser, o=example');
|
||||||
const options = {
|
const options = {
|
||||||
suffix: 'o=example',
|
suffix: 'o=example',
|
||||||
url: `ldap://localhost:${port}`,
|
url: `ldap://localhost:${port}`,
|
||||||
dn: 'uid={{id}}, o=example',
|
dn: 'uid={{id}}, o=example',
|
||||||
groupCn: 'powerusers',
|
groupCn: 'powerusers',
|
||||||
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
|
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
|
||||||
};
|
};
|
||||||
|
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
|
||||||
ldap
|
server.close(done);
|
||||||
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
|
|
||||||
.then(done)
|
|
||||||
.catch(done.fail)
|
|
||||||
.finally(() => server.close());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should fail if user is not in given group', done => {
|
it('Should fail if user is not in given group', async done => {
|
||||||
mockLdapServer(port, 'uid=testuser, o=example').then(server => {
|
const server = await mockLdapServer(port, 'uid=testuser, o=example');
|
||||||
const options = {
|
const options = {
|
||||||
suffix: 'o=example',
|
suffix: 'o=example',
|
||||||
url: `ldap://localhost:${port}`,
|
url: `ldap://localhost:${port}`,
|
||||||
dn: 'uid={{id}}, o=example',
|
dn: 'uid={{id}}, o=example',
|
||||||
groupCn: 'groupTheUserIsNotIn',
|
groupCn: 'groupTheUserIsNotIn',
|
||||||
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
|
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
|
||||||
};
|
};
|
||||||
|
try {
|
||||||
ldap
|
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
|
||||||
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
|
fail();
|
||||||
.then(done.fail)
|
} catch (err) {
|
||||||
.catch(err => {
|
expect(err.message).toBe('LDAP: User not in group');
|
||||||
jequal(err.message, 'LDAP: User not in group');
|
}
|
||||||
done();
|
server.close(done);
|
||||||
})
|
|
||||||
.finally(() => server.close());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should fail if the LDAP server does not allow searching inside the provided suffix', done => {
|
it('Should fail if the LDAP server does not allow searching inside the provided suffix', async done => {
|
||||||
mockLdapServer(port, 'uid=testuser, o=example').then(server => {
|
const server = await mockLdapServer(port, 'uid=testuser, o=example');
|
||||||
const options = {
|
const options = {
|
||||||
suffix: 'o=invalid',
|
suffix: 'o=invalid',
|
||||||
url: `ldap://localhost:${port}`,
|
url: `ldap://localhost:${port}`,
|
||||||
dn: 'uid={{id}}, o=example',
|
dn: 'uid={{id}}, o=example',
|
||||||
groupCn: 'powerusers',
|
groupCn: 'powerusers',
|
||||||
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
|
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
|
||||||
};
|
};
|
||||||
|
try {
|
||||||
ldap
|
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
|
||||||
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
|
fail();
|
||||||
.then(done.fail)
|
} catch (err) {
|
||||||
.catch(err => {
|
expect(err.message).toBe('LDAP group search failed');
|
||||||
jequal(err.message, 'LDAP group search failed');
|
}
|
||||||
done();
|
server.close(done);
|
||||||
})
|
|
||||||
.finally(() => server.close());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should fail if the LDAP server encounters an error while searching', done => {
|
it('Should fail if the LDAP server encounters an error while searching', async done => {
|
||||||
mockLdapServer(port, 'uid=testuser, o=example', true).then(server => {
|
const server = await mockLdapServer(port, 'uid=testuser, o=example', true);
|
||||||
const options = {
|
const options = {
|
||||||
suffix: 'o=example',
|
suffix: 'o=example',
|
||||||
url: `ldap://localhost:${port}`,
|
url: `ldap://localhost:${port}`,
|
||||||
dn: 'uid={{id}}, o=example',
|
dn: 'uid={{id}}, o=example',
|
||||||
groupCn: 'powerusers',
|
groupCn: 'powerusers',
|
||||||
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
|
groupFilter: '(&(uniqueMember=uid={{id}}, o=example)(objectClass=groupOfUniqueNames))',
|
||||||
};
|
};
|
||||||
|
try {
|
||||||
ldap
|
await ldap.validateAuthData({ id: 'testuser', password: 'secret' }, options);
|
||||||
.validateAuthData({ id: 'testuser', password: 'secret' }, options)
|
fail();
|
||||||
.then(done.fail)
|
} catch (err) {
|
||||||
.catch(err => {
|
expect(err.message).toBe('LDAP group search failed');
|
||||||
jequal(err.message, 'LDAP group search failed');
|
}
|
||||||
done();
|
server.close(done);
|
||||||
})
|
|
||||||
.finally(() => server.close());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should delete the password from authData after validation', done => {
|
it('Should delete the password from authData after validation', async done => {
|
||||||
mockLdapServer(port, 'uid=testuser, o=example', true).then(server => {
|
const server = await mockLdapServer(port, 'uid=testuser, o=example', true);
|
||||||
const options = {
|
const options = {
|
||||||
suffix: 'o=example',
|
suffix: 'o=example',
|
||||||
url: `ldap://localhost:${port}`,
|
url: `ldap://localhost:${port}`,
|
||||||
dn: 'uid={{id}}, o=example',
|
dn: 'uid={{id}}, o=example',
|
||||||
};
|
};
|
||||||
|
const authData = { id: 'testuser', password: 'secret' };
|
||||||
const authData = { id: 'testuser', password: 'secret' };
|
await ldap.validateAuthData(authData, options);
|
||||||
|
expect(authData).toEqual({ id: 'testuser' });
|
||||||
ldap
|
server.close(done);
|
||||||
.validateAuthData(authData, options)
|
|
||||||
.then(() => {
|
|
||||||
expect(authData).toEqual({ id: 'testuser' });
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(done.fail)
|
|
||||||
.finally(() => server.close());
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('Should not save the password in the user record after authentication', done => {
|
it('Should not save the password in the user record after authentication', async done => {
|
||||||
mockLdapServer(port, 'uid=testuser, o=example', true).then(server => {
|
const server = await mockLdapServer(port, 'uid=testuser, o=example', true);
|
||||||
const options = {
|
const options = {
|
||||||
suffix: 'o=example',
|
suffix: 'o=example',
|
||||||
url: `ldap://localhost:${port}`,
|
url: `ldap://localhost:${port}`,
|
||||||
dn: 'uid={{id}}, o=example',
|
dn: 'uid={{id}}, o=example',
|
||||||
};
|
};
|
||||||
reconfigureServer({ auth: { ldap: options } }).then(() => {
|
await reconfigureServer({ auth: { ldap: options } });
|
||||||
const authData = { authData: { id: 'testuser', password: 'secret' } };
|
const authData = { authData: { id: 'testuser', password: 'secret' } };
|
||||||
Parse.User.logInWith('ldap', authData).then(returnedUser => {
|
const returnedUser = await Parse.User.logInWith('ldap', authData);
|
||||||
const query = new Parse.Query('User');
|
const query = new Parse.Query('User');
|
||||||
query
|
const user = await query.equalTo('objectId', returnedUser.id).first({ useMasterKey: true });
|
||||||
.equalTo('objectId', returnedUser.id)
|
expect(user.get('authData')).toEqual({ ldap: { id: 'testuser' } });
|
||||||
.first({ useMasterKey: true })
|
expect(user.get('authData').ldap.password).toBeUndefined();
|
||||||
.then(user => {
|
server.close(done);
|
||||||
expect(user.get('authData')).toEqual({ ldap: { id: 'testuser' } });
|
|
||||||
expect(user.get('authData').ldap.password).toBeUndefined();
|
|
||||||
done();
|
|
||||||
})
|
|
||||||
.catch(done.fail)
|
|
||||||
.finally(() => server.close());
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -70,6 +70,7 @@ describe('LoggerController', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const loggerController = new LoggerController(new WinstonLoggerAdapter());
|
const loggerController = new LoggerController(new WinstonLoggerAdapter());
|
||||||
|
loggerController.error('can process an ascending query without throwing');
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
loggerController
|
loggerController
|
||||||
@@ -115,6 +116,7 @@ describe('LoggerController', () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const loggerController = new LoggerController(new WinstonLoggerAdapter());
|
const loggerController = new LoggerController(new WinstonLoggerAdapter());
|
||||||
|
loggerController.error('can process a descending query without throwing');
|
||||||
|
|
||||||
expect(() => {
|
expect(() => {
|
||||||
loggerController
|
loggerController
|
||||||
|
|||||||
@@ -2,10 +2,15 @@
|
|||||||
|
|
||||||
const request = require('../lib/request');
|
const request = require('../lib/request');
|
||||||
|
|
||||||
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
const pushCompleted = async pushId => {
|
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')) {
|
while (!(result && result.get('status') === 'succeeded')) {
|
||||||
result = await Parse.Push.getPushStatus(pushId);
|
await sleep(100);
|
||||||
|
result = await query.first({ useMasterKey: true });
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ const fetch = require('node-fetch');
|
|||||||
const FormData = require('form-data');
|
const FormData = require('form-data');
|
||||||
const ws = require('ws');
|
const ws = require('ws');
|
||||||
require('./helper');
|
require('./helper');
|
||||||
const { updateCLP } = require('./dev');
|
const { updateCLP } = require('./support/dev');
|
||||||
|
|
||||||
const pluralize = require('pluralize');
|
const pluralize = require('pluralize');
|
||||||
const { getMainDefinition } = require('apollo-utilities');
|
const { getMainDefinition } = require('apollo-utilities');
|
||||||
@@ -9033,7 +9033,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
|
|
||||||
it('should support object values', async () => {
|
it('should support object values', async () => {
|
||||||
try {
|
try {
|
||||||
const someFieldValue = {
|
const someObjectFieldValue = {
|
||||||
foo: { bar: 'baz' },
|
foo: { bar: 'baz' },
|
||||||
number: 10,
|
number: 10,
|
||||||
};
|
};
|
||||||
@@ -9048,7 +9048,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
`,
|
`,
|
||||||
variables: {
|
variables: {
|
||||||
schemaFields: {
|
schemaFields: {
|
||||||
addObjects: [{ name: 'someField' }],
|
addObjects: [{ name: 'someObjectField' }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
context: {
|
context: {
|
||||||
@@ -9057,11 +9057,10 @@ describe('ParseGraphQLServer', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
||||||
|
|
||||||
const schema = await new Parse.Schema('SomeClass').get();
|
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({
|
const createResult = await apolloClient.mutate({
|
||||||
mutation: gql`
|
mutation: gql`
|
||||||
@@ -9075,13 +9074,13 @@ describe('ParseGraphQLServer', () => {
|
|||||||
`,
|
`,
|
||||||
variables: {
|
variables: {
|
||||||
fields: {
|
fields: {
|
||||||
someField: someFieldValue,
|
someObjectField: someObjectFieldValue,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const where = {
|
const where = {
|
||||||
someField: {
|
someObjectField: {
|
||||||
equalTo: { key: 'foo.bar', value: 'baz' },
|
equalTo: { key: 'foo.bar', value: 'baz' },
|
||||||
notEqualTo: { key: 'foo.bar', value: 'bat' },
|
notEqualTo: { key: 'foo.bar', value: 'bat' },
|
||||||
greaterThan: { key: 'number', value: 9 },
|
greaterThan: { key: 'number', value: 9 },
|
||||||
@@ -9093,13 +9092,13 @@ describe('ParseGraphQLServer', () => {
|
|||||||
query GetSomeObject($id: ID!, $where: SomeClassWhereInput) {
|
query GetSomeObject($id: ID!, $where: SomeClassWhereInput) {
|
||||||
someClass(id: $id) {
|
someClass(id: $id) {
|
||||||
id
|
id
|
||||||
someField
|
someObjectField
|
||||||
}
|
}
|
||||||
someClasses(where: $where) {
|
someClasses(where: $where) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
someField
|
someObjectField
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9113,13 +9112,13 @@ describe('ParseGraphQLServer', () => {
|
|||||||
|
|
||||||
const { someClass: getResult, someClasses } = queryResult.data;
|
const { someClass: getResult, someClasses } = queryResult.data;
|
||||||
|
|
||||||
const { someField } = getResult;
|
const { someObjectField } = getResult;
|
||||||
expect(typeof someField).toEqual('object');
|
expect(typeof someObjectField).toEqual('object');
|
||||||
expect(someField).toEqual(someFieldValue);
|
expect(someObjectField).toEqual(someObjectFieldValue);
|
||||||
|
|
||||||
// Checks class query results
|
// Checks class query results
|
||||||
expect(someClasses.edges.length).toEqual(1);
|
expect(someClasses.edges.length).toEqual(1);
|
||||||
expect(someClasses.edges[0].node.someField).toEqual(someFieldValue);
|
expect(someClasses.edges[0].node.someObjectField).toEqual(someObjectFieldValue);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(e);
|
handleError(e);
|
||||||
}
|
}
|
||||||
@@ -9127,11 +9126,11 @@ describe('ParseGraphQLServer', () => {
|
|||||||
|
|
||||||
it('should support object composed queries', async () => {
|
it('should support object composed queries', async () => {
|
||||||
try {
|
try {
|
||||||
const someFieldValue = {
|
const someObjectFieldValue1 = {
|
||||||
lorem: 'ipsum',
|
lorem: 'ipsum',
|
||||||
number: 10,
|
number: 10,
|
||||||
};
|
};
|
||||||
const someFieldValue2 = {
|
const someObjectFieldValue2 = {
|
||||||
foo: {
|
foo: {
|
||||||
test: 'bar',
|
test: 'bar',
|
||||||
},
|
},
|
||||||
@@ -9144,7 +9143,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
createClass(
|
createClass(
|
||||||
input: {
|
input: {
|
||||||
name: "SomeClass"
|
name: "SomeClass"
|
||||||
schemaFields: { addObjects: [{ name: "someField" }] }
|
schemaFields: { addObjects: [{ name: "someObjectField" }] }
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
clientMutationId
|
clientMutationId
|
||||||
@@ -9180,10 +9179,10 @@ describe('ParseGraphQLServer', () => {
|
|||||||
`,
|
`,
|
||||||
variables: {
|
variables: {
|
||||||
fields1: {
|
fields1: {
|
||||||
someField: someFieldValue,
|
someObjectField: someObjectFieldValue1,
|
||||||
},
|
},
|
||||||
fields2: {
|
fields2: {
|
||||||
someField: someFieldValue2,
|
someObjectField: someObjectFieldValue2,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -9191,24 +9190,24 @@ describe('ParseGraphQLServer', () => {
|
|||||||
const where = {
|
const where = {
|
||||||
AND: [
|
AND: [
|
||||||
{
|
{
|
||||||
someField: {
|
someObjectField: {
|
||||||
greaterThan: { key: 'number', value: 9 },
|
greaterThan: { key: 'number', value: 9 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
someField: {
|
someObjectField: {
|
||||||
lessThan: { key: 'number', value: 11 },
|
lessThan: { key: 'number', value: 11 },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
OR: [
|
OR: [
|
||||||
{
|
{
|
||||||
someField: {
|
someObjectField: {
|
||||||
equalTo: { key: 'lorem', value: 'ipsum' },
|
equalTo: { key: 'lorem', value: 'ipsum' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
someField: {
|
someObjectField: {
|
||||||
equalTo: { key: 'foo.test', value: 'bar' },
|
equalTo: { key: 'foo.test', value: 'bar' },
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -9223,7 +9222,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
someField
|
someObjectField
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -9241,11 +9240,11 @@ describe('ParseGraphQLServer', () => {
|
|||||||
const { edges } = someClasses;
|
const { edges } = someClasses;
|
||||||
expect(edges.length).toEqual(2);
|
expect(edges.length).toEqual(2);
|
||||||
expect(
|
expect(
|
||||||
edges.find(result => result.node.id === create1.someClass.id).node.someField
|
edges.find(result => result.node.id === create1.someClass.id).node.someObjectField
|
||||||
).toEqual(someFieldValue);
|
).toEqual(someObjectFieldValue1);
|
||||||
expect(
|
expect(
|
||||||
edges.find(result => result.node.id === create2.someClass.id).node.someField
|
edges.find(result => result.node.id === create2.someClass.id).node.someObjectField
|
||||||
).toEqual(someFieldValue2);
|
).toEqual(someObjectFieldValue2);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(e);
|
handleError(e);
|
||||||
}
|
}
|
||||||
@@ -9253,7 +9252,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
|
|
||||||
it('should support array values', async () => {
|
it('should support array values', async () => {
|
||||||
try {
|
try {
|
||||||
const someFieldValue = [1, 'foo', ['bar'], { lorem: 'ipsum' }, true];
|
const someArrayFieldValue = [1, 'foo', ['bar'], { lorem: 'ipsum' }, true];
|
||||||
|
|
||||||
await apolloClient.mutate({
|
await apolloClient.mutate({
|
||||||
mutation: gql`
|
mutation: gql`
|
||||||
@@ -9265,7 +9264,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
`,
|
`,
|
||||||
variables: {
|
variables: {
|
||||||
schemaFields: {
|
schemaFields: {
|
||||||
addArrays: [{ name: 'someField' }],
|
addArrays: [{ name: 'someArrayField' }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
context: {
|
context: {
|
||||||
@@ -9278,7 +9277,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
await parseGraphQLServer.parseGraphQLSchema.databaseController.schemaCache.clear();
|
||||||
|
|
||||||
const schema = await new Parse.Schema('SomeClass').get();
|
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({
|
const createResult = await apolloClient.mutate({
|
||||||
mutation: gql`
|
mutation: gql`
|
||||||
@@ -9292,7 +9291,7 @@ describe('ParseGraphQLServer', () => {
|
|||||||
`,
|
`,
|
||||||
variables: {
|
variables: {
|
||||||
fields: {
|
fields: {
|
||||||
someField: someFieldValue,
|
someArrayField: someArrayFieldValue,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
@@ -9301,17 +9300,17 @@ describe('ParseGraphQLServer', () => {
|
|||||||
query: gql`
|
query: gql`
|
||||||
query GetSomeObject($id: ID!) {
|
query GetSomeObject($id: ID!) {
|
||||||
someClass(id: $id) {
|
someClass(id: $id) {
|
||||||
someField {
|
someArrayField {
|
||||||
... on Element {
|
... on Element {
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
someClasses(where: { someField: { exists: true } }) {
|
someClasses(where: { someArrayField: { exists: true } }) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
id
|
id
|
||||||
someField {
|
someArrayField {
|
||||||
... on Element {
|
... on Element {
|
||||||
value
|
value
|
||||||
}
|
}
|
||||||
@@ -9326,9 +9325,9 @@ describe('ParseGraphQLServer', () => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { someField } = getResult.data.someClass;
|
const { someArrayField } = getResult.data.someClass;
|
||||||
expect(Array.isArray(someField)).toBeTruthy();
|
expect(Array.isArray(someArrayField)).toBeTruthy();
|
||||||
expect(someField.map(element => element.value)).toEqual(someFieldValue);
|
expect(someArrayField.map(element => element.value)).toEqual(someArrayFieldValue);
|
||||||
expect(getResult.data.someClasses.edges.length).toEqual(1);
|
expect(getResult.data.someClasses.edges.length).toEqual(1);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
handleError(e);
|
handleError(e);
|
||||||
@@ -10201,101 +10200,99 @@ describe('ParseGraphQLServer', () => {
|
|||||||
let apolloClient;
|
let apolloClient;
|
||||||
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
if (!httpServer) {
|
const expressApp = express();
|
||||||
const expressApp = express();
|
httpServer = http.createServer(expressApp);
|
||||||
httpServer = http.createServer(expressApp);
|
const TypeEnum = new GraphQLEnumType({
|
||||||
const TypeEnum = new GraphQLEnumType({
|
name: 'TypeEnum',
|
||||||
name: 'TypeEnum',
|
values: {
|
||||||
values: {
|
human: { value: 'human' },
|
||||||
human: { value: 'human' },
|
robot: { value: 'robot' },
|
||||||
robot: { value: 'robot' },
|
},
|
||||||
},
|
});
|
||||||
});
|
const SomeClassType = new GraphQLObjectType({
|
||||||
const SomeClassType = new GraphQLObjectType({
|
name: 'SomeClass',
|
||||||
name: 'SomeClass',
|
fields: {
|
||||||
fields: {
|
nameUpperCase: {
|
||||||
nameUpperCase: {
|
type: new GraphQLNonNull(GraphQLString),
|
||||||
type: new GraphQLNonNull(GraphQLString),
|
resolve: p => p.name.toUpperCase(),
|
||||||
resolve: p => p.name.toUpperCase(),
|
|
||||||
},
|
|
||||||
type: { type: TypeEnum },
|
|
||||||
language: {
|
|
||||||
type: new GraphQLEnumType({
|
|
||||||
name: 'LanguageEnum',
|
|
||||||
values: {
|
|
||||||
fr: { value: 'fr' },
|
|
||||||
en: { value: 'en' },
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
resolve: () => 'fr',
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
}),
|
type: { type: TypeEnum },
|
||||||
parseGraphQLServer = new ParseGraphQLServer(parseServer, {
|
language: {
|
||||||
graphQLPath: '/graphql',
|
type: new GraphQLEnumType({
|
||||||
graphQLCustomTypeDefs: new GraphQLSchema({
|
name: 'LanguageEnum',
|
||||||
query: new GraphQLObjectType({
|
values: {
|
||||||
name: 'Query',
|
fr: { value: 'fr' },
|
||||||
fields: {
|
en: { value: 'en' },
|
||||||
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: [
|
resolve: () => 'fr',
|
||||||
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',
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
}),
|
||||||
|
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();
|
await httpServer.close();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,8 @@ const bodyParser = require('body-parser');
|
|||||||
const auth = require('../lib/Auth');
|
const auth = require('../lib/Auth');
|
||||||
const Config = require('../lib/Config');
|
const Config = require('../lib/Config');
|
||||||
|
|
||||||
const port = 12345;
|
const port = 34567;
|
||||||
const hookServerURL = 'http://localhost:' + port;
|
const hookServerURL = 'http://localhost:' + port;
|
||||||
const AppCache = require('../lib/cache').AppCache;
|
|
||||||
|
|
||||||
describe('Hooks', () => {
|
describe('Hooks', () => {
|
||||||
let server;
|
let server;
|
||||||
@@ -19,7 +18,7 @@ describe('Hooks', () => {
|
|||||||
if (!app) {
|
if (!app) {
|
||||||
app = express();
|
app = express();
|
||||||
app.use(bodyParser.json({ type: '*/*' }));
|
app.use(bodyParser.json({ type: '*/*' }));
|
||||||
server = app.listen(12345, undefined, done);
|
server = app.listen(port, undefined, done);
|
||||||
} else {
|
} else {
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
@@ -383,7 +382,7 @@ describe('Hooks', () => {
|
|||||||
}
|
}
|
||||||
const hooksController = new HooksController(
|
const hooksController = new HooksController(
|
||||||
Parse.applicationId,
|
Parse.applicationId,
|
||||||
AppCache.get('test').databaseController
|
Config.get('test').database
|
||||||
);
|
);
|
||||||
return hooksController.load();
|
return hooksController.load();
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -127,7 +127,10 @@ describe('ParseLiveQueryServer', function () {
|
|||||||
serverStartComplete: () => {
|
serverStartComplete: () => {
|
||||||
expect(parseServer.liveQueryServer).not.toBeUndefined();
|
expect(parseServer.liveQueryServer).not.toBeUndefined();
|
||||||
expect(parseServer.liveQueryServer.server).toBe(parseServer.server);
|
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).not.toBeUndefined();
|
||||||
expect(parseServer.liveQueryServer.server).not.toBe(parseServer.server);
|
expect(parseServer.liveQueryServer.server).not.toBe(parseServer.server);
|
||||||
parseServer.liveQueryServer.server.close(
|
parseServer.liveQueryServer.server.close(
|
||||||
parseServer.server.close.bind(parseServer.server, done)
|
parseServer.server.close.bind(parseServer.server, async () => {
|
||||||
|
await reconfigureServer();
|
||||||
|
done();
|
||||||
|
})
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -169,25 +169,25 @@ describe('ParseServerRESTController', () => {
|
|||||||
process.env.PARSE_SERVER_TEST_DB === 'postgres'
|
process.env.PARSE_SERVER_TEST_DB === 'postgres'
|
||||||
) {
|
) {
|
||||||
describe('transactions', () => {
|
describe('transactions', () => {
|
||||||
let parseServer;
|
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
await TestUtils.destroyAllDataPermanently(true);
|
||||||
if (
|
if (
|
||||||
semver.satisfies(process.env.MONGODB_VERSION, '>=4.0.4') &&
|
semver.satisfies(process.env.MONGODB_VERSION, '>=4.0.4') &&
|
||||||
process.env.MONGODB_TOPOLOGY === 'replicaset' &&
|
process.env.MONGODB_TOPOLOGY === 'replicaset' &&
|
||||||
process.env.MONGODB_STORAGE_ENGINE === 'wiredTiger'
|
process.env.MONGODB_STORAGE_ENGINE === 'wiredTiger'
|
||||||
) {
|
) {
|
||||||
if (!parseServer) {
|
await reconfigureServer({
|
||||||
parseServer = await reconfigureServer({
|
databaseAdapter: undefined,
|
||||||
databaseAdapter: undefined,
|
databaseURI:
|
||||||
databaseURI:
|
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset',
|
||||||
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset',
|
});
|
||||||
});
|
} else {
|
||||||
}
|
await reconfigureServer();
|
||||||
await TestUtils.destroyAllDataPermanently(true);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
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
|
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||||
myObject
|
myObject
|
||||||
.save()
|
.save()
|
||||||
@@ -236,117 +236,113 @@ describe('ParseServerRESTController', () => {
|
|||||||
.catch(done.fail);
|
.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
|
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||||
myObject
|
await myObject.save();
|
||||||
.save()
|
await myObject.destroy();
|
||||||
.then(() => {
|
try {
|
||||||
return myObject.destroy();
|
await RESTController.request('POST', 'batch', {
|
||||||
})
|
requests: [
|
||||||
.then(() => {
|
{
|
||||||
RESTController.request('POST', 'batch', {
|
method: 'POST',
|
||||||
requests: [
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
transaction: true,
|
||||||
},
|
|
||||||
],
|
|
||||||
transaction: true,
|
|
||||||
}).catch(error => {
|
|
||||||
expect(error).toBeDefined();
|
|
||||||
const query = new Parse.Query('MyObject');
|
|
||||||
query.find().then(results => {
|
|
||||||
expect(results.length).toBe(0);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
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 () => {
|
it('should generate separate session for each call', async () => {
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ function createParseServer(options) {
|
|||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const parseServer = new ParseServer.default(
|
const parseServer = new ParseServer.default(
|
||||||
Object.assign({}, defaultConfiguration, options, {
|
Object.assign({}, defaultConfiguration, options, {
|
||||||
serverURL: 'http://localhost:12666/parse',
|
serverURL: 'http://localhost:12668/parse',
|
||||||
serverStartComplete: error => {
|
serverStartComplete: error => {
|
||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
@@ -37,8 +37,8 @@ function createParseServer(options) {
|
|||||||
const app = express();
|
const app = express();
|
||||||
app.use('/parse', parseServer.app);
|
app.use('/parse', parseServer.app);
|
||||||
|
|
||||||
const server = app.listen(12666);
|
const server = app.listen(12668);
|
||||||
Parse.serverURL = 'http://localhost:12666/parse';
|
Parse.serverURL = 'http://localhost:12668/parse';
|
||||||
resolve(server);
|
resolve(server);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
const Config = require('../lib/Config');
|
const Config = require('../lib/Config');
|
||||||
const Parse = require('parse/node');
|
const Parse = require('parse/node');
|
||||||
const request = require('../lib/request');
|
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 () {
|
describe('ProtectedFields', function () {
|
||||||
it('should handle and empty protectedFields', async function () {
|
it('should handle and empty protectedFields', async function () {
|
||||||
|
|||||||
@@ -26,10 +26,15 @@ const successfulIOS = function (body, installations) {
|
|||||||
return Promise.all(promises);
|
return Promise.all(promises);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));
|
||||||
|
|
||||||
const pushCompleted = async pushId => {
|
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')) {
|
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);
|
await pushCompleted(pushStatusId);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should properly report failures in _PushStatus', done => {
|
it('should properly report failures in _PushStatus', async () => {
|
||||||
const pushAdapter = {
|
const pushAdapter = {
|
||||||
send: function (body, installations) {
|
send: function (body, installations) {
|
||||||
return installations.map(installation => {
|
return installations.map(installation => {
|
||||||
@@ -593,30 +598,27 @@ describe('PushController', () => {
|
|||||||
badge: 1,
|
badge: 1,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const config = Config.get(Parse.applicationId);
|
|
||||||
const auth = {
|
const auth = {
|
||||||
isMaster: true,
|
isMaster: true,
|
||||||
};
|
};
|
||||||
const pushController = new PushController();
|
const pushController = new PushController();
|
||||||
reconfigureServer({
|
await reconfigureServer({
|
||||||
push: { adapter: pushAdapter },
|
push: { adapter: pushAdapter },
|
||||||
})
|
});
|
||||||
.then(() => {
|
const config = Config.get(Parse.applicationId);
|
||||||
return pushController.sendPush(payload, where, config, auth);
|
try {
|
||||||
})
|
await pushController.sendPush(payload, where, config, auth);
|
||||||
.then(() => {
|
fail();
|
||||||
fail('should not succeed');
|
} catch (e) {
|
||||||
done();
|
const query = new Parse.Query('_PushStatus');
|
||||||
})
|
let results = await query.find({ useMasterKey: true });
|
||||||
.catch(() => {
|
while (results.length === 0) {
|
||||||
const query = new Parse.Query('_PushStatus');
|
results = await query.find({ useMasterKey: true });
|
||||||
query.find({ useMasterKey: true }).then(results => {
|
}
|
||||||
expect(results.length).toBe(1);
|
expect(results.length).toBe(1);
|
||||||
const pushStatus = results[0];
|
const pushStatus = results[0];
|
||||||
expect(pushStatus.get('status')).toBe('failed');
|
expect(pushStatus.get('status')).toBe('failed');
|
||||||
done();
|
}
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should support full RESTQuery for increment', async () => {
|
it('should support full RESTQuery for increment', async () => {
|
||||||
@@ -1237,7 +1239,7 @@ describe('PushController', () => {
|
|||||||
const auth = { isMaster: true };
|
const auth = { isMaster: true };
|
||||||
const pushController = new PushController();
|
const pushController = new PushController();
|
||||||
|
|
||||||
let config = Config.get(Parse.applicationId);
|
let config;
|
||||||
|
|
||||||
const pushes = [];
|
const pushes = [];
|
||||||
const pushAdapter = {
|
const pushAdapter = {
|
||||||
|
|||||||
@@ -1,6 +1,11 @@
|
|||||||
const RedisCacheAdapter = require('../lib/Adapters/Cache/RedisCacheAdapter').default;
|
const RedisCacheAdapter = require('../lib/Adapters/Cache/RedisCacheAdapter').default;
|
||||||
const Config = require('../lib/Config');
|
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
|
To run this test part of the complete suite
|
||||||
set PARSE_SERVER_TEST_CACHE='redis'
|
set PARSE_SERVER_TEST_CACHE='redis'
|
||||||
@@ -11,31 +16,30 @@ describe_only(() => {
|
|||||||
})('RedisCacheAdapter', function () {
|
})('RedisCacheAdapter', function () {
|
||||||
const KEY = 'hello';
|
const KEY = 'hello';
|
||||||
const VALUE = 'world';
|
const VALUE = 'world';
|
||||||
|
let cache;
|
||||||
|
|
||||||
function wait(sleep) {
|
beforeEach(async () => {
|
||||||
return new Promise(function (resolve) {
|
cache = new RedisCacheAdapter(null, 100);
|
||||||
setTimeout(resolve, sleep);
|
await cache.clear();
|
||||||
});
|
});
|
||||||
}
|
|
||||||
|
|
||||||
it('should get/set/clear', done => {
|
it('should get/set/clear', done => {
|
||||||
const cache = new RedisCacheAdapter({
|
const cacheNaN = new RedisCacheAdapter({
|
||||||
ttl: NaN,
|
ttl: NaN,
|
||||||
});
|
});
|
||||||
|
|
||||||
cache
|
cacheNaN
|
||||||
.put(KEY, VALUE)
|
.put(KEY, VALUE)
|
||||||
.then(() => cache.get(KEY))
|
.then(() => cacheNaN.get(KEY))
|
||||||
.then(value => expect(value).toEqual(VALUE))
|
.then(value => expect(value).toEqual(VALUE))
|
||||||
.then(() => cache.clear())
|
.then(() => cacheNaN.clear())
|
||||||
.then(() => cache.get(KEY))
|
.then(() => cacheNaN.get(KEY))
|
||||||
.then(value => expect(value).toEqual(null))
|
.then(value => expect(value).toEqual(null))
|
||||||
|
.then(() => cacheNaN.clear())
|
||||||
.then(done);
|
.then(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should expire after ttl', done => {
|
it('should expire after ttl', done => {
|
||||||
const cache = new RedisCacheAdapter(null, 100);
|
|
||||||
|
|
||||||
cache
|
cache
|
||||||
.put(KEY, VALUE)
|
.put(KEY, VALUE)
|
||||||
.then(() => cache.get(KEY))
|
.then(() => cache.get(KEY))
|
||||||
@@ -47,8 +51,6 @@ describe_only(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not store value for ttl=0', done => {
|
it('should not store value for ttl=0', done => {
|
||||||
const cache = new RedisCacheAdapter(null, 100);
|
|
||||||
|
|
||||||
cache
|
cache
|
||||||
.put(KEY, VALUE, 0)
|
.put(KEY, VALUE, 0)
|
||||||
.then(() => cache.get(KEY))
|
.then(() => cache.get(KEY))
|
||||||
@@ -57,8 +59,6 @@ describe_only(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should not expire when ttl=Infinity', done => {
|
it('should not expire when ttl=Infinity', done => {
|
||||||
const cache = new RedisCacheAdapter(null, 100);
|
|
||||||
|
|
||||||
cache
|
cache
|
||||||
.put(KEY, VALUE, Infinity)
|
.put(KEY, VALUE, Infinity)
|
||||||
.then(() => cache.get(KEY))
|
.then(() => cache.get(KEY))
|
||||||
@@ -70,7 +70,6 @@ describe_only(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should fallback to default ttl', done => {
|
it('should fallback to default ttl', done => {
|
||||||
const cache = new RedisCacheAdapter(null, 100);
|
|
||||||
let promise = Promise.resolve();
|
let promise = Promise.resolve();
|
||||||
|
|
||||||
[-100, null, undefined, 'not number', true].forEach(ttl => {
|
[-100, null, undefined, 'not number', true].forEach(ttl => {
|
||||||
@@ -89,8 +88,6 @@ describe_only(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('should find un-expired records', done => {
|
it('should find un-expired records', done => {
|
||||||
const cache = new RedisCacheAdapter(null, 100);
|
|
||||||
|
|
||||||
cache
|
cache
|
||||||
.put(KEY, VALUE)
|
.put(KEY, VALUE)
|
||||||
.then(() => cache.get(KEY))
|
.then(() => cache.get(KEY))
|
||||||
@@ -102,8 +99,6 @@ describe_only(() => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it('handleShutdown, close connection', async () => {
|
it('handleShutdown, close connection', async () => {
|
||||||
const cache = new RedisCacheAdapter(null, 100);
|
|
||||||
|
|
||||||
await cache.handleShutdown();
|
await cache.handleShutdown();
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
expect(cache.client.connected).toBe(false);
|
expect(cache.client.connected).toBe(false);
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
const UserController = require('../lib/Controllers/UserController').UserController;
|
const UserController = require('../lib/Controllers/UserController').UserController;
|
||||||
const emailAdapter = require('./MockEmailAdapter');
|
const emailAdapter = require('./support/MockEmailAdapter');
|
||||||
const AppCache = require('../lib/cache').AppCache;
|
|
||||||
|
|
||||||
describe('UserController', () => {
|
describe('UserController', () => {
|
||||||
const user = {
|
const user = {
|
||||||
@@ -11,55 +10,45 @@ describe('UserController', () => {
|
|||||||
|
|
||||||
describe('sendVerificationEmail', () => {
|
describe('sendVerificationEmail', () => {
|
||||||
describe('parseFrameURL not provided', () => {
|
describe('parseFrameURL not provided', () => {
|
||||||
it('uses publicServerURL', done => {
|
it('uses publicServerURL', async done => {
|
||||||
AppCache.put(
|
await reconfigureServer({
|
||||||
defaultConfiguration.appId,
|
publicServerURL: 'http://www.example.com',
|
||||||
Object.assign({}, defaultConfiguration, {
|
customPages: {
|
||||||
publicServerURL: 'http://www.example.com',
|
parseFrameURL: undefined,
|
||||||
customPages: {
|
},
|
||||||
parseFrameURL: undefined,
|
});
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
emailAdapter.sendVerificationEmail = options => {
|
emailAdapter.sendVerificationEmail = options => {
|
||||||
expect(options.link).toEqual(
|
expect(options.link).toEqual(
|
||||||
'http://www.example.com/apps/test/verify_email?token=testToken&username=testUser'
|
'http://www.example.com/apps/test/verify_email?token=testToken&username=testUser'
|
||||||
);
|
);
|
||||||
|
emailAdapter.sendVerificationEmail = () => Promise.resolve();
|
||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
|
|
||||||
const userController = new UserController(emailAdapter, 'test', {
|
const userController = new UserController(emailAdapter, 'test', {
|
||||||
verifyUserEmails: true,
|
verifyUserEmails: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
userController.sendVerificationEmail(user);
|
userController.sendVerificationEmail(user);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('parseFrameURL provided', () => {
|
describe('parseFrameURL provided', () => {
|
||||||
it('uses parseFrameURL and includes the destination in the link parameter', done => {
|
it('uses parseFrameURL and includes the destination in the link parameter', async done => {
|
||||||
AppCache.put(
|
await reconfigureServer({
|
||||||
defaultConfiguration.appId,
|
publicServerURL: 'http://www.example.com',
|
||||||
Object.assign({}, defaultConfiguration, {
|
customPages: {
|
||||||
publicServerURL: 'http://www.example.com',
|
parseFrameURL: 'http://someother.example.com/handle-parse-iframe',
|
||||||
customPages: {
|
},
|
||||||
parseFrameURL: 'http://someother.example.com/handle-parse-iframe',
|
});
|
||||||
},
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
emailAdapter.sendVerificationEmail = options => {
|
emailAdapter.sendVerificationEmail = options => {
|
||||||
expect(options.link).toEqual(
|
expect(options.link).toEqual(
|
||||||
'http://someother.example.com/handle-parse-iframe?link=%2Fapps%2Ftest%2Fverify_email&token=testToken&username=testUser'
|
'http://someother.example.com/handle-parse-iframe?link=%2Fapps%2Ftest%2Fverify_email&token=testToken&username=testUser'
|
||||||
);
|
);
|
||||||
|
emailAdapter.sendVerificationEmail = () => Promise.resolve();
|
||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
|
|
||||||
const userController = new UserController(emailAdapter, 'test', {
|
const userController = new UserController(emailAdapter, 'test', {
|
||||||
verifyUserEmails: true,
|
verifyUserEmails: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
userController.sendVerificationEmail(user);
|
userController.sendVerificationEmail(user);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions');
|
const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions');
|
||||||
const request = require('../lib/request');
|
const request = require('../lib/request');
|
||||||
const Config = require('../lib/Config');
|
const Config = require('../lib/Config');
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
const request = require('../lib/request');
|
const request = require('../lib/request');
|
||||||
const MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions');
|
const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions');
|
||||||
|
|
||||||
const verifyPassword = function (login, password, isEmail = false) {
|
const verifyPassword = function (login, password, isEmail = false) {
|
||||||
const body = !isEmail ? { username: login, password } : { email: login, password };
|
const body = !isEmail ? { username: login, password } : { email: login, password };
|
||||||
|
|||||||
@@ -175,6 +175,7 @@ describe('batch', () => {
|
|||||||
) {
|
) {
|
||||||
describe('transactions', () => {
|
describe('transactions', () => {
|
||||||
beforeEach(async () => {
|
beforeEach(async () => {
|
||||||
|
await TestUtils.destroyAllDataPermanently(true);
|
||||||
if (
|
if (
|
||||||
semver.satisfies(process.env.MONGODB_VERSION, '>=4.0.4') &&
|
semver.satisfies(process.env.MONGODB_VERSION, '>=4.0.4') &&
|
||||||
process.env.MONGODB_TOPOLOGY === 'replicaset' &&
|
process.env.MONGODB_TOPOLOGY === 'replicaset' &&
|
||||||
@@ -185,7 +186,8 @@ describe('batch', () => {
|
|||||||
databaseURI:
|
databaseURI:
|
||||||
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset',
|
'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
|
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||||
myObject
|
await myObject.save();
|
||||||
.save()
|
await myObject.destroy();
|
||||||
.then(() => {
|
try {
|
||||||
return myObject.destroy();
|
await request({
|
||||||
})
|
method: 'POST',
|
||||||
.then(() => {
|
headers: headers,
|
||||||
request({
|
url: 'http://localhost:8378/1/batch',
|
||||||
method: 'POST',
|
body: JSON.stringify({
|
||||||
headers: headers,
|
requests: [
|
||||||
url: 'http://localhost:8378/1/batch',
|
{
|
||||||
body: JSON.stringify({
|
method: 'POST',
|
||||||
requests: [
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
method: 'POST',
|
||||||
},
|
path: '/1/classes/MyObject',
|
||||||
{
|
body: { key: 'value1' },
|
||||||
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: 10 },
|
transaction: true,
|
||||||
},
|
}),
|
||||||
],
|
|
||||||
transaction: true,
|
|
||||||
}),
|
|
||||||
}).catch(error => {
|
|
||||||
expect(error.data).toBeDefined();
|
|
||||||
const query = new Parse.Query('MyObject');
|
|
||||||
query.find().then(results => {
|
|
||||||
expect(results.length).toBe(0);
|
|
||||||
done();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
} 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 () => {
|
it('should generate separate session for each call', async () => {
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const semver = require('semver');
|
const semver = require('semver');
|
||||||
const CurrentSpecReporter = require('./support/CurrentSpecReporter.js');
|
const CurrentSpecReporter = require('./support/CurrentSpecReporter.js');
|
||||||
|
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||||
|
|
||||||
// Sets up a Parse API server for testing.
|
// Sets up a Parse API server for testing.
|
||||||
jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 10000;
|
jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 10000;
|
||||||
jasmine.getEnv().addReporter(new CurrentSpecReporter());
|
jasmine.getEnv().addReporter(new CurrentSpecReporter());
|
||||||
if (process.env.PARSE_SERVER_LOG_LEVEL === 'debug') {
|
jasmine.getEnv().addReporter(new SpecReporter());
|
||||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
|
||||||
jasmine.getEnv().addReporter(new SpecReporter());
|
|
||||||
}
|
|
||||||
|
|
||||||
global.on_db = (db, callback, elseCallback) => {
|
global.on_db = (db, callback, elseCallback) => {
|
||||||
if (process.env.PARSE_SERVER_TEST_DB == db) {
|
if (process.env.PARSE_SERVER_TEST_DB == db) {
|
||||||
@@ -28,6 +26,7 @@ if (global._babelPolyfill) {
|
|||||||
process.noDeprecation = true;
|
process.noDeprecation = true;
|
||||||
|
|
||||||
const cache = require('../lib/cache').default;
|
const cache = require('../lib/cache').default;
|
||||||
|
const defaults = require('../lib/defaults').default;
|
||||||
const ParseServer = require('../lib/index').ParseServer;
|
const ParseServer = require('../lib/index').ParseServer;
|
||||||
const path = require('path');
|
const path = require('path');
|
||||||
const TestUtils = require('../lib/TestUtils');
|
const TestUtils = require('../lib/TestUtils');
|
||||||
@@ -113,7 +112,7 @@ const defaultConfiguration = {
|
|||||||
custom: mockCustom(),
|
custom: mockCustom(),
|
||||||
facebook: mockFacebook(),
|
facebook: mockFacebook(),
|
||||||
myoauth: {
|
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(),
|
shortLivedAuth: mockShortLivedAuth(),
|
||||||
},
|
},
|
||||||
@@ -124,6 +123,16 @@ if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const openConnections = {};
|
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.
|
// Set up a default API server for testing with default configuration.
|
||||||
let server;
|
let server;
|
||||||
|
|
||||||
@@ -146,7 +155,6 @@ const reconfigureServer = (changedConfiguration = {}) => {
|
|||||||
if (error) {
|
if (error) {
|
||||||
reject(error);
|
reject(error);
|
||||||
} else {
|
} else {
|
||||||
Parse.CoreManager.set('REQUEST_ATTEMPT_LIMIT', 1);
|
|
||||||
resolve(parseServer);
|
resolve(parseServer);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -194,8 +202,9 @@ beforeAll(async () => {
|
|||||||
afterEach(function (done) {
|
afterEach(function (done) {
|
||||||
const afterLogOut = async () => {
|
const afterLogOut = async () => {
|
||||||
if (Object.keys(openConnections).length > 0) {
|
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);
|
await TestUtils.destroyAllDataPermanently(true);
|
||||||
if (didChangeConfiguration) {
|
if (didChangeConfiguration) {
|
||||||
await reconfigureServer();
|
await reconfigureServer();
|
||||||
@@ -205,6 +214,7 @@ afterEach(function (done) {
|
|||||||
done();
|
done();
|
||||||
};
|
};
|
||||||
Parse.Cloud._removeAllHooks();
|
Parse.Cloud._removeAllHooks();
|
||||||
|
defaults.protectedFields = { _User: { '*': ['email'] } };
|
||||||
databaseAdapter
|
databaseAdapter
|
||||||
.getAllClasses()
|
.getAllClasses()
|
||||||
.then(allSchemas => {
|
.then(allSchemas => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
const request = require('../lib/request');
|
const request = require('../lib/request');
|
||||||
const parseServerPackage = require('../package.json');
|
const parseServerPackage = require('../package.json');
|
||||||
const MockEmailAdapterWithOptions = require('./MockEmailAdapterWithOptions');
|
const MockEmailAdapterWithOptions = require('./support/MockEmailAdapterWithOptions');
|
||||||
const ParseServer = require('../lib/index');
|
const ParseServer = require('../lib/index');
|
||||||
const Config = require('../lib/Config');
|
const Config = require('../lib/Config');
|
||||||
const express = require('express');
|
const express = require('express');
|
||||||
@@ -317,10 +317,16 @@ describe('server', () => {
|
|||||||
})
|
})
|
||||||
.then(obj => {
|
.then(obj => {
|
||||||
expect(obj.id).toEqual(objId);
|
expect(obj.id).toEqual(objId);
|
||||||
server.close(done);
|
server.close(async () => {
|
||||||
|
await reconfigureServer();
|
||||||
|
done();
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch(() => {
|
.catch(() => {
|
||||||
server.close(done);
|
server.close(async () => {
|
||||||
|
await reconfigureServer();
|
||||||
|
done();
|
||||||
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
@@ -354,12 +360,18 @@ describe('server', () => {
|
|||||||
})
|
})
|
||||||
.then(obj => {
|
.then(obj => {
|
||||||
expect(obj.id).toEqual(objId);
|
expect(obj.id).toEqual(objId);
|
||||||
server.close(done);
|
server.close(async () => {
|
||||||
|
await reconfigureServer();
|
||||||
|
done();
|
||||||
|
});
|
||||||
})
|
})
|
||||||
.catch(error => {
|
.catch(error => {
|
||||||
fail(JSON.stringify(error));
|
fail(JSON.stringify(error));
|
||||||
if (server) {
|
if (server) {
|
||||||
server.close(done);
|
server.close(async () => {
|
||||||
|
await reconfigureServer();
|
||||||
|
done();
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
done();
|
done();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1274,6 +1274,7 @@ describe('schemas', () => {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
|
delete response.data.indexes;
|
||||||
expect(
|
expect(
|
||||||
dd(response.data, {
|
dd(response.data, {
|
||||||
className: '_User',
|
className: '_User',
|
||||||
@@ -1302,6 +1303,7 @@ describe('schemas', () => {
|
|||||||
headers: masterKeyHeaders,
|
headers: masterKeyHeaders,
|
||||||
json: true,
|
json: true,
|
||||||
}).then(response => {
|
}).then(response => {
|
||||||
|
delete response.data.indexes;
|
||||||
expect(
|
expect(
|
||||||
dd(response.data, {
|
dd(response.data, {
|
||||||
className: '_User',
|
className: '_User',
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ const ldapjs = require('ldapjs');
|
|||||||
const fs = require('fs');
|
const fs = require('fs');
|
||||||
|
|
||||||
const tlsOptions = {
|
const tlsOptions = {
|
||||||
key: fs.readFileSync(__dirname + '/support/cert/key.pem'),
|
key: fs.readFileSync(__dirname + '/cert/key.pem'),
|
||||||
certificate: fs.readFileSync(__dirname + '/support/cert/cert.pem'),
|
certificate: fs.readFileSync(__dirname + '/cert/cert.pem'),
|
||||||
};
|
};
|
||||||
|
|
||||||
function newServer(port, dn, provokeSearchError = false, ssl = false) {
|
function newServer(port, dn, provokeSearchError = false, ssl = false) {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
const Config = require('../lib/Config');
|
const Config = require('../../lib/Config');
|
||||||
const Parse = require('parse/node');
|
const Parse = require('parse/node');
|
||||||
|
|
||||||
const className = 'AnObject';
|
const className = 'AnObject';
|
||||||
@@ -2,5 +2,5 @@
|
|||||||
"spec_dir": "spec",
|
"spec_dir": "spec",
|
||||||
"spec_files": ["*spec.js"],
|
"spec_files": ["*spec.js"],
|
||||||
"helpers": ["helper.js"],
|
"helpers": ["helper.js"],
|
||||||
"random": false
|
"random": true
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,6 +95,8 @@ function searchForGroup(client, options, id, resolve, reject) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
res.on('error', () => {
|
res.on('error', () => {
|
||||||
|
client.unbind();
|
||||||
|
client.destroy();
|
||||||
return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP group search failed'));
|
return reject(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'LDAP group search failed'));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ import { KeyPromiseQueue } from './KeyPromiseQueue';
|
|||||||
const DEFAULT_REDIS_TTL = 30 * 1000; // 30 seconds in milliseconds
|
const DEFAULT_REDIS_TTL = 30 * 1000; // 30 seconds in milliseconds
|
||||||
const FLUSH_DB_KEY = '__flush_db__';
|
const FLUSH_DB_KEY = '__flush_db__';
|
||||||
|
|
||||||
function debug() {
|
function debug(...args: any) {
|
||||||
logger.debug.apply(logger, ['RedisCacheAdapter', ...arguments]);
|
const message = ['RedisCacheAdapter: ' + arguments[0]].concat(args.slice(1, args.length));
|
||||||
|
logger.debug.apply(logger, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
const isValidTTL = ttl => typeof ttl === 'number' && ttl > 0;
|
const isValidTTL = ttl => typeof ttl === 'number' && ttl > 0;
|
||||||
@@ -33,13 +34,13 @@ export class RedisCacheAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
get(key) {
|
get(key) {
|
||||||
debug('get', key);
|
debug('get', { key });
|
||||||
return this.queue.enqueue(
|
return this.queue.enqueue(
|
||||||
key,
|
key,
|
||||||
() =>
|
() =>
|
||||||
new Promise(resolve => {
|
new Promise(resolve => {
|
||||||
this.client.get(key, function (err, res) {
|
this.client.get(key, function (err, res) {
|
||||||
debug('-> get', key, res);
|
debug('-> get', { key, res });
|
||||||
if (!res) {
|
if (!res) {
|
||||||
return resolve(null);
|
return resolve(null);
|
||||||
}
|
}
|
||||||
@@ -51,7 +52,7 @@ export class RedisCacheAdapter {
|
|||||||
|
|
||||||
put(key, value, ttl = this.ttl) {
|
put(key, value, ttl = this.ttl) {
|
||||||
value = JSON.stringify(value);
|
value = JSON.stringify(value);
|
||||||
debug('put', key, value, ttl);
|
debug('put', { key, value, ttl });
|
||||||
|
|
||||||
if (ttl === 0) {
|
if (ttl === 0) {
|
||||||
// ttl of zero is a logical no-op, but redis cannot set expire time of zero
|
// ttl of zero is a logical no-op, but redis cannot set expire time of zero
|
||||||
@@ -86,7 +87,7 @@ export class RedisCacheAdapter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
del(key) {
|
del(key) {
|
||||||
debug('del', key);
|
debug('del', { key });
|
||||||
return this.queue.enqueue(
|
return this.queue.enqueue(
|
||||||
key,
|
key,
|
||||||
() =>
|
() =>
|
||||||
|
|||||||
@@ -1215,16 +1215,10 @@ export default class SchemaController {
|
|||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
||||||
for (const fieldName in object) {
|
for (const fieldName in object) {
|
||||||
if (object[fieldName] === undefined) {
|
if (object[fieldName] && getType(object[fieldName]) === 'GeoPoint') {
|
||||||
continue;
|
|
||||||
}
|
|
||||||
const expected = getType(object[fieldName]);
|
|
||||||
if (expected === 'GeoPoint') {
|
|
||||||
geocount++;
|
geocount++;
|
||||||
}
|
}
|
||||||
if (geocount > 1) {
|
if (geocount > 1) {
|
||||||
// Make sure all field validation operations run before we return.
|
|
||||||
// If not - we are continuing to run logic, but already provided response from the server.
|
|
||||||
return Promise.reject(
|
return Promise.reject(
|
||||||
new Parse.Error(
|
new Parse.Error(
|
||||||
Parse.Error.INCORRECT_TYPE,
|
Parse.Error.INCORRECT_TYPE,
|
||||||
@@ -1232,6 +1226,12 @@ export default class SchemaController {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
for (const fieldName in object) {
|
||||||
|
if (object[fieldName] === undefined) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const expected = getType(object[fieldName]);
|
||||||
if (!expected) {
|
if (!expected) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user