fix: Parse Server doesn't shutdown gracefully (#9634)
This commit is contained in:
@@ -3,11 +3,9 @@ const request = require('../lib/request');
|
||||
describe('Enable express error handler', () => {
|
||||
it('should call the default handler in case of error, like updating a non existing object', async done => {
|
||||
spyOn(console, 'error');
|
||||
const parseServer = await reconfigureServer(
|
||||
Object.assign({}, defaultConfiguration, {
|
||||
enableExpressErrorHandler: true,
|
||||
})
|
||||
);
|
||||
const parseServer = await reconfigureServer({
|
||||
enableExpressErrorHandler: true,
|
||||
});
|
||||
parseServer.app.use(function (err, req, res, next) {
|
||||
expect(err.message).toBe('Object not found.');
|
||||
next(err);
|
||||
|
||||
@@ -33,9 +33,7 @@ describe('miscellaneous', () => {
|
||||
expect(results.length).toEqual(1);
|
||||
expect(results[0]['foo']).toEqual('bar');
|
||||
});
|
||||
});
|
||||
|
||||
describe('miscellaneous', function () {
|
||||
it('create a GameScore object', function (done) {
|
||||
const obj = new Parse.Object('GameScore');
|
||||
obj.set('score', 1337);
|
||||
|
||||
@@ -12,7 +12,6 @@ describe('Config Keys', () => {
|
||||
|
||||
it('recognizes invalid keys in root', async () => {
|
||||
await expectAsync(reconfigureServer({
|
||||
...defaultConfiguration,
|
||||
invalidKey: 1,
|
||||
})).toBeResolved();
|
||||
const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '');
|
||||
@@ -21,7 +20,6 @@ describe('Config Keys', () => {
|
||||
|
||||
it('recognizes invalid keys in pages.customUrls', async () => {
|
||||
await expectAsync(reconfigureServer({
|
||||
...defaultConfiguration,
|
||||
pages: {
|
||||
customUrls: {
|
||||
invalidKey: 1,
|
||||
@@ -37,7 +35,6 @@ describe('Config Keys', () => {
|
||||
|
||||
it('recognizes invalid keys in liveQueryServerOptions', async () => {
|
||||
await expectAsync(reconfigureServer({
|
||||
...defaultConfiguration,
|
||||
liveQueryServerOptions: {
|
||||
invalidKey: 1,
|
||||
MasterKey: 1,
|
||||
@@ -50,7 +47,6 @@ describe('Config Keys', () => {
|
||||
|
||||
it('recognizes invalid keys in rateLimit', async () => {
|
||||
await expectAsync(reconfigureServer({
|
||||
...defaultConfiguration,
|
||||
rateLimit: [
|
||||
{ invalidKey: 1 },
|
||||
{ RequestPath: 1 },
|
||||
@@ -64,7 +60,7 @@ describe('Config Keys', () => {
|
||||
expect(error).toMatch('rateLimit\\[2\\]\\.RequestTimeWindow');
|
||||
});
|
||||
|
||||
it('recognizes valid keys in default configuration', async () => {
|
||||
it_only_db('mongo')('recognizes valid keys in default configuration', async () => {
|
||||
await expectAsync(reconfigureServer({
|
||||
...defaultConfiguration,
|
||||
})).toBeResolved();
|
||||
|
||||
@@ -431,17 +431,32 @@ describe('ParseGraphQLServer', () => {
|
||||
objects.push(object1, object2, object3, object4);
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
async function createGQLFromParseServer(_parseServer) {
|
||||
if (parseLiveQueryServer) {
|
||||
await parseLiveQueryServer.server.close();
|
||||
}
|
||||
if (httpServer) {
|
||||
await httpServer.close();
|
||||
}
|
||||
const expressApp = express();
|
||||
httpServer = http.createServer(expressApp);
|
||||
expressApp.use('/parse', parseServer.app);
|
||||
expressApp.use('/parse', _parseServer.app);
|
||||
parseLiveQueryServer = await ParseServer.createLiveQueryServer(httpServer, {
|
||||
port: 1338,
|
||||
});
|
||||
parseGraphQLServer = new ParseGraphQLServer(_parseServer, {
|
||||
graphQLPath: '/graphql',
|
||||
playgroundPath: '/playground',
|
||||
subscriptionsPath: '/subscriptions',
|
||||
});
|
||||
parseGraphQLServer.applyGraphQL(expressApp);
|
||||
parseGraphQLServer.applyPlayground(expressApp);
|
||||
parseGraphQLServer.createSubscriptions(httpServer);
|
||||
await new Promise(resolve => httpServer.listen({ port: 13377 }, resolve));
|
||||
}
|
||||
|
||||
beforeEach(async () => {
|
||||
await createGQLFromParseServer(parseServer);
|
||||
|
||||
const subscriptionClient = new SubscriptionClient(
|
||||
'ws://localhost:13377/subscriptions',
|
||||
@@ -753,10 +768,6 @@ describe('ParseGraphQLServer', () => {
|
||||
}
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await resetGraphQLCache();
|
||||
});
|
||||
|
||||
it('should have Node interface', async () => {
|
||||
const schemaTypes = (
|
||||
await apolloClient.query({
|
||||
@@ -2821,7 +2832,8 @@ describe('ParseGraphQLServer', () => {
|
||||
}
|
||||
});
|
||||
it('Id inputs should work either with global id or object id with objectId higher than 19', async () => {
|
||||
await reconfigureServer({ objectIdSize: 20 });
|
||||
const parseServer = await reconfigureServer({ objectIdSize: 20 });
|
||||
await createGQLFromParseServer(parseServer);
|
||||
const obj = new Parse.Object('SomeClass');
|
||||
await obj.save({ name: 'aname', type: 'robot' });
|
||||
const result = await apolloClient.query({
|
||||
@@ -5328,7 +5340,7 @@ describe('ParseGraphQLServer', () => {
|
||||
parseServer = await global.reconfigureServer({
|
||||
maxLimit: 10,
|
||||
});
|
||||
|
||||
await createGQLFromParseServer(parseServer);
|
||||
const promises = [];
|
||||
for (let i = 0; i < 100; i++) {
|
||||
const obj = new Parse.Object('SomeClass');
|
||||
@@ -6841,7 +6853,7 @@ describe('ParseGraphQLServer', () => {
|
||||
parseServer = await global.reconfigureServer({
|
||||
publicServerURL: 'http://localhost:13377/parse',
|
||||
});
|
||||
|
||||
await createGQLFromParseServer(parseServer);
|
||||
const body = new FormData();
|
||||
body.append(
|
||||
'operations',
|
||||
@@ -7049,6 +7061,7 @@ describe('ParseGraphQLServer', () => {
|
||||
challengeAdapter,
|
||||
},
|
||||
});
|
||||
await createGQLFromParseServer(parseServer);
|
||||
const clientMutationId = uuidv4();
|
||||
|
||||
const result = await apolloClient.mutate({
|
||||
@@ -7095,6 +7108,7 @@ describe('ParseGraphQLServer', () => {
|
||||
challengeAdapter,
|
||||
},
|
||||
});
|
||||
await createGQLFromParseServer(parseServer);
|
||||
const clientMutationId = uuidv4();
|
||||
const userSchema = new Parse.Schema('_User');
|
||||
userSchema.addString('someField');
|
||||
@@ -7169,7 +7183,7 @@ describe('ParseGraphQLServer', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await createGQLFromParseServer(parseServer);
|
||||
userSchema.addString('someField');
|
||||
userSchema.addPointer('aPointer', '_User');
|
||||
await userSchema.update();
|
||||
@@ -7239,7 +7253,7 @@ describe('ParseGraphQLServer', () => {
|
||||
challengeAdapter,
|
||||
},
|
||||
});
|
||||
|
||||
await createGQLFromParseServer(parseServer);
|
||||
const user = new Parse.User();
|
||||
await user.save({ username: 'username', password: 'password' });
|
||||
|
||||
@@ -7310,6 +7324,7 @@ describe('ParseGraphQLServer', () => {
|
||||
challengeAdapter,
|
||||
},
|
||||
});
|
||||
await createGQLFromParseServer(parseServer);
|
||||
const clientMutationId = uuidv4();
|
||||
const user = new Parse.User();
|
||||
user.setUsername('user1');
|
||||
@@ -7441,6 +7456,7 @@ describe('ParseGraphQLServer', () => {
|
||||
emailAdapter: emailAdapter,
|
||||
publicServerURL: 'http://test.test',
|
||||
});
|
||||
await createGQLFromParseServer(parseServer);
|
||||
const user = new Parse.User();
|
||||
user.setUsername('user1');
|
||||
user.setPassword('user1');
|
||||
@@ -7488,6 +7504,7 @@ describe('ParseGraphQLServer', () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
await createGQLFromParseServer(parseServer);
|
||||
const user = new Parse.User();
|
||||
user.setUsername('user1');
|
||||
user.setPassword('user1');
|
||||
@@ -7550,6 +7567,7 @@ describe('ParseGraphQLServer', () => {
|
||||
emailAdapter: emailAdapter,
|
||||
publicServerURL: 'http://test.test',
|
||||
});
|
||||
await createGQLFromParseServer(parseServer);
|
||||
const user = new Parse.User();
|
||||
user.setUsername('user1');
|
||||
user.setPassword('user1');
|
||||
@@ -9306,7 +9324,7 @@ describe('ParseGraphQLServer', () => {
|
||||
parseServer = await global.reconfigureServer({
|
||||
publicServerURL: 'http://localhost:13377/parse',
|
||||
});
|
||||
|
||||
await createGQLFromParseServer(parseServer);
|
||||
const body = new FormData();
|
||||
body.append(
|
||||
'operations',
|
||||
@@ -9339,7 +9357,6 @@ describe('ParseGraphQLServer', () => {
|
||||
headers,
|
||||
body,
|
||||
});
|
||||
|
||||
expect(res.status).toEqual(200);
|
||||
|
||||
const result = JSON.parse(await res.text());
|
||||
@@ -9553,6 +9570,7 @@ describe('ParseGraphQLServer', () => {
|
||||
parseServer = await global.reconfigureServer({
|
||||
publicServerURL: 'http://localhost:13377/parse',
|
||||
});
|
||||
await createGQLFromParseServer(parseServer);
|
||||
const schemaController = await parseServer.config.databaseController.loadSchema();
|
||||
await schemaController.addClassIfNotExists('SomeClassWithRequiredFile', {
|
||||
someField: { type: 'File', required: true },
|
||||
@@ -9617,6 +9635,7 @@ describe('ParseGraphQLServer', () => {
|
||||
parseServer = await global.reconfigureServer({
|
||||
publicServerURL: 'http://localhost:13377/parse',
|
||||
});
|
||||
await createGQLFromParseServer(parseServer);
|
||||
const schema = new Parse.Schema('SomeClass');
|
||||
schema.addFile('someFileField');
|
||||
schema.addPointer('somePointerField', 'SomeClass');
|
||||
@@ -9725,7 +9744,7 @@ describe('ParseGraphQLServer', () => {
|
||||
parseServer = await global.reconfigureServer({
|
||||
publicServerURL: 'http://localhost:13377/parse',
|
||||
});
|
||||
|
||||
await createGQLFromParseServer(parseServer);
|
||||
const body = new FormData();
|
||||
body.append(
|
||||
'operations',
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
'use strict';
|
||||
const http = require('http');
|
||||
const Auth = require('../lib/Auth');
|
||||
const UserController = require('../lib/Controllers/UserController').UserController;
|
||||
const Config = require('../lib/Config');
|
||||
const ParseServer = require('../lib/index').ParseServer;
|
||||
const triggers = require('../lib/triggers');
|
||||
const { resolvingPromise, sleep } = require('../lib/TestUtils');
|
||||
const { resolvingPromise, sleep, getConnectionsCount } = require('../lib/TestUtils');
|
||||
const request = require('../lib/request');
|
||||
const validatorFail = () => {
|
||||
throw 'you are not authorized';
|
||||
};
|
||||
@@ -1181,6 +1183,78 @@ describe('ParseLiveQuery', function () {
|
||||
await new Promise(resolve => server.server.close(resolve));
|
||||
});
|
||||
|
||||
it_id('45655b74-716f-4fa1-a058-67eb21f3c3db')(it)('does shutdown separate liveQuery server', async () => {
|
||||
await reconfigureServer({ appId: 'test_app_id' });
|
||||
let close = false;
|
||||
const config = {
|
||||
appId: 'hello_test',
|
||||
masterKey: 'world',
|
||||
port: 1345,
|
||||
mountPath: '/1',
|
||||
serverURL: 'http://localhost:1345/1',
|
||||
liveQuery: {
|
||||
classNames: ['Yolo'],
|
||||
},
|
||||
startLiveQueryServer: true,
|
||||
verbose: false,
|
||||
silent: true,
|
||||
liveQueryServerOptions: {
|
||||
port: 1346,
|
||||
},
|
||||
serverCloseComplete: () => {
|
||||
close = true;
|
||||
},
|
||||
};
|
||||
if (process.env.PARSE_SERVER_TEST_DB === 'postgres') {
|
||||
config.databaseAdapter = new databaseAdapter.constructor({
|
||||
uri: databaseURI,
|
||||
collectionPrefix: 'test_',
|
||||
});
|
||||
config.filesAdapter = defaultConfiguration.filesAdapter;
|
||||
}
|
||||
const parseServer = await ParseServer.startApp(config);
|
||||
expect(parseServer.liveQueryServer).toBeDefined();
|
||||
expect(parseServer.liveQueryServer.server).not.toBe(parseServer.server);
|
||||
|
||||
// Open a connection to the liveQuery server
|
||||
const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient();
|
||||
client.serverURL = 'ws://localhost:1346/1';
|
||||
const query = await new Parse.Query('Yolo').subscribe();
|
||||
|
||||
// Open a connection to the parse server
|
||||
const health = await request({
|
||||
method: 'GET',
|
||||
url: `http://localhost:1345/1/health`,
|
||||
json: true,
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'hello_test',
|
||||
'X-Parse-Master-Key': 'world',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
agent: new http.Agent({ keepAlive: true }),
|
||||
}).then(res => res.data);
|
||||
expect(health.status).toBe('ok');
|
||||
|
||||
let parseConnectionCount = await getConnectionsCount(parseServer.server);
|
||||
let liveQueryConnectionCount = await getConnectionsCount(parseServer.liveQueryServer.server);
|
||||
|
||||
expect(parseConnectionCount > 0).toBe(true);
|
||||
expect(liveQueryConnectionCount > 0).toBe(true);
|
||||
await Promise.all([
|
||||
parseServer.handleShutdown(),
|
||||
new Promise(resolve => query.on('close', resolve)),
|
||||
]);
|
||||
expect(close).toBe(true);
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
expect(parseServer.liveQueryServer.server.address()).toBeNull();
|
||||
expect(parseServer.liveQueryServer.subscriber.isOpen).toBeFalse();
|
||||
|
||||
parseConnectionCount = await getConnectionsCount(parseServer.server);
|
||||
liveQueryConnectionCount = await getConnectionsCount(parseServer.liveQueryServer.server);
|
||||
expect(parseConnectionCount).toBe(0);
|
||||
expect(liveQueryConnectionCount).toBe(0);
|
||||
});
|
||||
|
||||
it('prevent afterSave trigger if not exists', async () => {
|
||||
await reconfigureServer({
|
||||
liveQuery: {
|
||||
|
||||
@@ -3,11 +3,8 @@
|
||||
const Config = require('../lib/Config');
|
||||
const Parse = require('parse/node');
|
||||
const request = require('../lib/request');
|
||||
let databaseAdapter;
|
||||
|
||||
const fullTextHelper = async () => {
|
||||
const config = Config.get('test');
|
||||
databaseAdapter = config.database.adapter;
|
||||
const subjects = [
|
||||
'coffee',
|
||||
'Coffee Shopping',
|
||||
@@ -18,12 +15,6 @@ const fullTextHelper = async () => {
|
||||
'coffee and cream',
|
||||
'Cafe con Leche',
|
||||
];
|
||||
await reconfigureServer({
|
||||
appId: 'test',
|
||||
restAPIKey: 'test',
|
||||
publicServerURL: 'http://localhost:8378/1',
|
||||
databaseAdapter,
|
||||
});
|
||||
await Parse.Object.saveAll(
|
||||
subjects.map(subject => new Parse.Object('TestObject').set({ subject, comment: subject }))
|
||||
);
|
||||
@@ -101,7 +92,7 @@ describe('Parse.Query Full Text Search testing', () => {
|
||||
body: { where, _method: 'GET' },
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
@@ -189,7 +180,7 @@ describe_only_db('mongo')('[mongodb] Parse.Query Full Text Search testing', () =
|
||||
url: 'http://localhost:8378/1/schemas/TestObject',
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
'X-Parse-Master-Key': 'test',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
@@ -220,7 +211,7 @@ describe_only_db('mongo')('[mongodb] Parse.Query Full Text Search testing', () =
|
||||
body: { where, _method: 'GET' },
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
@@ -288,7 +279,7 @@ describe_only_db('postgres')('[postgres] Parse.Query Full Text Search testing',
|
||||
body: { where, _method: 'GET' },
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
@@ -322,7 +313,7 @@ describe_only_db('postgres')('[postgres] Parse.Query Full Text Search testing',
|
||||
body: { where, _method: 'GET' },
|
||||
headers: {
|
||||
'X-Parse-Application-Id': 'test',
|
||||
'X-Parse-REST-API-Key': 'test',
|
||||
'X-Parse-REST-API-Key': 'rest',
|
||||
'Content-Type': 'application/json',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
'use strict';
|
||||
/* Tests for ParseServer.js */
|
||||
const express = require('express');
|
||||
const MongoStorageAdapter = require('../lib/Adapters/Storage/Mongo/MongoStorageAdapter').default;
|
||||
const PostgresStorageAdapter = require('../lib/Adapters/Storage/Postgres/PostgresStorageAdapter')
|
||||
.default;
|
||||
const ParseServer = require('../lib/ParseServer').default;
|
||||
const path = require('path');
|
||||
const { spawn } = require('child_process');
|
||||
@@ -45,49 +42,6 @@ describe('Server Url Checks', () => {
|
||||
);
|
||||
});
|
||||
|
||||
xit('handleShutdown, close connection', done => {
|
||||
const mongoURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase';
|
||||
const postgresURI = 'postgres://localhost:5432/parse_server_postgres_adapter_test_database';
|
||||
let databaseAdapter;
|
||||
if (process.env.PARSE_SERVER_TEST_DB === 'postgres') {
|
||||
databaseAdapter = new PostgresStorageAdapter({
|
||||
uri: process.env.PARSE_SERVER_TEST_DATABASE_URI || postgresURI,
|
||||
collectionPrefix: 'test_',
|
||||
});
|
||||
} else {
|
||||
databaseAdapter = new MongoStorageAdapter({
|
||||
uri: mongoURI,
|
||||
collectionPrefix: 'test_',
|
||||
});
|
||||
}
|
||||
let close = false;
|
||||
const newConfiguration = Object.assign({}, defaultConfiguration, {
|
||||
databaseAdapter,
|
||||
serverStartComplete: () => {
|
||||
let promise = Promise.resolve();
|
||||
if (process.env.PARSE_SERVER_TEST_DB !== 'postgres') {
|
||||
promise = parseServer.config.filesController.adapter._connect();
|
||||
}
|
||||
promise.then(() => {
|
||||
parseServer.handleShutdown();
|
||||
parseServer.server.close(err => {
|
||||
if (err) {
|
||||
done.fail('Close Server Error');
|
||||
}
|
||||
reconfigureServer({}).then(() => {
|
||||
expect(close).toBe(true);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
serverCloseComplete: () => {
|
||||
close = true;
|
||||
},
|
||||
});
|
||||
const parseServer = ParseServer.startApp(newConfiguration);
|
||||
});
|
||||
|
||||
it('does not have unhandled promise rejection in the case of load error', done => {
|
||||
const parseServerProcess = spawn(path.resolve(__dirname, './support/FailingServer.js'));
|
||||
let stdout;
|
||||
|
||||
@@ -282,7 +282,6 @@ describe('ParseServerRESTController', () => {
|
||||
});
|
||||
|
||||
it('should generate separate session for each call', async () => {
|
||||
await reconfigureServer();
|
||||
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||
await myObject.save({ key: 'stringField' });
|
||||
await myObject.destroy();
|
||||
|
||||
@@ -182,6 +182,9 @@ describe('PushController', () => {
|
||||
return ['ios', 'android'];
|
||||
},
|
||||
};
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
const payload = {
|
||||
data: {
|
||||
alert: 'Hello World!',
|
||||
@@ -212,9 +215,6 @@ describe('PushController', () => {
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
};
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
await Parse.Object.saveAll(installations);
|
||||
const pushStatusId = await sendPush(payload, {}, config, auth);
|
||||
await pushCompleted(pushStatusId);
|
||||
@@ -247,6 +247,9 @@ describe('PushController', () => {
|
||||
return ['ios', 'android'];
|
||||
},
|
||||
};
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
const payload = {
|
||||
data: {
|
||||
alert: 'Hello World!',
|
||||
@@ -277,9 +280,6 @@ describe('PushController', () => {
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
};
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
await Parse.Object.saveAll(installations);
|
||||
const pushStatusId = await sendPush(payload, {}, config, auth);
|
||||
await pushCompleted(pushStatusId);
|
||||
@@ -309,7 +309,9 @@ describe('PushController', () => {
|
||||
return ['ios'];
|
||||
},
|
||||
};
|
||||
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
const payload = {
|
||||
data: {
|
||||
alert: 'Hello World!',
|
||||
@@ -331,9 +333,6 @@ describe('PushController', () => {
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
};
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
await Parse.Object.saveAll(installations);
|
||||
const pushStatusId = await sendPush(payload, {}, config, auth);
|
||||
await pushCompleted(pushStatusId);
|
||||
@@ -382,14 +381,13 @@ describe('PushController', () => {
|
||||
return ['ios'];
|
||||
},
|
||||
};
|
||||
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
};
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
await Parse.Object.saveAll(installations);
|
||||
const objectIds = installations.map(installation => {
|
||||
return installation.id;
|
||||
@@ -445,14 +443,13 @@ describe('PushController', () => {
|
||||
return ['ios'];
|
||||
},
|
||||
};
|
||||
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
};
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
await Parse.Object.saveAll(installations);
|
||||
const pushStatusId = await sendPush(payload, {}, config, auth);
|
||||
await pushCompleted(pushStatusId);
|
||||
@@ -548,16 +545,15 @@ describe('PushController', () => {
|
||||
return ['ios'];
|
||||
},
|
||||
};
|
||||
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
};
|
||||
await installation.save();
|
||||
await reconfigureServer({
|
||||
serverURL: 'http://localhost:8378/', // server with borked URL
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
};
|
||||
const pushStatusId = await sendPush(payload, {}, config, auth);
|
||||
// it is enqueued so it can take time
|
||||
await jasmine.timeout(1000);
|
||||
@@ -580,6 +576,9 @@ describe('PushController', () => {
|
||||
return ['ios'];
|
||||
},
|
||||
};
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
// $ins is invalid query
|
||||
const where = {
|
||||
channels: {
|
||||
@@ -596,9 +595,6 @@ describe('PushController', () => {
|
||||
isMaster: true,
|
||||
};
|
||||
const pushController = new PushController();
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
const config = Config.get(Parse.applicationId);
|
||||
try {
|
||||
await pushController.sendPush(payload, where, config, auth);
|
||||
@@ -631,6 +627,9 @@ describe('PushController', () => {
|
||||
return ['ios'];
|
||||
},
|
||||
};
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
@@ -641,9 +640,6 @@ describe('PushController', () => {
|
||||
$in: ['device_token_0', 'device_token_1', 'device_token_2'],
|
||||
},
|
||||
};
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
const installations = [];
|
||||
while (installations.length != 5) {
|
||||
const installation = new Parse.Object('_Installation');
|
||||
@@ -678,7 +674,9 @@ describe('PushController', () => {
|
||||
return ['ios'];
|
||||
},
|
||||
};
|
||||
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
@@ -686,9 +684,6 @@ describe('PushController', () => {
|
||||
const where = {
|
||||
deviceType: 'ios',
|
||||
};
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
const installations = [];
|
||||
while (installations.length != 5) {
|
||||
const installation = new Parse.Object('_Installation');
|
||||
@@ -762,10 +757,6 @@ describe('PushController', () => {
|
||||
});
|
||||
|
||||
it('should not schedule push when not configured', async () => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
};
|
||||
const pushAdapter = {
|
||||
send: function (body, installations) {
|
||||
return successfulTransmissions(body, installations);
|
||||
@@ -774,7 +765,13 @@ describe('PushController', () => {
|
||||
return ['ios'];
|
||||
},
|
||||
};
|
||||
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
};
|
||||
const pushController = new PushController();
|
||||
const payload = {
|
||||
data: {
|
||||
@@ -793,10 +790,6 @@ describe('PushController', () => {
|
||||
installation.set('deviceType', 'ios');
|
||||
installations.push(installation);
|
||||
}
|
||||
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
await Parse.Object.saveAll(installations);
|
||||
await pushController.sendPush(payload, {}, config, auth);
|
||||
await jasmine.timeout(1000);
|
||||
@@ -986,6 +979,10 @@ describe('PushController', () => {
|
||||
return ['ios'];
|
||||
},
|
||||
};
|
||||
spyOn(pushAdapter, 'send').and.callThrough();
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
@@ -1007,10 +1004,6 @@ describe('PushController', () => {
|
||||
installations[1].set('localeIdentifier', 'fr-FR');
|
||||
installations[2].set('localeIdentifier', 'en-US');
|
||||
|
||||
spyOn(pushAdapter, 'send').and.callThrough();
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
await Parse.Object.saveAll(installations);
|
||||
const pushStatusId = await sendPush(payload, where, config, auth);
|
||||
await pushCompleted(pushStatusId);
|
||||
@@ -1039,7 +1032,10 @@ describe('PushController', () => {
|
||||
return ['ios'];
|
||||
},
|
||||
};
|
||||
|
||||
spyOn(pushAdapter, 'send').and.callThrough();
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const auth = {
|
||||
isMaster: true,
|
||||
@@ -1060,10 +1056,6 @@ describe('PushController', () => {
|
||||
installation.set('deviceType', 'ios');
|
||||
installations.push(installation);
|
||||
}
|
||||
spyOn(pushAdapter, 'send').and.callThrough();
|
||||
await reconfigureServer({
|
||||
push: { adapter: pushAdapter },
|
||||
});
|
||||
await Parse.Object.saveAll(installations);
|
||||
|
||||
// Create an audience
|
||||
|
||||
@@ -5,10 +5,8 @@ describe('Schema Performance', function () {
|
||||
let config;
|
||||
|
||||
beforeEach(async () => {
|
||||
await reconfigureServer();
|
||||
config = Config.get('test');
|
||||
config.schemaCache.clear();
|
||||
const databaseAdapter = config.database.adapter;
|
||||
await reconfigureServer({ databaseAdapter });
|
||||
getAllSpy = spyOn(databaseAdapter, 'getAllClasses').and.callThrough();
|
||||
});
|
||||
|
||||
|
||||
@@ -67,18 +67,22 @@ describe('Security Check Groups', () => {
|
||||
|
||||
it('checks succeed correctly', async () => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const uri = config.database.adapter._uri;
|
||||
config.database.adapter._uri = 'protocol://user:aMoreSecur3Passwor7!@example.com';
|
||||
const group = new CheckGroupDatabase();
|
||||
await group.run();
|
||||
expect(group.checks()[0].checkState()).toBe(CheckState.success);
|
||||
config.database.adapter._uri = uri;
|
||||
});
|
||||
|
||||
it('checks fail correctly', async () => {
|
||||
const config = Config.get(Parse.applicationId);
|
||||
const uri = config.database.adapter._uri;
|
||||
config.database.adapter._uri = 'protocol://user:insecure@example.com';
|
||||
const group = new CheckGroupDatabase();
|
||||
await group.run();
|
||||
expect(group.checks()[0].checkState()).toBe(CheckState.fail);
|
||||
config.database.adapter._uri = uri;
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -366,7 +366,6 @@ describe('batch', () => {
|
||||
});
|
||||
|
||||
it('should generate separate session for each call', async () => {
|
||||
await reconfigureServer();
|
||||
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
|
||||
await myObject.save({ key: 'stringField' });
|
||||
await myObject.destroy();
|
||||
|
||||
@@ -36,6 +36,7 @@ module.exports = [
|
||||
describe_only_db: "readonly",
|
||||
fdescribe_only_db: "readonly",
|
||||
describe_only: "readonly",
|
||||
fdescribe_only: "readonly",
|
||||
on_db: "readonly",
|
||||
defaultConfiguration: "readonly",
|
||||
range: "readonly",
|
||||
|
||||
102
spec/helper.js
102
spec/helper.js
@@ -1,9 +1,11 @@
|
||||
'use strict';
|
||||
const dns = require('dns');
|
||||
const semver = require('semver');
|
||||
const Parse = require('parse/node');
|
||||
const CurrentSpecReporter = require('./support/CurrentSpecReporter.js');
|
||||
const { SpecReporter } = require('jasmine-spec-reporter');
|
||||
const SchemaCache = require('../lib/Adapters/Cache/SchemaCache').default;
|
||||
const { sleep, Connections } = require('../lib/TestUtils');
|
||||
|
||||
// Ensure localhost resolves to ipv4 address first on node v17+
|
||||
if (dns.setDefaultResultOrder) {
|
||||
@@ -53,7 +55,6 @@ const mongoURI = 'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase'
|
||||
const postgresURI = 'postgres://localhost:5432/parse_server_postgres_adapter_test_database';
|
||||
let databaseAdapter;
|
||||
let databaseURI;
|
||||
// need to bind for mocking mocha
|
||||
|
||||
if (process.env.PARSE_SERVER_DATABASE_ADAPTER) {
|
||||
databaseAdapter = JSON.parse(process.env.PARSE_SERVER_DATABASE_ADAPTER);
|
||||
@@ -73,7 +74,7 @@ if (process.env.PARSE_SERVER_DATABASE_ADAPTER) {
|
||||
}
|
||||
|
||||
const port = 8378;
|
||||
|
||||
const serverURL = `http://localhost:${port}/1`;
|
||||
let filesAdapter;
|
||||
|
||||
on_db(
|
||||
@@ -99,7 +100,7 @@ if (process.env.PARSE_SERVER_LOG_LEVEL) {
|
||||
// Default server configuration for tests.
|
||||
const defaultConfiguration = {
|
||||
filesAdapter,
|
||||
serverURL: 'http://localhost:' + port + '/1',
|
||||
serverURL,
|
||||
databaseAdapter,
|
||||
appId: 'test',
|
||||
javascriptKey: 'test',
|
||||
@@ -153,34 +154,38 @@ if (silent) {
|
||||
};
|
||||
}
|
||||
|
||||
if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') {
|
||||
defaultConfiguration.cacheAdapter = new RedisCacheAdapter();
|
||||
}
|
||||
|
||||
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 parseServer;
|
||||
let didChangeConfiguration = false;
|
||||
const openConnections = new Connections();
|
||||
|
||||
const shutdownServer = async (_parseServer) => {
|
||||
await _parseServer.handleShutdown();
|
||||
// Connection close events are not immediate on node 10+, so wait a bit
|
||||
await sleep(0);
|
||||
expect(openConnections.count() > 0).toBeFalsy(`There were ${openConnections.count()} open connections to the server left after the test finished`);
|
||||
parseServer = undefined;
|
||||
};
|
||||
|
||||
// Allows testing specific configurations of Parse Server
|
||||
const reconfigureServer = async (changedConfiguration = {}) => {
|
||||
if (parseServer) {
|
||||
destroyAliveConnections();
|
||||
await new Promise(resolve => parseServer.server.close(resolve));
|
||||
parseServer = undefined;
|
||||
await shutdownServer(parseServer);
|
||||
return reconfigureServer(changedConfiguration);
|
||||
}
|
||||
didChangeConfiguration = Object.keys(changedConfiguration).length !== 0;
|
||||
databaseAdapter = new databaseAdapter.constructor({
|
||||
uri: databaseURI,
|
||||
collectionPrefix: 'test_',
|
||||
});
|
||||
defaultConfiguration.databaseAdapter = databaseAdapter;
|
||||
global.databaseAdapter = databaseAdapter;
|
||||
if (filesAdapter instanceof GridFSBucketAdapter) {
|
||||
defaultConfiguration.filesAdapter = new GridFSBucketAdapter(mongoURI);
|
||||
}
|
||||
if (process.env.PARSE_SERVER_TEST_CACHE === 'redis') {
|
||||
defaultConfiguration.cacheAdapter = new RedisCacheAdapter();
|
||||
}
|
||||
const newConfiguration = Object.assign({}, defaultConfiguration, changedConfiguration, {
|
||||
mountPath: '/1',
|
||||
port,
|
||||
@@ -192,39 +197,19 @@ const reconfigureServer = async (changedConfiguration = {}) => {
|
||||
console.error(err);
|
||||
fail('should not call next');
|
||||
});
|
||||
parseServer.liveQueryServer?.server?.on('connection', connection => {
|
||||
const key = `${connection.remoteAddress}:${connection.remotePort}`;
|
||||
openConnections[key] = connection;
|
||||
connection.on('close', () => {
|
||||
delete openConnections[key];
|
||||
});
|
||||
});
|
||||
parseServer.server.on('connection', connection => {
|
||||
const key = `${connection.remoteAddress}:${connection.remotePort}`;
|
||||
openConnections[key] = connection;
|
||||
connection.on('close', () => {
|
||||
delete openConnections[key];
|
||||
});
|
||||
});
|
||||
openConnections.track(parseServer.server);
|
||||
if (parseServer.liveQueryServer?.server && parseServer.liveQueryServer.server !== parseServer.server) {
|
||||
openConnections.track(parseServer.liveQueryServer.server);
|
||||
}
|
||||
return parseServer;
|
||||
};
|
||||
|
||||
// Set up a Parse client to talk to our test API server
|
||||
const Parse = require('parse/node');
|
||||
Parse.serverURL = 'http://localhost:' + port + '/1';
|
||||
|
||||
beforeAll(async () => {
|
||||
try {
|
||||
Parse.User.enableUnsafeCurrentUser();
|
||||
} catch (error) {
|
||||
if (error !== 'You need to call Parse.initialize before using Parse.') {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
await reconfigureServer();
|
||||
|
||||
Parse.initialize('test', 'test', 'test');
|
||||
Parse.serverURL = 'http://localhost:' + port + '/1';
|
||||
Parse.serverURL = serverURL;
|
||||
Parse.User.enableUnsafeCurrentUser();
|
||||
Parse.CoreManager.set('REQUEST_ATTEMPT_LIMIT', 1);
|
||||
});
|
||||
|
||||
global.afterEachFn = async () => {
|
||||
@@ -253,19 +238,7 @@ global.afterEachFn = async () => {
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
await Parse.User.logOut().catch(() => {});
|
||||
|
||||
// Connection close events are not immediate on node 10+, so wait a bit
|
||||
await new Promise(resolve => setTimeout(resolve, 0));
|
||||
|
||||
// After logout operations
|
||||
if (Object.keys(openConnections).length > 1) {
|
||||
console.warn(
|
||||
`There were ${Object.keys(openConnections).length} open connections to the server left after the test finished`
|
||||
);
|
||||
}
|
||||
|
||||
await TestUtils.destroyAllDataPermanently(true);
|
||||
SchemaCache.clear();
|
||||
|
||||
@@ -454,6 +427,7 @@ global.mockCustomAuthenticator = mockCustomAuthenticator;
|
||||
global.mockFacebookAuthenticator = mockFacebookAuthenticator;
|
||||
global.databaseAdapter = databaseAdapter;
|
||||
global.databaseURI = databaseURI;
|
||||
global.shutdownServer = shutdownServer;
|
||||
global.jfail = function (err) {
|
||||
fail(JSON.stringify(err));
|
||||
};
|
||||
@@ -610,6 +584,14 @@ global.describe_only = validator => {
|
||||
}
|
||||
};
|
||||
|
||||
global.fdescribe_only = validator => {
|
||||
if (validator()) {
|
||||
return fdescribe;
|
||||
} else {
|
||||
return xdescribe;
|
||||
}
|
||||
};
|
||||
|
||||
const libraryCache = {};
|
||||
jasmine.mockLibrary = function (library, name, mock) {
|
||||
const original = require(library)[name];
|
||||
|
||||
@@ -62,6 +62,7 @@ describe('server', () => {
|
||||
});
|
||||
|
||||
it('fails if database is unreachable', async () => {
|
||||
spyOn(console, 'error').and.callFake(() => {});
|
||||
const server = new ParseServer.default({
|
||||
...defaultConfiguration,
|
||||
databaseAdapter: new MongoStorageAdapter({
|
||||
@@ -145,7 +146,7 @@ describe('server', () => {
|
||||
},
|
||||
publicServerURL: 'http://localhost:8378/1',
|
||||
};
|
||||
expectAsync(reconfigureServer(options)).toBeRejected('MockMailAdapterConstructor');
|
||||
await expectAsync(reconfigureServer(options)).toBeRejected('MockMailAdapterConstructor');
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -20,6 +20,8 @@ const flakyTests = [
|
||||
"UserController sendVerificationEmail parseFrameURL provided uses parseFrameURL and includes the destination in the link parameter",
|
||||
// Expected undefined to be defined
|
||||
"Email Verification Token Expiration: sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp",
|
||||
// Expected 0 to be 1.
|
||||
"Email Verification Token Expiration: should send a new verification email when a resend is requested and the user is UNVERIFIED",
|
||||
];
|
||||
|
||||
/** The minimum execution time in seconds for a test to be considered slow. */
|
||||
|
||||
Reference in New Issue
Block a user