Skip to content

Commit 89de779

Browse files
committed
fix: Parse server doesn't shutdown gracefully
1 parent 4f55c3e commit 89de779

19 files changed

+310
-237
lines changed

spec/EnableExpressErrorHandler.spec.js

+3-5
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,9 @@ const request = require('../lib/request');
33
describe('Enable express error handler', () => {
44
it('should call the default handler in case of error, like updating a non existing object', async done => {
55
spyOn(console, 'error');
6-
const parseServer = await reconfigureServer(
7-
Object.assign({}, defaultConfiguration, {
8-
enableExpressErrorHandler: true,
9-
})
10-
);
6+
const parseServer = await reconfigureServer({
7+
enableExpressErrorHandler: true,
8+
});
119
parseServer.app.use(function (err, req, res, next) {
1210
expect(err.message).toBe('Object not found.');
1311
next(err);

spec/ParseAPI.spec.js

-2
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,7 @@ describe('miscellaneous', () => {
3333
expect(results.length).toEqual(1);
3434
expect(results[0]['foo']).toEqual('bar');
3535
});
36-
});
3736

38-
describe('miscellaneous', function () {
3937
it('create a GameScore object', function (done) {
4038
const obj = new Parse.Object('GameScore');
4139
obj.set('score', 1337);

spec/ParseConfigKey.spec.js

+1-5
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ describe('Config Keys', () => {
1212

1313
it('recognizes invalid keys in root', async () => {
1414
await expectAsync(reconfigureServer({
15-
...defaultConfiguration,
1615
invalidKey: 1,
1716
})).toBeResolved();
1817
const error = loggerErrorSpy.calls.all().reduce((s, call) => s += call.args[0], '');
@@ -21,7 +20,6 @@ describe('Config Keys', () => {
2120

2221
it('recognizes invalid keys in pages.customUrls', async () => {
2322
await expectAsync(reconfigureServer({
24-
...defaultConfiguration,
2523
pages: {
2624
customUrls: {
2725
invalidKey: 1,
@@ -37,7 +35,6 @@ describe('Config Keys', () => {
3735

3836
it('recognizes invalid keys in liveQueryServerOptions', async () => {
3937
await expectAsync(reconfigureServer({
40-
...defaultConfiguration,
4138
liveQueryServerOptions: {
4239
invalidKey: 1,
4340
MasterKey: 1,
@@ -50,7 +47,6 @@ describe('Config Keys', () => {
5047

5148
it('recognizes invalid keys in rateLimit', async () => {
5249
await expectAsync(reconfigureServer({
53-
...defaultConfiguration,
5450
rateLimit: [
5551
{ invalidKey: 1 },
5652
{ RequestPath: 1 },
@@ -64,7 +60,7 @@ describe('Config Keys', () => {
6460
expect(error).toMatch('rateLimit\\[2\\]\\.RequestTimeWindow');
6561
});
6662

67-
it('recognizes valid keys in default configuration', async () => {
63+
it_only_db('mongo')('recognizes valid keys in default configuration', async () => {
6864
await expectAsync(reconfigureServer({
6965
...defaultConfiguration,
7066
})).toBeResolved();

spec/ParseGraphQLServer.spec.js

+33-14
Original file line numberDiff line numberDiff line change
@@ -431,17 +431,32 @@ describe('ParseGraphQLServer', () => {
431431
objects.push(object1, object2, object3, object4);
432432
}
433433

434-
beforeEach(async () => {
434+
async function createGQLFromParseServer(_parseServer) {
435+
if (parseLiveQueryServer) {
436+
await parseLiveQueryServer.server.close();
437+
}
438+
if (httpServer) {
439+
await httpServer.close();
440+
}
435441
const expressApp = express();
436442
httpServer = http.createServer(expressApp);
437-
expressApp.use('/parse', parseServer.app);
443+
expressApp.use('/parse', _parseServer.app);
438444
parseLiveQueryServer = await ParseServer.createLiveQueryServer(httpServer, {
439445
port: 1338,
440446
});
447+
parseGraphQLServer = new ParseGraphQLServer(_parseServer, {
448+
graphQLPath: '/graphql',
449+
playgroundPath: '/playground',
450+
subscriptionsPath: '/subscriptions',
451+
});
441452
parseGraphQLServer.applyGraphQL(expressApp);
442453
parseGraphQLServer.applyPlayground(expressApp);
443454
parseGraphQLServer.createSubscriptions(httpServer);
444455
await new Promise(resolve => httpServer.listen({ port: 13377 }, resolve));
456+
}
457+
458+
beforeEach(async () => {
459+
await createGQLFromParseServer(parseServer);
445460

446461
const subscriptionClient = new SubscriptionClient(
447462
'ws://localhost:13377/subscriptions',
@@ -753,10 +768,6 @@ describe('ParseGraphQLServer', () => {
753768
}
754769
});
755770

756-
afterAll(async () => {
757-
await resetGraphQLCache();
758-
});
759-
760771
it('should have Node interface', async () => {
761772
const schemaTypes = (
762773
await apolloClient.query({
@@ -2821,7 +2832,8 @@ describe('ParseGraphQLServer', () => {
28212832
}
28222833
});
28232834
it('Id inputs should work either with global id or object id with objectId higher than 19', async () => {
2824-
await reconfigureServer({ objectIdSize: 20 });
2835+
const parseServer = await reconfigureServer({ objectIdSize: 20 });
2836+
await createGQLFromParseServer(parseServer);
28252837
const obj = new Parse.Object('SomeClass');
28262838
await obj.save({ name: 'aname', type: 'robot' });
28272839
const result = await apolloClient.query({
@@ -5328,7 +5340,7 @@ describe('ParseGraphQLServer', () => {
53285340
parseServer = await global.reconfigureServer({
53295341
maxLimit: 10,
53305342
});
5331-
5343+
await createGQLFromParseServer(parseServer);
53325344
const promises = [];
53335345
for (let i = 0; i < 100; i++) {
53345346
const obj = new Parse.Object('SomeClass');
@@ -6841,7 +6853,7 @@ describe('ParseGraphQLServer', () => {
68416853
parseServer = await global.reconfigureServer({
68426854
publicServerURL: 'http://localhost:13377/parse',
68436855
});
6844-
6856+
await createGQLFromParseServer(parseServer);
68456857
const body = new FormData();
68466858
body.append(
68476859
'operations',
@@ -7049,6 +7061,7 @@ describe('ParseGraphQLServer', () => {
70497061
challengeAdapter,
70507062
},
70517063
});
7064+
await createGQLFromParseServer(parseServer);
70527065
const clientMutationId = uuidv4();
70537066

70547067
const result = await apolloClient.mutate({
@@ -7095,6 +7108,7 @@ describe('ParseGraphQLServer', () => {
70957108
challengeAdapter,
70967109
},
70977110
});
7111+
await createGQLFromParseServer(parseServer);
70987112
const clientMutationId = uuidv4();
70997113
const userSchema = new Parse.Schema('_User');
71007114
userSchema.addString('someField');
@@ -7169,7 +7183,7 @@ describe('ParseGraphQLServer', () => {
71697183
},
71707184
},
71717185
});
7172-
7186+
await createGQLFromParseServer(parseServer);
71737187
userSchema.addString('someField');
71747188
userSchema.addPointer('aPointer', '_User');
71757189
await userSchema.update();
@@ -7239,7 +7253,7 @@ describe('ParseGraphQLServer', () => {
72397253
challengeAdapter,
72407254
},
72417255
});
7242-
7256+
await createGQLFromParseServer(parseServer);
72437257
const user = new Parse.User();
72447258
await user.save({ username: 'username', password: 'password' });
72457259

@@ -7310,6 +7324,7 @@ describe('ParseGraphQLServer', () => {
73107324
challengeAdapter,
73117325
},
73127326
});
7327+
await createGQLFromParseServer(parseServer);
73137328
const clientMutationId = uuidv4();
73147329
const user = new Parse.User();
73157330
user.setUsername('user1');
@@ -7441,6 +7456,7 @@ describe('ParseGraphQLServer', () => {
74417456
emailAdapter: emailAdapter,
74427457
publicServerURL: 'http://test.test',
74437458
});
7459+
await createGQLFromParseServer(parseServer);
74447460
const user = new Parse.User();
74457461
user.setUsername('user1');
74467462
user.setPassword('user1');
@@ -7488,6 +7504,7 @@ describe('ParseGraphQLServer', () => {
74887504
},
74897505
},
74907506
});
7507+
await createGQLFromParseServer(parseServer);
74917508
const user = new Parse.User();
74927509
user.setUsername('user1');
74937510
user.setPassword('user1');
@@ -7550,6 +7567,7 @@ describe('ParseGraphQLServer', () => {
75507567
emailAdapter: emailAdapter,
75517568
publicServerURL: 'http://test.test',
75527569
});
7570+
await createGQLFromParseServer(parseServer);
75537571
const user = new Parse.User();
75547572
user.setUsername('user1');
75557573
user.setPassword('user1');
@@ -9306,7 +9324,7 @@ describe('ParseGraphQLServer', () => {
93069324
parseServer = await global.reconfigureServer({
93079325
publicServerURL: 'http://localhost:13377/parse',
93089326
});
9309-
9327+
await createGQLFromParseServer(parseServer);
93109328
const body = new FormData();
93119329
body.append(
93129330
'operations',
@@ -9339,7 +9357,6 @@ describe('ParseGraphQLServer', () => {
93399357
headers,
93409358
body,
93419359
});
9342-
93439360
expect(res.status).toEqual(200);
93449361

93459362
const result = JSON.parse(await res.text());
@@ -9553,6 +9570,7 @@ describe('ParseGraphQLServer', () => {
95539570
parseServer = await global.reconfigureServer({
95549571
publicServerURL: 'http://localhost:13377/parse',
95559572
});
9573+
await createGQLFromParseServer(parseServer);
95569574
const schemaController = await parseServer.config.databaseController.loadSchema();
95579575
await schemaController.addClassIfNotExists('SomeClassWithRequiredFile', {
95589576
someField: { type: 'File', required: true },
@@ -9617,6 +9635,7 @@ describe('ParseGraphQLServer', () => {
96179635
parseServer = await global.reconfigureServer({
96189636
publicServerURL: 'http://localhost:13377/parse',
96199637
});
9638+
await createGQLFromParseServer(parseServer);
96209639
const schema = new Parse.Schema('SomeClass');
96219640
schema.addFile('someFileField');
96229641
schema.addPointer('somePointerField', 'SomeClass');
@@ -9725,7 +9744,7 @@ describe('ParseGraphQLServer', () => {
97259744
parseServer = await global.reconfigureServer({
97269745
publicServerURL: 'http://localhost:13377/parse',
97279746
});
9728-
9747+
await createGQLFromParseServer(parseServer);
97299748
const body = new FormData();
97309749
body.append(
97319750
'operations',

spec/ParseLiveQuery.spec.js

+75-1
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
'use strict';
2+
const http = require('http');
23
const Auth = require('../lib/Auth');
34
const UserController = require('../lib/Controllers/UserController').UserController;
45
const Config = require('../lib/Config');
56
const ParseServer = require('../lib/index').ParseServer;
67
const triggers = require('../lib/triggers');
7-
const { resolvingPromise, sleep } = require('../lib/TestUtils');
8+
const { resolvingPromise, sleep, getConnectionsCount } = require('../lib/TestUtils');
9+
const request = require('../lib/request');
810
const validatorFail = () => {
911
throw 'you are not authorized';
1012
};
@@ -1181,6 +1183,78 @@ describe('ParseLiveQuery', function () {
11811183
await new Promise(resolve => server.server.close(resolve));
11821184
});
11831185

1186+
it_id('45655b74-716f-4fa1-a058-67eb21f3c3db')(it)('does shutdown separate liveQuery server', async () => {
1187+
await reconfigureServer({ appId: 'test_app_id' });
1188+
let close = false;
1189+
const config = {
1190+
appId: 'hello_test',
1191+
masterKey: 'world',
1192+
port: 1345,
1193+
mountPath: '/1',
1194+
serverURL: 'http://localhost:1345/1',
1195+
liveQuery: {
1196+
classNames: ['Yolo'],
1197+
},
1198+
startLiveQueryServer: true,
1199+
verbose: false,
1200+
silent: true,
1201+
liveQueryServerOptions: {
1202+
port: 1346,
1203+
},
1204+
serverCloseComplete: () => {
1205+
close = true;
1206+
},
1207+
};
1208+
if (process.env.PARSE_SERVER_TEST_DB === 'postgres') {
1209+
config.databaseAdapter = new databaseAdapter.constructor({
1210+
uri: databaseURI,
1211+
collectionPrefix: 'test_',
1212+
});
1213+
config.filesAdapter = defaultConfiguration.filesAdapter;
1214+
}
1215+
const parseServer = await ParseServer.startApp(config);
1216+
expect(parseServer.liveQueryServer).toBeDefined();
1217+
expect(parseServer.liveQueryServer.server).not.toBe(parseServer.server);
1218+
1219+
// Open a connection to the liveQuery server
1220+
const client = await Parse.CoreManager.getLiveQueryController().getDefaultLiveQueryClient();
1221+
client.serverURL = 'ws://localhost:1346/1';
1222+
const query = await new Parse.Query('Yolo').subscribe();
1223+
1224+
// Open a connection to the parse server
1225+
const health = await request({
1226+
method: 'GET',
1227+
url: `http://localhost:1345/1/health`,
1228+
json: true,
1229+
headers: {
1230+
'X-Parse-Application-Id': 'hello_test',
1231+
'X-Parse-Master-Key': 'world',
1232+
'Content-Type': 'application/json',
1233+
},
1234+
agent: new http.Agent({ keepAlive: true }),
1235+
}).then(res => res.data);
1236+
expect(health.status).toBe('ok');
1237+
1238+
let parseConnectionCount = await getConnectionsCount(parseServer.server);
1239+
let liveQueryConnectionCount = await getConnectionsCount(parseServer.liveQueryServer.server);
1240+
1241+
expect(parseConnectionCount > 0).toBe(true);
1242+
expect(liveQueryConnectionCount > 0).toBe(true);
1243+
await Promise.all([
1244+
parseServer.handleShutdown(),
1245+
new Promise(resolve => query.on('close', resolve)),
1246+
]);
1247+
expect(close).toBe(true);
1248+
await new Promise(resolve => setTimeout(resolve, 100));
1249+
expect(parseServer.liveQueryServer.server.address()).toBeNull();
1250+
expect(parseServer.liveQueryServer.subscriber.isOpen).toBeFalse();
1251+
1252+
parseConnectionCount = await getConnectionsCount(parseServer.server);
1253+
liveQueryConnectionCount = await getConnectionsCount(parseServer.liveQueryServer.server);
1254+
expect(parseConnectionCount).toBe(0);
1255+
expect(liveQueryConnectionCount).toBe(0);
1256+
});
1257+
11841258
it('prevent afterSave trigger if not exists', async () => {
11851259
await reconfigureServer({
11861260
liveQuery: {

0 commit comments

Comments
 (0)