diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b93134a84a..da415e25b4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -263,9 +263,7 @@ If your pull request introduces a change that may affect the storage or retrieva * If your feature is intended to work with MongoDB and PostgreSQL, you can include or exclude tests more granularly with: - `it_only_mongodb_version('>=4.4')` // will test with any version of Postgres but only with version >=4.4 of MongoDB; accepts semver notation to specify a version range - - `it_exclude_mongodb_version('<4.4')` // will test with any version of Postgres and MongoDB, excluding version <4.4 of MongoDB; accepts semver notation to specify a version range - `it_only_postgres_version('>=13')` // will test with any version of Mongo but only with version >=13 of Postgres; accepts semver notation to specify a version range - - `it_exclude_postgres_version('<13')` // will test with any version of Postgres and MongoDB, excluding version <13 of Postgres; accepts semver notation to specify a version range #### Postgres with Docker diff --git a/package.json b/package.json index 69ec29852e..68a2c024f7 100644 --- a/package.json +++ b/package.json @@ -126,6 +126,7 @@ "test:mongodb:5.3.2": "npm run test:mongodb --dbversion=5.3.2", "test:mongodb:6.0.2": "npm run test:mongodb --dbversion=6.0.2", "test:mongodb:7.0.1": "npm run test:mongodb --dbversion=7.0.1", + "test:mongodb:8.0.3": "npm run test:mongodb --dbversion=8.0.3", "test:postgres:testonly": "cross-env PARSE_SERVER_TEST_DB=postgres PARSE_SERVER_TEST_DATABASE_URI=postgres://postgres:password@localhost:5432/parse_server_postgres_adapter_test_database npm run testonly", "pretest": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} mongodb-runner start -t ${MONGODB_TOPOLOGY} --version ${MONGODB_VERSION} -- --port 27017", "testonly": "cross-env MONGODB_VERSION=${MONGODB_VERSION:=5.3.2} MONGODB_TOPOLOGY=${MONGODB_TOPOLOGY:=standalone} TESTING=1 jasmine", diff --git a/spec/.eslintrc.json b/spec/.eslintrc.json index 63ef1765e8..933e6ed15d 100644 --- a/spec/.eslintrc.json +++ b/spec/.eslintrc.json @@ -22,11 +22,8 @@ "it_only_postgres_version": true, "it_only_node_version": true, "fit_only_mongodb_version": true, + "fit_only_postgres_version": true, "fit_only_node_version": true, - "it_exclude_mongodb_version": true, - "it_exclude_postgres_version": true, - "fit_exclude_mongodb_version": true, - "fit_exclude_node_version": true, "it_exclude_dbs": true, "fit_exclude_dbs": true, "describe_only_db": true, diff --git a/spec/MongoStorageAdapter.spec.js b/spec/MongoStorageAdapter.spec.js index e7bbfe0cd8..a913930c28 100644 --- a/spec/MongoStorageAdapter.spec.js +++ b/spec/MongoStorageAdapter.spec.js @@ -390,7 +390,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { await expectAsync(adapter.getClass('UnknownClass')).toBeRejectedWith(undefined); }); - it_only_mongodb_version('<5.1>=6')('should use index for caseInsensitive query', async () => { + it_only_mongodb_version('<5.1 || >=6')('should use index for caseInsensitive query', async () => { const user = new Parse.User(); user.set('username', 'Bugs'); user.set('password', 'Bunny'); @@ -424,7 +424,7 @@ describe_only_db('mongo')('MongoStorageAdapter', () => { expect(postIndexPlan.executionStats.executionStages.stage).toBe('FETCH'); }); - it_only_mongodb_version('>=5.1<6')('should use index for caseInsensitive query', async () => { + it_only_mongodb_version('>=5.1 <6')('should use index for caseInsensitive query', async () => { const user = new Parse.User(); user.set('username', 'Bugs'); user.set('password', 'Bunny'); diff --git a/spec/ParseQuery.hint.spec.js b/spec/ParseQuery.hint.spec.js index 7ebffa4d34..cd8a01cbe9 100644 --- a/spec/ParseQuery.hint.spec.js +++ b/spec/ParseQuery.hint.spec.js @@ -27,7 +27,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { await TestUtils.destroyAllDataPermanently(false); }); - it_only_mongodb_version('<5.1>=6')('query find with hint string', async () => { + it_only_mongodb_version('<5.1 || >=6 <8')('query find with hint string', async () => { const object = new TestObject(); await object.save(); @@ -39,7 +39,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('>=5.1<6')('query find with hint string', async () => { + it_only_mongodb_version('>=5.1 <6')('query find with hint string', async () => { const object = new TestObject(); await object.save(); @@ -50,7 +50,19 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(explain.queryPlanner.winningPlan.queryPlan.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('<5.1>=6')('query find with hint object', async () => { + it_only_mongodb_version('>=8')('query find with hint string', async () => { + const object = new TestObject(); + await object.save(); + + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + let explain = await collection._rawFind({ _id: object.id }, { explain: true }); + expect(explain.queryPlanner.winningPlan.stage).toBe('EXPRESS_IXSCAN'); + explain = await collection._rawFind({ _id: object.id }, { hint: '_id_', explain: true }); + expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); + expect(explain.queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); + }); + + it_only_mongodb_version('<5.1 || >=6 <8')('query find with hint object', async () => { const object = new TestObject(); await object.save(); @@ -64,7 +76,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { }); }); - it_only_mongodb_version('>=5.1<6')('query find with hint object', async () => { + it_only_mongodb_version('>=5.1 <6')('query find with hint object', async () => { const object = new TestObject(); await object.save(); @@ -78,6 +90,20 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(explain.queryPlanner.winningPlan.queryPlan.inputStage.keyPattern).toEqual({ _id: 1 }); }); + it_only_mongodb_version('>=8')('query find with hint object', async () => { + const object = new TestObject(); + await object.save(); + + const collection = await config.database.adapter._adaptiveCollection('TestObject'); + let explain = await collection._rawFind({ _id: object.id }, { explain: true }); + expect(explain.queryPlanner.winningPlan.stage).toBe('EXPRESS_IXSCAN'); + explain = await collection._rawFind({ _id: object.id }, { hint: { _id: 1 }, explain: true }); + expect(explain.queryPlanner.winningPlan.stage).toBe('FETCH'); + expect(explain.queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ + _id: 1, + }); + }); + it_only_mongodb_version('<4.4')('query aggregate with hint string', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -100,7 +126,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('>=4.4<5.1')('query aggregate with hint string', async () => { + it_only_mongodb_version('>=4.4 <5.1')('query aggregate with hint string', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -124,7 +150,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('>=5.1<5.2')('query aggregate with hint string', async () => { + it_only_mongodb_version('>=5.1 <5.2')('query aggregate with hint string', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -192,7 +218,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it_only_mongodb_version('>=4.4<5.1')('query aggregate with hint object', async () => { + it_only_mongodb_version('>=4.4 <5.1')('query aggregate with hint object', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -217,7 +243,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it_only_mongodb_version('>=5.1<5.2')('query aggregate with hint object', async () => { + it_only_mongodb_version('>=5.1 <5.2')('query aggregate with hint object', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); @@ -267,7 +293,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.queryPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it_only_mongodb_version('<5.1>=6')('query find with hint (rest)', async () => { + it_only_mongodb_version('<5.1 || >=6')('query find with hint (rest)', async () => { const object = new TestObject(); await object.save(); let options = Object.assign({}, masterKeyOptions, { @@ -292,7 +318,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(explain.queryPlanner.winningPlan.inputStage.inputStage.indexName).toBe('_id_'); }); - it_only_mongodb_version('>=5.1<6')('query find with hint (rest)', async () => { + it_only_mongodb_version('>=5.1 <6')('query find with hint (rest)', async () => { const object = new TestObject(); await object.save(); let options = Object.assign({}, masterKeyOptions, { @@ -344,7 +370,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it_only_mongodb_version('>=4.4<5.1')('query aggregate with hint (rest)', async () => { + it_only_mongodb_version('>=4.4 <5.1')('query aggregate with hint (rest)', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); let options = Object.assign({}, masterKeyOptions, { @@ -377,7 +403,7 @@ describe_only_db('mongo')('Parse.Query hint', () => { expect(queryPlanner.winningPlan.inputStage.inputStage.keyPattern).toEqual({ _id: 1 }); }); - it_only_mongodb_version('>=5.1<5.2')('query aggregate with hint (rest)', async () => { + it_only_mongodb_version('>=5.1 <5.2')('query aggregate with hint (rest)', async () => { const object = new TestObject({ foo: 'bar' }); await object.save(); let options = Object.assign({}, masterKeyOptions, { diff --git a/spec/helper.js b/spec/helper.js index c09a0c4eb4..7093cfcc4c 100644 --- a/spec/helper.js +++ b/spec/helper.js @@ -493,6 +493,9 @@ global.it_only_db = db => { }; global.it_only_mongodb_version = version => { + if (!semver.validRange(version)) { + throw new Error('Invalid version range'); + } const envVersion = process.env.MONGODB_VERSION; if (!envVersion || semver.satisfies(envVersion, version)) { return it; @@ -502,6 +505,9 @@ global.it_only_mongodb_version = version => { }; global.it_only_postgres_version = version => { + if (!semver.validRange(version)) { + throw new Error('Invalid version range'); + } const envVersion = process.env.POSTGRES_VERSION; if (!envVersion || semver.satisfies(envVersion, version)) { return it; @@ -511,6 +517,9 @@ global.it_only_postgres_version = version => { }; global.it_only_node_version = version => { + if (!semver.validRange(version)) { + throw new Error('Invalid version range'); + } const envVersion = process.version; if (!envVersion || semver.satisfies(envVersion, version)) { return it; @@ -520,16 +529,10 @@ global.it_only_node_version = version => { }; global.fit_only_mongodb_version = version => { - const envVersion = process.env.MONGODB_VERSION; - if (!envVersion || semver.satisfies(envVersion, version)) { - return fit; - } else { - return xit; + if (!semver.validRange(version)) { + throw new Error('Invalid version range'); } -}; - -global.fit_only_node_version = version => { - const envVersion = process.version; + const envVersion = process.env.MONGODB_VERSION; if (!envVersion || semver.satisfies(envVersion, version)) { return fit; } else { @@ -537,45 +540,24 @@ global.fit_only_node_version = version => { } }; -global.it_exclude_mongodb_version = version => { - const envVersion = process.env.MONGODB_VERSION; - if (!envVersion || !semver.satisfies(envVersion, version)) { - return it; - } else { - return xit; +global.fit_only_postgres_version = version => { + if (!semver.validRange(version)) { + throw new Error('Invalid version range'); } -}; - -global.it_exclude_postgres_version = version => { const envVersion = process.env.POSTGRES_VERSION; - if (!envVersion || !semver.satisfies(envVersion, version)) { - return it; - } else { - return xit; - } -}; - -global.it_exclude_node_version = version => { - const envVersion = process.env.NODE_VERSION; - if (!envVersion || !semver.satisfies(envVersion, version)) { - return it; - } else { - return xit; - } -}; - -global.fit_exclude_mongodb_version = version => { - const envVersion = process.env.MONGODB_VERSION; - if (!envVersion || !semver.satisfies(envVersion, version)) { + if (!envVersion || semver.satisfies(envVersion, version)) { return fit; } else { return xit; } }; -global.fit_exclude_node_version = version => { - const envVersion = process.env.NODE_VERSION; - if (!envVersion || !semver.satisfies(envVersion, version)) { +global.fit_only_node_version = version => { + if (!semver.validRange(version)) { + throw new Error('Invalid version range'); + } + const envVersion = process.version; + if (!envVersion || semver.satisfies(envVersion, version)) { return fit; } else { return xit;