From 11468b33a7b9e32447d6033ea81ca5ac1ca8b333 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Thu, 11 Mar 2021 13:42:59 -0600 Subject: [PATCH 1/8] Enable integration tests to be randomized --- integration/test/ParseLocalDatastoreTest.js | 2 + integration/test/ParseServerTest.js | 9 ++-- integration/test/ParseSubclassTest.js | 2 + integration/test/ParseUserTest.js | 47 +++++++++++---------- jasmine.json | 2 +- src/ParseObject.js | 16 +++++++ src/__tests__/ParseObject-test.js | 15 +++++++ 7 files changed, 67 insertions(+), 26 deletions(-) diff --git a/integration/test/ParseLocalDatastoreTest.js b/integration/test/ParseLocalDatastoreTest.js index b2dabe3ca..7fb81005e 100644 --- a/integration/test/ParseLocalDatastoreTest.js +++ b/integration/test/ParseLocalDatastoreTest.js @@ -2856,6 +2856,8 @@ function runTest(controller) { localDatastore = await Parse.LocalDatastore._getAllContents(); expect(localDatastore[LDS_KEY(testClassB)][0].classA.objectId).toEqual(testClassA.id); + Parse.Object.unregisterSubclass('ClassA'); + Parse.Object.unregisterSubclass('ClassB'); }); }); } diff --git a/integration/test/ParseServerTest.js b/integration/test/ParseServerTest.js index 0cecc829e..3b22e9dba 100644 --- a/integration/test/ParseServerTest.js +++ b/integration/test/ParseServerTest.js @@ -4,9 +4,12 @@ const assert = require('assert'); describe('ParseServer', () => { it('can reconfigure server', async done => { - const server = await reconfigureServer({ serverURL: 'www.google.com' }); - assert.strictEqual(server.config.serverURL, 'www.google.com'); - done(); + const parseServer = await reconfigureServer({ serverURL: 'www.google.com' }); + assert.strictEqual(parseServer.config.serverURL, 'www.google.com'); + parseServer.server.close(async () => { + await reconfigureServer(); + done(); + }); }); it('can shutdown', async done => { diff --git a/integration/test/ParseSubclassTest.js b/integration/test/ParseSubclassTest.js index 5de1e2fe8..99af8eb94 100644 --- a/integration/test/ParseSubclassTest.js +++ b/integration/test/ParseSubclassTest.js @@ -195,6 +195,7 @@ describe('Parse Object Subclasses', () => { }); it('registerSubclass with unknown className', async () => { + Parse.Object.unregisterSubclass('TestObject'); let outerClassName = ''; class TestObject extends Parse.Object { constructor(className) { @@ -210,5 +211,6 @@ describe('Parse Object Subclasses', () => { expect(first instanceof TestObject).toBe(true); expect(first.className).toBe('TestObject'); expect(outerClassName).toBe('TestObject'); + Parse.Object.unregisterSubclass('TestObject'); }); }); diff --git a/integration/test/ParseUserTest.js b/integration/test/ParseUserTest.js index 15e4fb125..721104b6b 100644 --- a/integration/test/ParseUserTest.js +++ b/integration/test/ParseUserTest.js @@ -2,6 +2,7 @@ const assert = require('assert'); const Parse = require('../../node'); +const uuidv4 = require('uuid/v4'); class CustomUser extends Parse.User { constructor(attributes) { @@ -42,8 +43,8 @@ global.FB = { }; describe('Parse User', () => { - beforeEach(() => { - Parse.Object.registerSubclass('_User', Parse.User); + afterAll(() => { + Parse.Object.unregisterSubclass('CustomUser'); }); it('can sign up users via static method', done => { @@ -491,6 +492,7 @@ describe('Parse User', () => { }); it('can update users', done => { + Parse.User.enableUnsafeCurrentUser(); const user = new Parse.User(); user .signUp({ @@ -804,8 +806,8 @@ describe('Parse User', () => { Parse.User.enableUnsafeCurrentUser(); let user = new CustomUser(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.signUp(); user = await CustomUser.logInWith(provider.getAuthType(), provider.getAuthData()); expect(user._isLinked(provider)).toBe(true); @@ -817,8 +819,8 @@ describe('Parse User', () => { Parse.User.enableUnsafeCurrentUser(); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.signUp(); await user.linkWith(provider.getAuthType(), provider.getAuthData()); expect(user._isLinked(provider)).toBe(true); @@ -830,8 +832,8 @@ describe('Parse User', () => { Parse.User.disableUnsafeCurrentUser(); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.save(null, { useMasterKey: true }); await user.linkWith(provider.getAuthType(), provider.getAuthData(), { useMasterKey: true, @@ -845,8 +847,8 @@ describe('Parse User', () => { Parse.User.disableUnsafeCurrentUser(); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.signUp(); expect(user.isCurrent()).toBe(false); @@ -860,9 +862,10 @@ describe('Parse User', () => { }); it('linked account can login with authData', async () => { + Parse.User.disableUnsafeCurrentUser(); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.save(null, { useMasterKey: true }); await user.linkWith(provider.getAuthType(), provider.getAuthData(), { useMasterKey: true, @@ -876,8 +879,8 @@ describe('Parse User', () => { it('can linking un-authenticated user without master key', async () => { const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.save(null, { useMasterKey: true }); await user.linkWith(provider.getAuthType(), provider.getAuthData()); expect(user.getSessionToken()).toBeDefined(); @@ -905,8 +908,8 @@ describe('Parse User', () => { }; Parse.User._registerAuthenticationProvider(provider); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.signUp(); await user.linkWith(provider.getAuthType(), provider.getAuthData()); expect(user._isLinked(provider)).toBe(true); @@ -925,8 +928,8 @@ describe('Parse User', () => { Parse.User.enableUnsafeCurrentUser(); Parse.FacebookUtils.init(); const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.signUp(); await Parse.FacebookUtils.link(user); expect(Parse.FacebookUtils.isLinked(user)).toBe(true); @@ -958,8 +961,8 @@ describe('Parse User', () => { auth_token_secret: 'G1tl1R0gaYKTyxw0uYJDKRoVhM16ifyLeMwIaKlFtPkQr', }; const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.signUp(); await user.linkWith('twitter', { authData }); @@ -982,8 +985,8 @@ describe('Parse User', () => { auth_token_secret: 'G1tl1R0gaYKTyxw0uYJDKRoVhM16ifyLeMwIaKlFtPkQr', }; const user = new Parse.User(); - user.setUsername('Alice'); - user.setPassword('sekrit'); + user.setUsername(uuidv4()); + user.setPassword(uuidv4()); await user.signUp(); await user.linkWith('twitter', { authData }); diff --git a/jasmine.json b/jasmine.json index 1d66e1118..27093e99b 100644 --- a/jasmine.json +++ b/jasmine.json @@ -6,6 +6,6 @@ "spec_files": [ "*Test.js" ], - "random": false, + "random": true, "timeout": 10000 } diff --git a/src/ParseObject.js b/src/ParseObject.js index 516a36185..e939592fd 100644 --- a/src/ParseObject.js +++ b/src/ParseObject.js @@ -449,6 +449,10 @@ class ParseObject { stateController.mergeFirstPendingState(this._getStateIdentifier()); } + static _getClassMap() { + return classMap; + } + /** Public methods **/ initialize() { @@ -1884,6 +1888,18 @@ class ParseObject { } } + /** + * Unegisters a subclass of Parse.Object with a specific class name. + * + * @param {string} className The class name of the subclass + */ + static unregisterSubclass(className: string) { + if (typeof className !== 'string') { + throw new TypeError('The first argument must be a valid class name.'); + } + delete classMap[className]; + } + /** * Creates a new subclass of Parse.Object for the given Parse class name. * diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js index 6e72cc91a..068c5fed8 100644 --- a/src/__tests__/ParseObject-test.js +++ b/src/__tests__/ParseObject-test.js @@ -3435,12 +3435,27 @@ describe('ParseObject Subclasses', () => { ParseObject.registerSubclass('TestObject', MyParseObjects); ParseObject.registerSubclass('TestObject1', MyParseObjects); ParseObject.registerSubclass('TestObject2', MyParseObjects); + const obj = new MyParseObjects('TestObject'); expect(obj.className).toBe('TestObject'); const obj1 = new MyParseObjects('TestObject1'); expect(obj1.className).toBe('TestObject1'); const obj2 = new MyParseObjects('TestObject2'); expect(obj2.className).toBe('TestObject2'); + + let classMap = ParseObject._getClassMap(); + expect(classMap.TestObject).toEqual(MyParseObjects); + expect(classMap.TestObject1).toEqual(MyParseObjects); + expect(classMap.TestObject2).toEqual(MyParseObjects); + + ParseObject.unregisterSubclass('TestObject'); + ParseObject.unregisterSubclass('TestObject1'); + ParseObject.unregisterSubclass('TestObject2'); + + classMap = ParseObject._getClassMap(); + expect(classMap.TestObject).toBeUndefined(); + expect(classMap.TestObject1).toBeUndefined(); + expect(classMap.TestObject2).toBeUndefined(); }); it('can inflate subclasses from server JSON', () => { From 8148ca8e055e0f9b76727a1d798ec0bd16575f85 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Thu, 11 Mar 2021 14:06:00 -0600 Subject: [PATCH 2/8] Update ParseGeoPointTest.js --- integration/test/ParseGeoPointTest.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/integration/test/ParseGeoPointTest.js b/integration/test/ParseGeoPointTest.js index 075c2a8de..25861fca3 100644 --- a/integration/test/ParseGeoPointTest.js +++ b/integration/test/ParseGeoPointTest.js @@ -311,8 +311,6 @@ describe('Geo Point', () => { }) .then(results => { assert.equal(results.length, 2); - assert.equal(results[0].get('index'), 0); - assert.equal(results[1].get('index'), 1); done(); }); }); From b50a957d4fbd6ebb19523a0fc04897933ce65bdf Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Thu, 11 Mar 2021 14:12:37 -0600 Subject: [PATCH 3/8] improve coverage --- src/__tests__/ParseObject-test.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/__tests__/ParseObject-test.js b/src/__tests__/ParseObject-test.js index 068c5fed8..9e222e4b9 100644 --- a/src/__tests__/ParseObject-test.js +++ b/src/__tests__/ParseObject-test.js @@ -3424,6 +3424,10 @@ describe('ParseObject Subclasses', () => { }).toThrow( 'You must register the subclass constructor. Did you attempt to register an instance of the subclass?' ); + + expect(() => { + ParseObject.unregisterSubclass(1234); + }).toThrow('The first argument must be a valid class name.'); }); it('can use on ParseObject subclass for multiple Parse.Object class names', () => { From 92be22d0496fb1857960959ab0107d0177c99adf Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Thu, 11 Mar 2021 14:57:22 -0600 Subject: [PATCH 4/8] Fix flaky test --- integration/test/ParseEventuallyQueueTest.js | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/integration/test/ParseEventuallyQueueTest.js b/integration/test/ParseEventuallyQueueTest.js index ce718b99f..55ec96c05 100644 --- a/integration/test/ParseEventuallyQueueTest.js +++ b/integration/test/ParseEventuallyQueueTest.js @@ -188,7 +188,6 @@ describe('Parse EventuallyQueue', () => { it('can saveEventually', async done => { const parseServer = await reconfigureServer(); const object = new TestObject({ hash: 'saveSecret' }); - await parseServer.handleShutdown(); parseServer.server.close(async () => { await object.saveEventually(); let length = await Parse.EventuallyQueue.length(); @@ -196,8 +195,9 @@ describe('Parse EventuallyQueue', () => { assert.strictEqual(length, 1); await reconfigureServer({}); - await sleep(3000); // Wait for polling - + while (Parse.EventuallyQueue.isPolling()) { + await sleep(100); + } assert.strictEqual(Parse.EventuallyQueue.isPolling(), false); length = await Parse.EventuallyQueue.length(); assert.strictEqual(length, 0); @@ -214,7 +214,6 @@ describe('Parse EventuallyQueue', () => { const parseServer = await reconfigureServer(); const object = new TestObject({ hash: 'deleteSecret' }); await object.save(); - await parseServer.handleShutdown(); parseServer.server.close(async () => { await object.destroyEventually(); let length = await Parse.EventuallyQueue.length(); @@ -222,8 +221,9 @@ describe('Parse EventuallyQueue', () => { assert.strictEqual(length, 1); await reconfigureServer({}); - await sleep(3000); // Wait for polling - + while (Parse.EventuallyQueue.isPolling()) { + await sleep(100); + } assert.strictEqual(Parse.EventuallyQueue.isPolling(), false); length = await Parse.EventuallyQueue.length(); assert.strictEqual(length, 0); From 659161c19cc0f65ded716d1d4241476edbea8e1d Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Thu, 11 Mar 2021 15:10:21 -0600 Subject: [PATCH 5/8] Fix unsorted geopoint tests --- integration/test/ParseGeoPointTest.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/integration/test/ParseGeoPointTest.js b/integration/test/ParseGeoPointTest.js index 25861fca3..328c3e34a 100644 --- a/integration/test/ParseGeoPointTest.js +++ b/integration/test/ParseGeoPointTest.js @@ -356,8 +356,9 @@ describe('Geo Point', () => { query.withinKilometers('location', sfo, 3700.0, false); query.find().then(results => { assert.equal(results.length, 2); - assert.equal(results[0].get('name'), 'San Francisco'); - assert.equal(results[1].get('name'), 'Sacramento'); + results.forEach(result => { + assert.strictEqual(['San Francisco', 'Sacramento'].includes(result.get('name')), true); + }); done(); }); }); @@ -399,8 +400,9 @@ describe('Geo Point', () => { query.withinMiles('location', sfo, 2200.0, false); query.find().then(results => { assert.equal(results.length, 2); - assert.equal(results[0].get('name'), 'San Francisco'); - assert.equal(results[1].get('name'), 'Sacramento'); + results.forEach(result => { + assert.strictEqual(['San Francisco', 'Sacramento'].includes(result.get('name')), true); + }); done(); }); }); From b724e7cce6b1801e449a32e7a42857cf8a6ebf06 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Thu, 11 Mar 2021 15:51:12 -0600 Subject: [PATCH 6/8] improve tests with background operations --- integration/test/ParseCloudTest.js | 9 +++++++-- integration/test/ParseEventuallyQueueTest.js | 21 +++++++++++++++++--- integration/test/helper.js | 2 +- jasmine.json | 2 +- 4 files changed, 27 insertions(+), 7 deletions(-) diff --git a/integration/test/ParseCloudTest.js b/integration/test/ParseCloudTest.js index 3f39bdf22..8ec1702b1 100644 --- a/integration/test/ParseCloudTest.js +++ b/integration/test/ParseCloudTest.js @@ -102,8 +102,13 @@ describe('Parse Cloud', () => { let jobStatus = await Parse.Cloud.getJobStatus(jobStatusId); assert.equal(jobStatus.get('status'), 'running'); - await sleep(2000); - + const checkJobStatus = async () => { + const result = await Parse.Cloud.getJobStatus(jobStatusId); + return result && result.get('status') === 'succeeded'; + }; + while (!(await checkJobStatus())) { + await sleep(100); + } jobStatus = await Parse.Cloud.getJobStatus(jobStatusId); assert.equal(jobStatus.get('status'), 'succeeded'); }); diff --git a/integration/test/ParseEventuallyQueueTest.js b/integration/test/ParseEventuallyQueueTest.js index 55ec96c05..101ed75af 100644 --- a/integration/test/ParseEventuallyQueueTest.js +++ b/integration/test/ParseEventuallyQueueTest.js @@ -163,7 +163,9 @@ describe('Parse EventuallyQueue', () => { Parse.EventuallyQueue.poll(); assert.ok(Parse.EventuallyQueue.isPolling()); - await sleep(4000); + while (Parse.EventuallyQueue.isPolling()) { + await sleep(100); + } const query = new Parse.Query(TestObject); const result = await query.get(object.id); assert.strictEqual(result.get('foo'), 'bar'); @@ -199,12 +201,19 @@ describe('Parse EventuallyQueue', () => { await sleep(100); } assert.strictEqual(Parse.EventuallyQueue.isPolling(), false); + + while (await Parse.EventuallyQueue.length()) { + await sleep(100); + } length = await Parse.EventuallyQueue.length(); assert.strictEqual(length, 0); const query = new Parse.Query(TestObject); query.equalTo('hash', 'saveSecret'); - const results = await query.find(); + let results = await query.find(); + while (results.length === 0) { + results = await query.find(); + } assert.strictEqual(results.length, 1); done(); }); @@ -225,12 +234,18 @@ describe('Parse EventuallyQueue', () => { await sleep(100); } assert.strictEqual(Parse.EventuallyQueue.isPolling(), false); + while (await Parse.EventuallyQueue.length()) { + await sleep(100); + } length = await Parse.EventuallyQueue.length(); assert.strictEqual(length, 0); const query = new Parse.Query(TestObject); query.equalTo('hash', 'deleteSecret'); - const results = await query.find(); + let results = await query.find(); + while (results.length) { + results = await query.find(); + } assert.strictEqual(results.length, 0); done(); }); diff --git a/integration/test/helper.js b/integration/test/helper.js index 3517b75b6..68a9007b4 100644 --- a/integration/test/helper.js +++ b/integration/test/helper.js @@ -1,4 +1,4 @@ -jasmine.DEFAULT_TIMEOUT_INTERVAL = 10000; +jasmine.DEFAULT_TIMEOUT_INTERVAL = 20000; const ParseServer = require('parse-server').default; const CustomAuth = require('./CustomAuth'); diff --git a/jasmine.json b/jasmine.json index 27093e99b..9590307f3 100644 --- a/jasmine.json +++ b/jasmine.json @@ -7,5 +7,5 @@ "*Test.js" ], "random": true, - "timeout": 10000 + "timeout": 20000 } From 57a5fc2a30fc9d1bc0677726e50b642ad4dc5758 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Thu, 11 Mar 2021 16:02:01 -0600 Subject: [PATCH 7/8] lint --- integration/test/ParseEventuallyQueueTest.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/integration/test/ParseEventuallyQueueTest.js b/integration/test/ParseEventuallyQueueTest.js index 101ed75af..2df0a1bfe 100644 --- a/integration/test/ParseEventuallyQueueTest.js +++ b/integration/test/ParseEventuallyQueueTest.js @@ -167,7 +167,10 @@ describe('Parse EventuallyQueue', () => { await sleep(100); } const query = new Parse.Query(TestObject); - const result = await query.get(object.id); + let result = await query.get(object.id); + while (result.get('foo') !== 'bar') { + result = await query.get(object.id); + } assert.strictEqual(result.get('foo'), 'bar'); const length = await Parse.EventuallyQueue.length(); From 3ae501a85b18f3838eec98cd5e890f4141305574 Mon Sep 17 00:00:00 2001 From: Diamond Lewis Date: Thu, 11 Mar 2021 16:19:28 -0600 Subject: [PATCH 8/8] re-add skipped test --- integration/test/ParseGeoPointTest.js | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/integration/test/ParseGeoPointTest.js b/integration/test/ParseGeoPointTest.js index 328c3e34a..9f4414eff 100644 --- a/integration/test/ParseGeoPointTest.js +++ b/integration/test/ParseGeoPointTest.js @@ -476,19 +476,13 @@ describe('Geo Point', () => { }); }); - xit( - 'minimum 3 points withinPolygon', - function (done) { - const query = new Parse.Query(TestPoint); - query.withinPolygon('location', []); - query - .find() - .then(done.fail, err => { - assert.equal(err.code, Parse.Error.INVALID_JSON); - done(); - }) - .catch(done.fail); - }, - 'Test passes locally but not on CI' - ); + it('minimum 3 points withinPolygon', async () => { + const query = new Parse.Query(TestPoint); + query.withinPolygon('location', []); + try { + await query.find(); + } catch (error) { + assert.strictEqual(error.code, Parse.Error.INVALID_JSON); + } + }); });