Compare commits

..
46 Commits
Author SHA1 Message Date
semantic-release-bot 128023bf88 chore(release): 7.3.0 [skip ci]
# [7.3.0](https://github.com/parse-community/parse-server/compare/7.2.0...7.3.0) (2024-10-03)

### Bug Fixes

* Custom object ID allows to acquire role privileges ([GHSA-8xq9-g7ch-35hg](https://github.com/parse-community/parse-server/security/advisories/GHSA-8xq9-g7ch-35hg)) ([#9317](https://github.com/parse-community/parse-server/issues/9317)) ([13ee52f](https://github.com/parse-community/parse-server/commit/13ee52f0d19ef3a3524b3d79aea100e587eb3cfc))
* Parse Server `databaseOptions` nested keys incorrectly identified as invalid ([#9213](https://github.com/parse-community/parse-server/issues/9213)) ([77206d8](https://github.com/parse-community/parse-server/commit/77206d804443cfc1618c24f8961bd677de9920c0))
* Parse Server installation fails due to post install script incorrectly parsing required min. Node version ([#9216](https://github.com/parse-community/parse-server/issues/9216)) ([0fa82a5](https://github.com/parse-community/parse-server/commit/0fa82a54fe38ec14e8054339285d3db71a8624c8))
* Parse Server option `maxLogFiles` doesn't recognize day duration literals such as `1d` to mean 1 day ([#9215](https://github.com/parse-community/parse-server/issues/9215)) ([0319cee](https://github.com/parse-community/parse-server/commit/0319cee2dbf65e90bad377af1ed14ea25c595bf5))
* Security upgrade path-to-regexp from 6.2.1 to 6.3.0 ([#9314](https://github.com/parse-community/parse-server/issues/9314)) ([8b7fe69](https://github.com/parse-community/parse-server/commit/8b7fe699c1c376ecd8cc1c97cce8e704ee41f28a))

### Features

* Add atomic operations for Cloud Config parameters ([#9219](https://github.com/parse-community/parse-server/issues/9219)) ([35cadf9](https://github.com/parse-community/parse-server/commit/35cadf9b8324879fb7309ba5d7ea46f2c722d614))
* Add Cloud Code triggers `Parse.Cloud.beforeSave` and `Parse.Cloud.afterSave` for Parse Config ([#9232](https://github.com/parse-community/parse-server/issues/9232)) ([90a1e4a](https://github.com/parse-community/parse-server/commit/90a1e4a200423d644efb3f0ba2fba4b99f5cf954))
* Add Node 22 support ([#9187](https://github.com/parse-community/parse-server/issues/9187)) ([7778471](https://github.com/parse-community/parse-server/commit/7778471999c7e42236ce404229660d80ecc2acd6))
* Add support for asynchronous invocation of `FilesAdapter.getFileLocation` ([#9271](https://github.com/parse-community/parse-server/issues/9271)) ([1a2da40](https://github.com/parse-community/parse-server/commit/1a2da4055abe831b3017172fb75e16d7a8093873))
2024-10-03 20:07:51 +00:00
Manuel 85b63eef17 build: Release (#9320) 2024-10-03 22:07:01 +02:00
Manuel Trezza 59d86c8a77 empty 2024-10-03 21:40:40 +02:00
semantic-release-bot 4d37dee9cc chore(release): 7.3.0-beta.1 [skip ci]
# [7.3.0-beta.1](https://github.com/parse-community/parse-server/compare/7.2.0...7.3.0-beta.1) (2024-10-03)

### Bug Fixes

* Custom object ID allows to acquire role privileges ([GHSA-8xq9-g7ch-35hg](https://github.com/parse-community/parse-server/security/advisories/GHSA-8xq9-g7ch-35hg)) ([#9317](https://github.com/parse-community/parse-server/issues/9317)) ([13ee52f](https://github.com/parse-community/parse-server/commit/13ee52f0d19ef3a3524b3d79aea100e587eb3cfc))
* Parse Server `databaseOptions` nested keys incorrectly identified as invalid ([#9213](https://github.com/parse-community/parse-server/issues/9213)) ([77206d8](https://github.com/parse-community/parse-server/commit/77206d804443cfc1618c24f8961bd677de9920c0))
* Parse Server installation fails due to post install script incorrectly parsing required min. Node version ([#9216](https://github.com/parse-community/parse-server/issues/9216)) ([0fa82a5](https://github.com/parse-community/parse-server/commit/0fa82a54fe38ec14e8054339285d3db71a8624c8))
* Parse Server option `maxLogFiles` doesn't recognize day duration literals such as `1d` to mean 1 day ([#9215](https://github.com/parse-community/parse-server/issues/9215)) ([0319cee](https://github.com/parse-community/parse-server/commit/0319cee2dbf65e90bad377af1ed14ea25c595bf5))
* Security upgrade path-to-regexp from 6.2.1 to 6.3.0 ([#9314](https://github.com/parse-community/parse-server/issues/9314)) ([8b7fe69](https://github.com/parse-community/parse-server/commit/8b7fe699c1c376ecd8cc1c97cce8e704ee41f28a))

### Features

* Add atomic operations for Cloud Config parameters ([#9219](https://github.com/parse-community/parse-server/issues/9219)) ([35cadf9](https://github.com/parse-community/parse-server/commit/35cadf9b8324879fb7309ba5d7ea46f2c722d614))
* Add Cloud Code triggers `Parse.Cloud.beforeSave` and `Parse.Cloud.afterSave` for Parse Config ([#9232](https://github.com/parse-community/parse-server/issues/9232)) ([90a1e4a](https://github.com/parse-community/parse-server/commit/90a1e4a200423d644efb3f0ba2fba4b99f5cf954))
* Add Node 22 support ([#9187](https://github.com/parse-community/parse-server/issues/9187)) ([7778471](https://github.com/parse-community/parse-server/commit/7778471999c7e42236ce404229660d80ecc2acd6))
* Add support for asynchronous invocation of `FilesAdapter.getFileLocation` ([#9271](https://github.com/parse-community/parse-server/issues/9271)) ([1a2da40](https://github.com/parse-community/parse-server/commit/1a2da4055abe831b3017172fb75e16d7a8093873))
2024-10-03 19:39:42 +00:00
Manuel 388cd3e1d3 build: Release (#9319) 2024-10-03 21:38:47 +02:00
Manuel Trezza 7298641f14 empty 2024-10-03 21:21:23 +02:00
semantic-release-bot f66ece53b1 chore(release): 7.3.0-alpha.9 [skip ci]
# [7.3.0-alpha.9](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.8...7.3.0-alpha.9) (2024-10-03)

### Bug Fixes

* Custom object ID allows to acquire role privileges ([GHSA-8xq9-g7ch-35hg](https://github.com/parse-community/parse-server/security/advisories/GHSA-8xq9-g7ch-35hg)) ([#9317](https://github.com/parse-community/parse-server/issues/9317)) ([13ee52f](https://github.com/parse-community/parse-server/commit/13ee52f0d19ef3a3524b3d79aea100e587eb3cfc))
2024-10-03 19:18:09 +00:00
Manuel 13ee52f0d1 fix: Custom object ID allows to acquire role privileges ([GHSA-8xq9-g7ch-35hg](https://github.com/parse-community/parse-server/security/advisories/GHSA-8xq9-g7ch-35hg)) (#9317) 2024-10-03 21:17:14 +02:00
Kaiwalya Koparkar b86906f303 docs: Add Elestio as one-click deploy option (#9289) 2024-09-27 13:26:34 +02:00
semantic-release-bot 6613f8f403 chore(release): 7.3.0-alpha.8 [skip ci]
# [7.3.0-alpha.8](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.7...7.3.0-alpha.8) (2024-09-25)

### Bug Fixes

* Security upgrade path-to-regexp from 6.2.1 to 6.3.0 ([#9314](https://github.com/parse-community/parse-server/issues/9314)) ([8b7fe69](https://github.com/parse-community/parse-server/commit/8b7fe699c1c376ecd8cc1c97cce8e704ee41f28a))
2024-09-25 01:17:15 +00:00
dependabot[bot] 8b7fe699c1 fix: Security upgrade path-to-regexp from 6.2.1 to 6.3.0 (#9314) 2024-09-25 03:16:18 +02:00
dependabot[bot] 435f0d1a42 refactor: Bump dset from 3.1.3 to 3.1.4 (#9305) 2024-09-16 03:45:00 +02:00
Parse Platform 3bb3a8090d refactor: Fix for 4 vulnerabilities (#9311) 2024-09-12 14:41:24 +02:00
semantic-release-bot e944616ff7 chore(release): 7.3.0-alpha.7 [skip ci]
# [7.3.0-alpha.7](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.6...7.3.0-alpha.7) (2024-08-27)

### Features

* Add support for asynchronous invocation of `FilesAdapter.getFileLocation` ([#9271](https://github.com/parse-community/parse-server/issues/9271)) ([1a2da40](https://github.com/parse-community/parse-server/commit/1a2da4055abe831b3017172fb75e16d7a8093873))
2024-08-27 15:10:17 +00:00
Vahid Sane 1a2da4055a feat: Add support for asynchronous invocation of FilesAdapter.getFileLocation (#9271) 2024-08-27 17:09:19 +02:00
Doug Drechsel 2a63129ff6 test: Add more test IDs (#9285) 2024-08-16 21:21:56 +02:00
Manuel 9552a4cbee ci: Fix test exclusion list in combination with other exclusions (#9277) 2024-08-13 22:13:19 +02:00
Diamond Lewis 9fd707092e ci: Add test retry logic for flaky tests (#9218) 2024-08-11 14:24:50 +02:00
dependabot[bot] 453a9879ef refactor: Bump requirejs from 2.3.6 to 2.3.7 (#9252) 2024-07-26 19:31:01 +02:00
dependabot[bot] e08af3bbf7 refactor: Bump commander from 12.0.0 to 12.1.0 (#9251) 2024-07-26 19:30:35 +02:00
dependabot[bot] d0cb857e37 refactor: Bump yaml from 1.10.0 to 2.5.0 (#9245) 2024-07-26 17:42:26 +02:00
dependabot[bot] 388ecde6a6 refactor: Bump @apollo/client from 3.9.11 to 3.11.1 (#9249) 2024-07-25 16:39:45 +02:00
dependabot[bot] cb26e20463 refactor: Bump @apollo/server from 4.10.4 to 4.10.5 (#9244) 2024-07-24 23:18:22 +02:00
dependabot[bot] 1e7d7447dc refactor: Bump semver from 7.6.2 to 7.6.3 (#9241) 2024-07-22 17:15:08 +02:00
Diamond Lewis 5d09a1fd81 ci: Fix flaky direct access transaction tests (#9235) 2024-07-21 12:11:03 +02:00
semantic-release-bot 71a34eccc2 chore(release): 7.3.0-alpha.6 [skip ci]
# [7.3.0-alpha.6](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.5...7.3.0-alpha.6) (2024-07-20)

### Features

* Add Cloud Code triggers `Parse.Cloud.beforeSave` and `Parse.Cloud.afterSave` for Parse Config ([#9232](https://github.com/parse-community/parse-server/issues/9232)) ([90a1e4a](https://github.com/parse-community/parse-server/commit/90a1e4a200423d644efb3f0ba2fba4b99f5cf954))
2024-07-20 18:36:31 +00:00
Diamond Lewis 90a1e4a200 feat: Add Cloud Code triggers Parse.Cloud.beforeSave and Parse.Cloud.afterSave for Parse Config (#9232) 2024-07-20 20:35:41 +02:00
Parse Platform 4d86ace2cc refactor: Upgrade @apollo/server from 4.10.1 to 4.10.4 (#9233) 2024-07-20 13:22:03 +02:00
Parse Platform c4db9e6ae4 refactor: Upgrade semver from 7.6.0 to 7.6.2 (#9234) 2024-07-20 13:21:40 +02:00
dependabot[bot] edad6329cc refactor: Bump eslint from 8.26.0 to 8.57.0 (#9230) 2024-07-19 14:43:19 +02:00
dependabot[bot] b5e550b593 refactor: Bump mongodb-runner from 5.5.4 to 5.6.4 (#9231) 2024-07-19 14:42:57 +02:00
Parse Platform f4c620b24b refactor: Upgrade @graphql-tools/merge from 9.0.3 to 9.0.4 (#9226) 2024-07-19 13:40:00 +02:00
dependabot[bot] ef63c63097 refactor: Bump graphql-relay from 0.10.1 to 0.10.2 (#9228) 2024-07-19 13:39:36 +02:00
dependabot[bot] a9132a6bcc refactor: Bump otpauth from 9.2.2 to 9.3.1 (#9229) 2024-07-19 13:39:17 +02:00
semantic-release-bot b0b3051e64 chore(release): 7.3.0-alpha.5 [skip ci]
# [7.3.0-alpha.5](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.4...7.3.0-alpha.5) (2024-07-18)

### Bug Fixes

* Parse Server option `maxLogFiles` doesn't recognize day duration literals such as `1d` to mean 1 day ([#9215](https://github.com/parse-community/parse-server/issues/9215)) ([0319cee](https://github.com/parse-community/parse-server/commit/0319cee2dbf65e90bad377af1ed14ea25c595bf5))
2024-07-18 13:42:54 +00:00
Diamond Lewis 0319cee2db fix: Parse Server option maxLogFiles doesn't recognize day duration literals such as 1d to mean 1 day (#9215) 2024-07-18 15:41:59 +02:00
Manuel 901cff5edd test: Add test IDs (#9205) 2024-07-18 15:41:04 +02:00
dependabot[bot] 4b04c2222c refactor: Bump @babel/preset-env from 7.24.0 to 7.24.8 (#9224) 2024-07-18 15:20:41 +02:00
dependabot[bot] 76dbcc5f7e refactor: Bump @graphql-tools/schema from 10.0.3 to 10.0.4 (#9225) 2024-07-18 15:20:20 +02:00
Manuel 49006e87e9 docs: Fix CI badges in README (#9221) 2024-07-18 04:19:19 +02:00
semantic-release-bot 50d9a8fad1 chore(release): 7.3.0-alpha.4 [skip ci]
# [7.3.0-alpha.4](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.3...7.3.0-alpha.4) (2024-07-18)

### Features

* Add atomic operations for Cloud Config parameters ([#9219](https://github.com/parse-community/parse-server/issues/9219)) ([35cadf9](https://github.com/parse-community/parse-server/commit/35cadf9b8324879fb7309ba5d7ea46f2c722d614))
2024-07-18 00:53:32 +00:00
Diamond Lewis 35cadf9b83 feat: Add atomic operations for Cloud Config parameters (#9219) 2024-07-18 02:52:39 +02:00
Diamond Lewis 69aba3bd3b ci: Fix Postgres flaky tests (#9217) 2024-07-17 19:28:09 +02:00
semantic-release-bot 16f31504b3 chore(release): 7.3.0-alpha.3 [skip ci]
# [7.3.0-alpha.3](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.2...7.3.0-alpha.3) (2024-07-17)

### Bug Fixes

* Parse Server installation fails due to post install script incorrectly parsing required min. Node version ([#9216](https://github.com/parse-community/parse-server/issues/9216)) ([0fa82a5](https://github.com/parse-community/parse-server/commit/0fa82a54fe38ec14e8054339285d3db71a8624c8))
2024-07-17 17:10:30 +00:00
Manuel 0fa82a54fe fix: Parse Server installation fails due to post install script incorrectly parsing required min. Node version (#9216) 2024-07-17 19:09:34 +02:00
Diamond Lewis 5fa58fe774 ci: Fix CI not testing with Postgres 16 and flaky test (#9210) 2024-07-17 17:51:13 +02:00
74 changed files with 3019 additions and 2201 deletions
+3 -3
View File
@@ -175,7 +175,7 @@ jobs:
NODE_VERSION: 18.20.4
fail-fast: false
name: ${{ matrix.name }}
timeout-minutes: 15
timeout-minutes: 20
runs-on: ubuntu-latest
services:
redis:
@@ -238,11 +238,11 @@ jobs:
POSTGRES_IMAGE: postgis/postgis:15-3.4
NODE_VERSION: 22.4.1
- name: PostgreSQL 16, PostGIS 3.4
POSTGRES_IMAGE: postgis/postgis:15-3.4
POSTGRES_IMAGE: postgis/postgis:16-3.4
NODE_VERSION: 22.4.1
fail-fast: false
name: ${{ matrix.name }}
timeout-minutes: 15
timeout-minutes: 20
runs-on: ubuntu-latest
services:
redis:
+4 -3
View File
@@ -2,9 +2,9 @@
---
[![Build Status](https://github.com/parse-community/parse-server/workflows/ci/badge.svg?branch=alpha)](https://github.com/parse-community/parse-server/actions?query=workflow%3Aci+branch%3Aalpha)
[![Build Status](https://github.com/parse-community/parse-server/workflows/ci/badge.svg?branch=beta)](https://github.com/parse-community/parse-server/actions?query=workflow%3Aci+branch%3Abeta)
[![Build Status](https://github.com/parse-community/parse-server/workflows/ci/badge.svg?branch=release)](https://github.com/parse-community/parse-server/actions?query=workflow%3Aci+branch%3Arelease)
[![Build Status](https://github.com/parse-community/parse-server/actions/workflows/ci.yml/badge.svg?branch=alpha)](https://github.com/parse-community/parse-server/actions/workflows/ci.yml?query=workflow%3Aci+branch%3Aalpha)
[![Build Status](https://github.com/parse-community/parse-server/actions/workflows/ci.yml/badge.svg?branch=beta)](https://github.com/parse-community/parse-server/actions/workflows/ci.yml?query=workflow%3Aci+branch%3Abeta)
[![Build Status](https://github.com/parse-community/parse-server/actions/workflows/ci.yml/badge.svg?branch=release)](https://github.com/parse-community/parse-server/actions/workflows/ci.yml?query=workflow%3Aci+branch%3Arelease)
[![Snyk Badge](https://snyk.io/test/github/parse-community/parse-server/badge.svg)](https://snyk.io/test/github/parse-community/parse-server)
[![Coverage](https://codecov.io/github/parse-community/parse-server/branch/alpha/graph/badge.svg)](https://app.codecov.io/github/parse-community/parse-server/tree/alpha)
[![auto-release](https://img.shields.io/badge/%F0%9F%9A%80-auto--release-9e34eb.svg)](https://github.com/parse-community/parse-dashboard/releases)
@@ -276,6 +276,7 @@ We have provided a basic [Node.js application](https://github.com/parse-communit
* [Back4app](https://www.back4app.com/docs/get-started/welcome)
* [Glitch](https://glitch.com/edit/#!/parse-server)
* [Flynn](https://flynn.io/blog/parse-apps-on-flynn)
* [Elestio](https://elest.io/open-source/parse)
### Parse Server + Express
+49
View File
@@ -1,3 +1,52 @@
# [7.3.0-alpha.9](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.8...7.3.0-alpha.9) (2024-10-03)
### Bug Fixes
* Custom object ID allows to acquire role privileges ([GHSA-8xq9-g7ch-35hg](https://github.com/parse-community/parse-server/security/advisories/GHSA-8xq9-g7ch-35hg)) ([#9317](https://github.com/parse-community/parse-server/issues/9317)) ([13ee52f](https://github.com/parse-community/parse-server/commit/13ee52f0d19ef3a3524b3d79aea100e587eb3cfc))
# [7.3.0-alpha.8](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.7...7.3.0-alpha.8) (2024-09-25)
### Bug Fixes
* Security upgrade path-to-regexp from 6.2.1 to 6.3.0 ([#9314](https://github.com/parse-community/parse-server/issues/9314)) ([8b7fe69](https://github.com/parse-community/parse-server/commit/8b7fe699c1c376ecd8cc1c97cce8e704ee41f28a))
# [7.3.0-alpha.7](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.6...7.3.0-alpha.7) (2024-08-27)
### Features
* Add support for asynchronous invocation of `FilesAdapter.getFileLocation` ([#9271](https://github.com/parse-community/parse-server/issues/9271)) ([1a2da40](https://github.com/parse-community/parse-server/commit/1a2da4055abe831b3017172fb75e16d7a8093873))
# [7.3.0-alpha.6](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.5...7.3.0-alpha.6) (2024-07-20)
### Features
* Add Cloud Code triggers `Parse.Cloud.beforeSave` and `Parse.Cloud.afterSave` for Parse Config ([#9232](https://github.com/parse-community/parse-server/issues/9232)) ([90a1e4a](https://github.com/parse-community/parse-server/commit/90a1e4a200423d644efb3f0ba2fba4b99f5cf954))
# [7.3.0-alpha.5](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.4...7.3.0-alpha.5) (2024-07-18)
### Bug Fixes
* Parse Server option `maxLogFiles` doesn't recognize day duration literals such as `1d` to mean 1 day ([#9215](https://github.com/parse-community/parse-server/issues/9215)) ([0319cee](https://github.com/parse-community/parse-server/commit/0319cee2dbf65e90bad377af1ed14ea25c595bf5))
# [7.3.0-alpha.4](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.3...7.3.0-alpha.4) (2024-07-18)
### Features
* Add atomic operations for Cloud Config parameters ([#9219](https://github.com/parse-community/parse-server/issues/9219)) ([35cadf9](https://github.com/parse-community/parse-server/commit/35cadf9b8324879fb7309ba5d7ea46f2c722d614))
# [7.3.0-alpha.3](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.2...7.3.0-alpha.3) (2024-07-17)
### Bug Fixes
* Parse Server installation fails due to post install script incorrectly parsing required min. Node version ([#9216](https://github.com/parse-community/parse-server/issues/9216)) ([0fa82a5](https://github.com/parse-community/parse-server/commit/0fa82a54fe38ec14e8054339285d3db71a8624c8))
# [7.3.0-alpha.2](https://github.com/parse-community/parse-server/compare/7.3.0-alpha.1...7.3.0-alpha.2) (2024-07-17)
+18
View File
@@ -1,3 +1,21 @@
# [7.3.0-beta.1](https://github.com/parse-community/parse-server/compare/7.2.0...7.3.0-beta.1) (2024-10-03)
### Bug Fixes
* Custom object ID allows to acquire role privileges ([GHSA-8xq9-g7ch-35hg](https://github.com/parse-community/parse-server/security/advisories/GHSA-8xq9-g7ch-35hg)) ([#9317](https://github.com/parse-community/parse-server/issues/9317)) ([13ee52f](https://github.com/parse-community/parse-server/commit/13ee52f0d19ef3a3524b3d79aea100e587eb3cfc))
* Parse Server `databaseOptions` nested keys incorrectly identified as invalid ([#9213](https://github.com/parse-community/parse-server/issues/9213)) ([77206d8](https://github.com/parse-community/parse-server/commit/77206d804443cfc1618c24f8961bd677de9920c0))
* Parse Server installation fails due to post install script incorrectly parsing required min. Node version ([#9216](https://github.com/parse-community/parse-server/issues/9216)) ([0fa82a5](https://github.com/parse-community/parse-server/commit/0fa82a54fe38ec14e8054339285d3db71a8624c8))
* Parse Server option `maxLogFiles` doesn't recognize day duration literals such as `1d` to mean 1 day ([#9215](https://github.com/parse-community/parse-server/issues/9215)) ([0319cee](https://github.com/parse-community/parse-server/commit/0319cee2dbf65e90bad377af1ed14ea25c595bf5))
* Security upgrade path-to-regexp from 6.2.1 to 6.3.0 ([#9314](https://github.com/parse-community/parse-server/issues/9314)) ([8b7fe69](https://github.com/parse-community/parse-server/commit/8b7fe699c1c376ecd8cc1c97cce8e704ee41f28a))
### Features
* Add atomic operations for Cloud Config parameters ([#9219](https://github.com/parse-community/parse-server/issues/9219)) ([35cadf9](https://github.com/parse-community/parse-server/commit/35cadf9b8324879fb7309ba5d7ea46f2c722d614))
* Add Cloud Code triggers `Parse.Cloud.beforeSave` and `Parse.Cloud.afterSave` for Parse Config ([#9232](https://github.com/parse-community/parse-server/issues/9232)) ([90a1e4a](https://github.com/parse-community/parse-server/commit/90a1e4a200423d644efb3f0ba2fba4b99f5cf954))
* Add Node 22 support ([#9187](https://github.com/parse-community/parse-server/issues/9187)) ([7778471](https://github.com/parse-community/parse-server/commit/7778471999c7e42236ce404229660d80ecc2acd6))
* Add support for asynchronous invocation of `FilesAdapter.getFileLocation` ([#9271](https://github.com/parse-community/parse-server/issues/9271)) ([1a2da40](https://github.com/parse-community/parse-server/commit/1a2da4055abe831b3017172fb75e16d7a8093873))
# [7.2.0-beta.1](https://github.com/parse-community/parse-server/compare/7.1.0...7.2.0-beta.1) (2024-07-09)
+18
View File
@@ -1,3 +1,21 @@
# [7.3.0](https://github.com/parse-community/parse-server/compare/7.2.0...7.3.0) (2024-10-03)
### Bug Fixes
* Custom object ID allows to acquire role privileges ([GHSA-8xq9-g7ch-35hg](https://github.com/parse-community/parse-server/security/advisories/GHSA-8xq9-g7ch-35hg)) ([#9317](https://github.com/parse-community/parse-server/issues/9317)) ([13ee52f](https://github.com/parse-community/parse-server/commit/13ee52f0d19ef3a3524b3d79aea100e587eb3cfc))
* Parse Server `databaseOptions` nested keys incorrectly identified as invalid ([#9213](https://github.com/parse-community/parse-server/issues/9213)) ([77206d8](https://github.com/parse-community/parse-server/commit/77206d804443cfc1618c24f8961bd677de9920c0))
* Parse Server installation fails due to post install script incorrectly parsing required min. Node version ([#9216](https://github.com/parse-community/parse-server/issues/9216)) ([0fa82a5](https://github.com/parse-community/parse-server/commit/0fa82a54fe38ec14e8054339285d3db71a8624c8))
* Parse Server option `maxLogFiles` doesn't recognize day duration literals such as `1d` to mean 1 day ([#9215](https://github.com/parse-community/parse-server/issues/9215)) ([0319cee](https://github.com/parse-community/parse-server/commit/0319cee2dbf65e90bad377af1ed14ea25c595bf5))
* Security upgrade path-to-regexp from 6.2.1 to 6.3.0 ([#9314](https://github.com/parse-community/parse-server/issues/9314)) ([8b7fe69](https://github.com/parse-community/parse-server/commit/8b7fe699c1c376ecd8cc1c97cce8e704ee41f28a))
### Features
* Add atomic operations for Cloud Config parameters ([#9219](https://github.com/parse-community/parse-server/issues/9219)) ([35cadf9](https://github.com/parse-community/parse-server/commit/35cadf9b8324879fb7309ba5d7ea46f2c722d614))
* Add Cloud Code triggers `Parse.Cloud.beforeSave` and `Parse.Cloud.afterSave` for Parse Config ([#9232](https://github.com/parse-community/parse-server/issues/9232)) ([90a1e4a](https://github.com/parse-community/parse-server/commit/90a1e4a200423d644efb3f0ba2fba4b99f5cf954))
* Add Node 22 support ([#9187](https://github.com/parse-community/parse-server/issues/9187)) ([7778471](https://github.com/parse-community/parse-server/commit/7778471999c7e42236ce404229660d80ecc2acd6))
* Add support for asynchronous invocation of `FilesAdapter.getFileLocation` ([#9271](https://github.com/parse-community/parse-server/issues/9271)) ([1a2da40](https://github.com/parse-community/parse-server/commit/1a2da4055abe831b3017172fb75e16d7a8093873))
# [7.2.0](https://github.com/parse-community/parse-server/compare/7.1.0...7.2.0) (2024-07-09)
+1868 -1665
View File
File diff suppressed because it is too large Load Diff
+19 -19
View File
@@ -1,6 +1,6 @@
{
"name": "parse-server",
"version": "7.3.0-alpha.2",
"version": "7.3.0",
"description": "An express module providing a Parse-compatible API server",
"main": "lib/index.js",
"repository": {
@@ -19,24 +19,24 @@
],
"license": "Apache-2.0",
"dependencies": {
"@apollo/server": "4.10.1",
"@apollo/server": "4.10.5",
"@babel/eslint-parser": "7.24.7",
"@graphql-tools/merge": "9.0.3",
"@graphql-tools/schema": "10.0.3",
"@graphql-tools/merge": "9.0.4",
"@graphql-tools/schema": "10.0.4",
"@graphql-tools/utils": "8.12.0",
"@parse/fs-files-adapter": "3.0.0",
"@parse/push-adapter": "6.4.1",
"bcryptjs": "2.4.3",
"body-parser": "1.20.2",
"commander": "12.0.0",
"body-parser": "1.20.3",
"commander": "12.1.0",
"cors": "2.8.5",
"deepcopy": "2.1.0",
"express": "4.19.2",
"express": "4.21.0",
"express-rate-limit": "7.3.1",
"follow-redirects": "1.15.6",
"graphql": "16.8.1",
"graphql-list-fields": "2.0.4",
"graphql-relay": "0.10.1",
"graphql-relay": "0.10.2",
"graphql-tag": "2.12.6",
"graphql-upload": "15.0.2",
"intersect": "1.0.1",
@@ -48,16 +48,16 @@
"mime": "3.0.0",
"mongodb": "5.9.0",
"mustache": "4.2.0",
"otpauth": "9.2.2",
"otpauth": "9.3.1",
"parse": "5.3.0",
"path-to-regexp": "6.2.1",
"pg-monitor": "2.0.0",
"pg-promise": "11.7.8",
"path-to-regexp": "6.3.0",
"pg-monitor": "2.1.0",
"pg-promise": "11.9.1",
"pluralize": "8.0.0",
"punycode": "2.3.1",
"rate-limit-redis": "4.2.0",
"redis": "4.6.13",
"semver": "7.6.0",
"semver": "7.6.3",
"subscriptions-transport-ws": "0.11.0",
"tv4": "1.3.0",
"uuid": "10.0.0",
@@ -67,12 +67,12 @@
},
"devDependencies": {
"@actions/core": "1.10.1",
"@apollo/client": "3.9.11",
"@apollo/client": "3.11.1",
"@babel/cli": "7.24.8",
"@babel/core": "7.24.7",
"@babel/plugin-proposal-object-rest-spread": "7.20.7",
"@babel/plugin-transform-flow-strip-types": "7.24.7",
"@babel/preset-env": "7.24.0",
"@babel/preset-env": "7.24.8",
"@saithodev/semantic-release-backmerge": "4.0.1",
"@semantic-release/changelog": "6.0.3",
"@semantic-release/commit-analyzer": "13.0.0",
@@ -85,7 +85,7 @@
"clean-jsdoc-theme": "4.3.0",
"cross-env": "7.0.2",
"deep-diff": "1.0.2",
"eslint": "8.26.0",
"eslint": "8.57.0",
"eslint-plugin-flowtype": "8.0.3",
"flow-bin": "0.119.1",
"form-data": "4.0.0",
@@ -100,13 +100,13 @@
"madge": "7.0.0",
"mock-files-adapter": "file:spec/dependencies/mock-files-adapter",
"mock-mail-adapter": "file:spec/dependencies/mock-mail-adapter",
"mongodb-runner": "5.5.4",
"mongodb-runner": "5.6.4",
"node-abort-controller": "3.1.1",
"node-fetch": "3.2.10",
"nyc": "15.1.0",
"prettier": "2.0.5",
"semantic-release": "24.0.0",
"yaml": "1.10.0"
"yaml": "2.5.0"
},
"scripts": {
"ci:check": "node ./ci/ciCheck.js",
@@ -139,7 +139,7 @@
"madge:circular": "node_modules/.bin/madge ./src --circular"
},
"engines": {
"node": "18 || 19 || 20 || 22"
"node": ">=18.0.0 <19.0.0 || >=19.0.0 <20.0.0 || >=20.0.0 <21.0.0 || >=22.0.0 <23.0.0"
},
"bin": {
"parse-server": "bin/parse-server"
+34 -46
View File
@@ -1,50 +1,38 @@
const pkg = require('./package.json');
const message = `
1111111111
1111111111111111
1111111111111111111111
11111111111111111111111111
111111111111111 11111111
1111111111111 111 111111
1111111111111 111111111 111111
111111111111 11111111111 111111
1111111111111 11111111111 111111
1111111111111 1111111111 111111
1111111111111111111111111 1111111
11111111 11111111
111111 111 1111111111111111111
11111 11111 111111111111111111
11111 1 11111111111111111
111111 111111111111111111
11111111111111111111111111
1111111111111111111111
111111111111111111
11111111111
Thank you for using Parse Platform!
https://parseplatform.org
Please consider donating to help us maintain
this package:
const version = parseFloat(process.version.substring(1));
const minimum = parseFloat(pkg.engines.node.match(/\d+/g).join('.'));
👉 https://opencollective.com/parse-server 👈
module.exports = function () {
const openCollective = `
1111111111
1111111111111111
1111111111111111111111
11111111111111111111111111
111111111111111 11111111
1111111111111 111111
1111111111111 111111111 111111
111111111111 11111111111 111111
1111111111111 11111111111 111111
1111111111111 1111111111 111111
1111111111111111111111111 1111111
11111111 11111111
111111 1111111111111111111
11111 11111 111111111111111111
11111 11111111111111111
111111 111111111111111111
11111111111111111111111111
1111111111111111111111
111111111111111111
11111111111
`;
function main() {
process.stdout.write(message);
process.exit(0);
}
Thanks for installing parse 🙏
Please consider donating to our open collective
to help us maintain this package.
👉 https://opencollective.com/parse-server
`;
process.stdout.write(openCollective);
if (version >= minimum) {
process.exit(0);
}
const errorMessage = `
⚠️ parse-server requires at least node@${minimum}!
You have node@${version}
`;
process.stdout.write(errorMessage);
process.exit(1);
};
module.exports = main;
+6
View File
@@ -161,6 +161,9 @@ function mapperFor(elt, t) {
if (type == 'NumberOrBoolean') {
return wrap(t.identifier('numberOrBooleanParser'));
}
if (type == 'NumberOrString') {
return t.callExpression(wrap(t.identifier('numberOrStringParser')), [t.stringLiteral(elt.name)]);
}
if (type === 'StringOrStringArray') {
return wrap(t.identifier('arrayParser'));
}
@@ -212,6 +215,9 @@ function parseDefaultValue(elt, value, t) {
if (type == 'NumberOrBoolean') {
literalValue = t.numericLiteral(parsers.numberOrBoolParser('')(value));
}
if (type == 'NumberOrString') {
literalValue = t.numericLiteral(parsers.numberOrStringParser('')(value));
}
if (nestedOptionTypes.includes(type)) {
const object = parsers.objectParser(value);
+2
View File
@@ -16,6 +16,7 @@
"expectAsync": true,
"notEqual": true,
"it_id": true,
"fit_id": true,
"it_only_db": true,
"it_only_mongodb_version": true,
"it_only_postgres_version": true,
@@ -27,6 +28,7 @@
"fit_exclude_mongodb_version": true,
"fit_exclude_node_version": true,
"it_exclude_dbs": true,
"fit_exclude_dbs": true,
"describe_only_db": true,
"fdescribe_only_db": true,
"describe_only": true,
+1 -1
View File
@@ -317,7 +317,7 @@ describe('AudiencesRouter', () => {
);
});
it_exclude_dbs(['postgres'])('should support legacy parse.com audience fields', done => {
it_id('af1111b5-3251-4b40-8f06-fb0fc624fa91')(it_exclude_dbs(['postgres']))('should support legacy parse.com audience fields', done => {
const database = Config.get(Parse.applicationId).database.adapter.database;
const now = new Date();
Parse._request(
+15 -15
View File
@@ -645,7 +645,7 @@ describe('google auth adapter', () => {
// }
// });
it('(using client id as string) should verify id_token', async () => {
it('(using client id as string) should verify id_token (google.com)', async () => {
const fakeClaim = {
iss: 'https://accounts.google.com',
aud: 'secret',
@@ -663,7 +663,7 @@ describe('google auth adapter', () => {
expect(result).toEqual(fakeClaim);
});
it('(using client id as string) should throw error with with invalid jwt issuer', async () => {
it('(using client id as string) should throw error with with invalid jwt issuer (google.com)', async () => {
const fakeClaim = {
iss: 'https://not.google.com',
sub: 'the_user_id',
@@ -1466,7 +1466,7 @@ describe('apple signin auth adapter', () => {
}
});
it('should use algorithm from key header to verify id_token', async () => {
it('should use algorithm from key header to verify id_token (apple.com)', async () => {
const fakeClaim = {
iss: 'https://appleid.apple.com',
aud: 'secret',
@@ -1516,7 +1516,7 @@ describe('apple signin auth adapter', () => {
}
});
it('(using client id as string) should verify id_token', async () => {
it('(using client id as string) should verify id_token (apple.com)', async () => {
const fakeClaim = {
iss: 'https://appleid.apple.com',
aud: 'secret',
@@ -1536,7 +1536,7 @@ describe('apple signin auth adapter', () => {
expect(result).toEqual(fakeClaim);
});
it('(using client id as array) should verify id_token', async () => {
it('(using client id as array) should verify id_token (apple.com)', async () => {
const fakeClaim = {
iss: 'https://appleid.apple.com',
aud: 'secret',
@@ -1556,7 +1556,7 @@ describe('apple signin auth adapter', () => {
expect(result).toEqual(fakeClaim);
});
it('(using client id as array with multiple items) should verify id_token', async () => {
it('(using client id as array with multiple items) should verify id_token (apple.com)', async () => {
const fakeClaim = {
iss: 'https://appleid.apple.com',
aud: 'secret',
@@ -1576,7 +1576,7 @@ describe('apple signin auth adapter', () => {
expect(result).toEqual(fakeClaim);
});
it('(using client id as string) should throw error with with invalid jwt issuer', async () => {
it('(using client id as string) should throw error with with invalid jwt issuer (apple.com)', async () => {
const fakeClaim = {
iss: 'https://not.apple.com',
sub: 'the_user_id',
@@ -1629,7 +1629,7 @@ describe('apple signin auth adapter', () => {
}
});
it('(using client id as string) should throw error with with invalid jwt issuer with token', async () => {
it('(using client id as string) should throw error with with invalid jwt issuer with token (apple.com)', async () => {
const fakeClaim = {
iss: 'https://not.apple.com',
sub: 'the_user_id',
@@ -1698,7 +1698,7 @@ describe('apple signin auth adapter', () => {
}
});
it('should throw error with with invalid user id', async () => {
it('should throw error with with invalid user id (apple.com)', async () => {
const fakeClaim = {
iss: 'https://appleid.apple.com',
aud: 'invalid_client_id',
@@ -2022,7 +2022,7 @@ describe('facebook limited auth adapter', () => {
}
});
it('should use algorithm from key header to verify id_token', async () => {
it_id('7bfa55ab-8fd7-4526-992e-6de3df16bf9c')(it)('should use algorithm from key header to verify id_token (facebook.com)', async () => {
const fakeClaim = {
iss: 'https://www.facebook.com',
aud: 'secret',
@@ -2072,7 +2072,7 @@ describe('facebook limited auth adapter', () => {
}
});
it('(using client id as string) should verify id_token', async () => {
it_id('4bcb1a1a-11f8-4e12-a3f6-73f7e25e355a')(it)('using client id as string) should verify id_token (facebook.com)', async () => {
const fakeClaim = {
iss: 'https://www.facebook.com',
aud: 'secret',
@@ -2092,7 +2092,7 @@ describe('facebook limited auth adapter', () => {
expect(result).toEqual(fakeClaim);
});
it('(using client id as array) should verify id_token', async () => {
it_id('c521a272-2ac2-4d8b-b5ed-ea250336d8b1')(it)('(using client id as array) should verify id_token (facebook.com)', async () => {
const fakeClaim = {
iss: 'https://www.facebook.com',
aud: 'secret',
@@ -2112,7 +2112,7 @@ describe('facebook limited auth adapter', () => {
expect(result).toEqual(fakeClaim);
});
it('(using client id as array with multiple items) should verify id_token', async () => {
it_id('e3f16404-18e9-4a87-a555-4710cfbdac67')(it)('(using client id as array with multiple items) should verify id_token (facebook.com)', async () => {
const fakeClaim = {
iss: 'https://www.facebook.com',
aud: 'secret',
@@ -2132,7 +2132,7 @@ describe('facebook limited auth adapter', () => {
expect(result).toEqual(fakeClaim);
});
it('(using client id as string) should throw error with with invalid jwt issuer', async () => {
it_id('549c33a1-3a6b-4732-8cf6-8f010ad4569c')(it)('(using client id as string) should throw error with with invalid jwt issuer (facebook.com)', async () => {
const fakeClaim = {
iss: 'https://not.facebook.com',
sub: 'the_user_id',
@@ -2263,7 +2263,7 @@ describe('facebook limited auth adapter', () => {
}
});
it('should throw error with with invalid user id', async () => {
it_id('c194d902-e697-46c9-a303-82c2d914473c')(it)('should throw error with with invalid user id (facebook.com)', async () => {
const fakeClaim = {
iss: 'https://www.facebook.com',
aud: 'invalid_client_id',
+4 -4
View File
@@ -248,7 +248,7 @@ describe('execution', () => {
}
});
it('should start Parse Server', done => {
it_id('a0ab74b4-f805-4e03-b31d-b5cd59e64495')(it)('should start Parse Server', done => {
const env = { ...process.env };
env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation';
childProcess = spawn(
@@ -261,7 +261,7 @@ describe('execution', () => {
handleError(childProcess, done);
});
it('should start Parse Server with GraphQL', async done => {
it_id('d7165081-b133-4cba-901b-19128ce41301')(it)('should start Parse Server with GraphQL', async done => {
const env = { ...process.env };
env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation';
childProcess = spawn(
@@ -287,7 +287,7 @@ describe('execution', () => {
handleError(childProcess, done);
});
it('should start Parse Server with GraphQL and Playground', async done => {
it_id('2769cdb4-ce8a-484d-8a91-635b5894ba7e')(it)('should start Parse Server with GraphQL and Playground', async done => {
const env = { ...process.env };
env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation';
childProcess = spawn(
@@ -315,7 +315,7 @@ describe('execution', () => {
handleError(childProcess, done);
});
it('can start Parse Server with auth via CLI', done => {
it_id('23caddd7-bfea-4869-8bd4-0f2cd283c8bd')(it)('can start Parse Server with auth via CLI', done => {
const env = { ...process.env };
env.NODE_OPTIONS = '--dns-result-order=ipv4first --trace-deprecation';
childProcess = spawn(
+72 -1
View File
@@ -6,6 +6,9 @@ const validatorFail = () => {
const validatorSuccess = () => {
return true;
};
function testConfig() {
return Parse.Config.save({ internal: 'i', string: 's', number: 12 }, { internal: true });
}
describe('cloud validator', () => {
it('complete validator', async done => {
@@ -731,6 +734,38 @@ describe('cloud validator', () => {
done();
});
it_id('893eec0c-41bd-4adf-8f0a-306087ad8d61')(it)('basic beforeSave Parse.Config skipWithMasterKey', async () => {
Parse.Cloud.beforeSave(
Parse.Config,
() => {
throw 'beforeSaveFile should have resolved using master key.';
},
{
skipWithMasterKey: true,
}
);
const config = await testConfig();
expect(config.get('internal')).toBe('i');
expect(config.get('string')).toBe('s');
expect(config.get('number')).toBe(12);
});
it_id('91e739a4-6a38-405c-8f83-f36d48220734')(it)('basic afterSave Parse.Config skipWithMasterKey', async () => {
Parse.Cloud.afterSave(
Parse.Config,
() => {
throw 'beforeSaveFile should have resolved using master key.';
},
{
skipWithMasterKey: true,
}
);
const config = await testConfig();
expect(config.get('internal')).toBe('i');
expect(config.get('string')).toBe('s');
expect(config.get('number')).toBe(12);
});
it('beforeSave validateMasterKey and skipWithMasterKey fail', async function (done) {
Parse.Cloud.beforeSave(
'BeforeSave',
@@ -1441,7 +1476,7 @@ describe('cloud validator', () => {
});
it('validate afterSaveFile fail', async done => {
Parse.Cloud.beforeSave(Parse.File, () => {}, validatorFail);
Parse.Cloud.afterSave(Parse.File, () => {}, validatorFail);
try {
const file = new Parse.File('popeye.txt', [1, 2, 3], 'text/plain');
await file.save({ useMasterKey: true });
@@ -1496,6 +1531,42 @@ describe('cloud validator', () => {
}
});
it_id('32ca1a99-7f2b-429d-a7cf-62b6661d0af6')(it)('validate beforeSave Parse.Config', async () => {
Parse.Cloud.beforeSave(Parse.Config, () => {}, validatorSuccess);
const config = await testConfig();
expect(config.get('internal')).toBe('i');
expect(config.get('string')).toBe('s');
expect(config.get('number')).toBe(12);
});
it_id('c84d11e7-d09c-4843-ad98-f671511bf612')(it)('validate beforeSave Parse.Config fail', async () => {
Parse.Cloud.beforeSave(Parse.Config, () => {}, validatorFail);
try {
await testConfig();
fail('cloud function should have failed.');
} catch (e) {
expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
}
});
it_id('b18b9a6a-0e35-4b60-9771-30f53501df3c')(it)('validate afterSave Parse.Config', async () => {
Parse.Cloud.afterSave(Parse.Config, () => {}, validatorSuccess);
const config = await testConfig();
expect(config.get('internal')).toBe('i');
expect(config.get('string')).toBe('s');
expect(config.get('number')).toBe(12);
});
it_id('ef761222-1758-4614-b984-da84d73fc10c')(it)('validate afterSave Parse.Config fail', async () => {
Parse.Cloud.afterSave(Parse.Config, () => {}, validatorFail);
try {
await testConfig();
fail('cloud function should have failed.');
} catch (e) {
expect(e.code).toBe(Parse.Error.VALIDATION_ERROR);
}
});
it('Should have validator', async done => {
Parse.Cloud.define(
'myFunction',
+162 -6
View File
@@ -2317,7 +2317,7 @@ describe('beforeFind hooks', () => {
);
});
it('should handle empty where', done => {
it_id('6ef0d226-af30-4dfd-8306-972a1b4becd3')(it)('should handle empty where', done => {
Parse.Cloud.beforeFind('MyObject', req => {
const otherQuery = new Parse.Query('MyObject');
otherQuery.equalTo('some', true);
@@ -2919,7 +2919,7 @@ describe('afterFind hooks', () => {
}).toThrow('Only the _Session class is allowed for the afterLogout trigger.');
});
it('should skip afterFind hooks for aggregate', done => {
it_id('c16159b5-e8ee-42d5-8fe3-e2f7c006881d')(it)('should skip afterFind hooks for aggregate', done => {
const hook = {
method: function () {
return Promise.reject();
@@ -2946,7 +2946,7 @@ describe('afterFind hooks', () => {
});
});
it('should skip afterFind hooks for distinct', done => {
it_id('ca55c90d-36db-422c-9060-a30583ce5224')(it)('should skip afterFind hooks for distinct', done => {
const hook = {
method: function () {
return Promise.reject();
@@ -3033,7 +3033,7 @@ describe('afterFind hooks', () => {
expect(calledAfter).toBe(false);
});
it('should expose context in beforeSave/afterSave via header', async () => {
it_id('55ef1741-cf72-4a7c-a029-00cb75f53233')(it)('should expose context in beforeSave/afterSave via header', async () => {
let calledBefore = false;
let calledAfter = false;
Parse.Cloud.beforeSave('TestObject', req => {
@@ -3347,7 +3347,7 @@ describe('beforeLogin hook', () => {
expect(response).toEqual(error);
});
it('should have expected data in request', async done => {
it_id('5656d6d7-65ef-43d1-8ca6-6942ae3614d5')(it)('should have expected data in request in beforeLogin', async done => {
Parse.Cloud.beforeLogin(req => {
expect(req.object).toBeDefined();
expect(req.user).toBeUndefined();
@@ -3464,7 +3464,7 @@ describe('afterLogin hook', () => {
done();
});
it('should have expected data in request', async done => {
it_id('e86155c4-62e1-4c6e-ab4a-9ac6c87c60f2')(it)('should have expected data in request in afterLogin', async done => {
Parse.Cloud.afterLogin(req => {
expect(req.object).toBeDefined();
expect(req.user).toBeDefined();
@@ -3921,6 +3921,162 @@ describe('saveFile hooks', () => {
});
});
describe('Cloud Config hooks', () => {
function testConfig() {
return Parse.Config.save({ internal: 'i', string: 's', number: 12 }, { internal: true });
}
it_id('997fe20a-96f7-454a-a5b0-c155b8d02f05')(it)('beforeSave(Parse.Config) can run hook with new config', async () => {
let count = 0;
Parse.Cloud.beforeSave(Parse.Config, (req) => {
expect(req.object).toBeDefined();
expect(req.original).toBeUndefined();
expect(req.user).toBeUndefined();
expect(req.headers).toBeDefined();
expect(req.ip).toBeDefined();
expect(req.installationId).toBeDefined();
expect(req.context).toBeDefined();
const config = req.object;
expect(config.get('internal')).toBe('i');
expect(config.get('string')).toBe('s');
expect(config.get('number')).toBe(12);
count += 1;
});
await testConfig();
const config = await Parse.Config.get({ useMasterKey: true });
expect(config.get('internal')).toBe('i');
expect(config.get('string')).toBe('s');
expect(config.get('number')).toBe(12);
expect(count).toBe(1);
});
it_id('06a9b66c-ffb4-43d1-a025-f7d2192500e7')(it)('beforeSave(Parse.Config) can run hook with existing config', async () => {
let count = 0;
Parse.Cloud.beforeSave(Parse.Config, (req) => {
if (count === 0) {
expect(req.object.get('number')).toBe(12);
expect(req.original).toBeUndefined();
}
if (count === 1) {
expect(req.object.get('number')).toBe(13);
expect(req.original.get('number')).toBe(12);
}
count += 1;
});
await testConfig();
await Parse.Config.save({ number: 13 });
expect(count).toBe(2);
});
it_id('ca76de8e-671b-4c2d-9535-bd28a855fa1a')(it)('beforeSave(Parse.Config) should not change config if nothing is returned', async () => {
let count = 0;
Parse.Cloud.beforeSave(Parse.Config, () => {
count += 1;
return;
});
await testConfig();
const config = await Parse.Config.get({ useMasterKey: true });
expect(config.get('internal')).toBe('i');
expect(config.get('string')).toBe('s');
expect(config.get('number')).toBe(12);
expect(count).toBe(1);
});
it('beforeSave(Parse.Config) throw custom error', async () => {
Parse.Cloud.beforeSave(Parse.Config, () => {
throw new Parse.Error(Parse.Error.SCRIPT_FAILED, 'It should fail');
});
try {
await testConfig();
fail('error should have thrown');
} catch (e) {
expect(e.code).toBe(Parse.Error.SCRIPT_FAILED);
expect(e.message).toBe('It should fail');
}
});
it('beforeSave(Parse.Config) throw string error', async () => {
Parse.Cloud.beforeSave(Parse.Config, () => {
throw 'before save failed';
});
try {
await testConfig();
fail('error should have thrown');
} catch (e) {
expect(e.code).toBe(Parse.Error.SCRIPT_FAILED);
expect(e.message).toBe('before save failed');
}
});
it('beforeSave(Parse.Config) throw empty error', async () => {
Parse.Cloud.beforeSave(Parse.Config, () => {
throw null;
});
try {
await testConfig();
fail('error should have thrown');
} catch (e) {
expect(e.code).toBe(Parse.Error.SCRIPT_FAILED);
expect(e.message).toBe('Script failed. Unknown error.');
}
});
it_id('3e7a75c0-6c2e-4c7e-b042-6eb5f23acf94')(it)('afterSave(Parse.Config) can run hook with new config', async () => {
let count = 0;
Parse.Cloud.afterSave(Parse.Config, (req) => {
expect(req.object).toBeDefined();
expect(req.original).toBeUndefined();
expect(req.user).toBeUndefined();
expect(req.headers).toBeDefined();
expect(req.ip).toBeDefined();
expect(req.installationId).toBeDefined();
expect(req.context).toBeDefined();
const config = req.object;
expect(config.get('internal')).toBe('i');
expect(config.get('string')).toBe('s');
expect(config.get('number')).toBe(12);
count += 1;
});
await testConfig();
const config = await Parse.Config.get({ useMasterKey: true });
expect(config.get('internal')).toBe('i');
expect(config.get('string')).toBe('s');
expect(config.get('number')).toBe(12);
expect(count).toBe(1);
});
it_id('5cffb28a-2924-4857-84bb-f5778d80372a')(it)('afterSave(Parse.Config) can run hook with existing config', async () => {
let count = 0;
Parse.Cloud.afterSave(Parse.Config, (req) => {
if (count === 0) {
expect(req.object.get('number')).toBe(12);
expect(req.original).toBeUndefined();
}
if (count === 1) {
expect(req.object.get('number')).toBe(13);
expect(req.original.get('number')).toBe(12);
}
count += 1;
});
await testConfig();
await Parse.Config.save({ number: 13 });
expect(count).toBe(2);
});
it_id('49883992-ce91-4797-85f9-7cce1f819407')(it)('afterSave(Parse.Config) should throw error', async () => {
Parse.Cloud.afterSave(Parse.Config, () => {
throw new Parse.Error(400, 'It should fail');
});
try {
await testConfig();
fail('error should have thrown');
} catch (e) {
expect(e.code).toBe(400);
expect(e.message).toBe('It should fail');
}
});
});
describe('sendEmail', () => {
it('can send email via Parse.Cloud', async done => {
const emailAdapter = {
+10 -10
View File
@@ -37,7 +37,7 @@ describe('Cloud Code Logger', () => {
// Note that helpers takes care of logout.
// see helpers.js:afterEach
it('should expose log to functions', () => {
it_id('02d53b97-3ec7-46fb-abb6-176fd6e85590')(it)('should expose log to functions', () => {
const spy = spyOn(Config.get('test').loggerController, 'log').and.callThrough();
Parse.Cloud.define('loggerTest', req => {
req.log.info('logTest', 'info log', { info: 'some log' });
@@ -67,7 +67,7 @@ describe('Cloud Code Logger', () => {
});
});
it('trigger should obfuscate password', done => {
it_id('768412f5-d32f-4134-89a6-08949781a6c0')(it)('trigger should obfuscate password', done => {
Parse.Cloud.beforeSave(Parse.User, req => {
return req.object;
});
@@ -82,7 +82,7 @@ describe('Cloud Code Logger', () => {
.then(null, e => done.fail(e));
});
it('should expose log to trigger', done => {
it_id('3c394047-272e-4728-9d02-9eaa660d2ed2')(it)('should expose log to trigger', done => {
Parse.Cloud.beforeSave('MyObject', req => {
req.log.info('beforeSave MyObject', 'info log', { info: 'some log' });
req.log.error('beforeSave MyObject', 'error log', {
@@ -120,7 +120,7 @@ describe('Cloud Code Logger', () => {
expect(truncatedString.length).toBe(1015); // truncate length + the string '... (truncated)'
});
it('should truncate input and result of long lines', done => {
it_id('4a009b1f-9203-49ca-8d48-5b45f4eedbdf')(it)('should truncate input and result of long lines', done => {
const longString = fs.readFileSync(loremFile, 'utf8');
Parse.Cloud.define('aFunction', req => {
return req.params;
@@ -138,7 +138,7 @@ describe('Cloud Code Logger', () => {
.then(null, e => done.fail(e));
});
it('should log an afterSave', done => {
it_id('9857e15d-bb18-478d-8a67-fdaad3e89565')(it)('should log an afterSave', done => {
Parse.Cloud.afterSave('MyObject', () => {});
new Parse.Object('MyObject')
.save()
@@ -151,7 +151,7 @@ describe('Cloud Code Logger', () => {
.then(null, e => done.fail(e));
});
it('should log a denied beforeSave', done => {
it_id('ec13a296-f8b1-4fc6-985a-3593462edd9c')(it)('should log a denied beforeSave', done => {
Parse.Cloud.beforeSave('MyObject', () => {
throw 'uh oh!';
});
@@ -174,7 +174,7 @@ describe('Cloud Code Logger', () => {
});
});
it('should log cloud function success', done => {
it_id('3e0caa45-60d6-41af-829a-fd389710c132')(it)('should log cloud function success', done => {
Parse.Cloud.define('aFunction', () => {
return 'it worked!';
});
@@ -189,7 +189,7 @@ describe('Cloud Code Logger', () => {
});
});
it('should log cloud function execution using the custom log level', async done => {
it_id('8088de8a-7cba-4035-8b05-4a903307e674')(it)('should log cloud function execution using the custom log level', async done => {
Parse.Cloud.define('aFunction', () => {
return 'it worked!';
});
@@ -260,7 +260,7 @@ describe('Cloud Code Logger', () => {
expect(calls).toEqual({ beforeSave: 'warn', afterSave: undefined });
});
it('should log cloud function failure', done => {
it_id('97e0eafa-cde6-4a9a-9e53-7db98bacbc62')(it)('should log cloud function failure', done => {
Parse.Cloud.define('aFunction', () => {
throw 'it failed!';
});
@@ -311,7 +311,7 @@ describe('Cloud Code Logger', () => {
.then(null, e => done.fail(JSON.stringify(e)));
}).pend('needs more work.....');
it('cloud function should obfuscate password', done => {
it_id('b86e8168-8370-4730-a4ba-24ca3016ad66')(it)('cloud function should obfuscate password', done => {
Parse.Cloud.define('testFunction', () => {
return 'verify code success';
});
+2 -1
View File
@@ -642,7 +642,8 @@ describe('DefinedSchemas', () => {
expect(logger.error).toHaveBeenCalledWith(`Failed to run migrations: ${error.toString()}`);
});
it('should perform migration in parallel without failing', async () => {
it_id('a18bf4f2-25c8-4de3-b986-19cb1ab163b8')(it)('should perform migration in parallel without failing', async () => {
const server = await reconfigureServer();
const logger = require('../lib/logger').logger;
spyOn(logger, 'error').and.callThrough();
+16 -16
View File
@@ -104,7 +104,7 @@ describe('Email Verification Token Expiration: ', () => {
});
});
it('if user clicks on the email verify link before email verification token expiration then show the verify email success page', done => {
it_id('f20dd3c2-87d9-4bc6-a51d-4ea2834acbcc')(it)('if user clicks on the email verify link before email verification token expiration then show the verify email success page', done => {
const user = new Parse.User();
let sendEmailOptions;
const emailAdapter = {
@@ -146,7 +146,7 @@ describe('Email Verification Token Expiration: ', () => {
});
});
it('if user clicks on the email verify link before email verification token expiration then emailVerified should be true', done => {
it_id('94956799-c85e-4297-b879-e2d1f985394c')(it)('if user clicks on the email verify link before email verification token expiration then emailVerified should be true', done => {
const user = new Parse.User();
let sendEmailOptions;
const emailAdapter = {
@@ -194,7 +194,7 @@ describe('Email Verification Token Expiration: ', () => {
});
});
it('if user clicks on the email verify link before email verification token expiration then user should be able to login', done => {
it_id('25f3f895-c987-431c-9841-17cb6aaf18b5')(it)('if user clicks on the email verify link before email verification token expiration then user should be able to login', done => {
const user = new Parse.User();
let sendEmailOptions;
const emailAdapter = {
@@ -242,7 +242,7 @@ describe('Email Verification Token Expiration: ', () => {
});
});
it('sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp', done => {
it_id('c6a3e188-9065-4f50-842d-454d1e82f289')(it)('sets the _email_verify_token_expires_at and _email_verify_token fields after user SignUp', done => {
const user = new Parse.User();
let sendEmailOptions;
const emailAdapter = {
@@ -292,7 +292,7 @@ describe('Email Verification Token Expiration: ', () => {
});
});
it('can conditionally send emails', async () => {
it_id('9365c53c-b8b4-41f7-a3c1-77882f76a89c')(it)('can conditionally send emails', async () => {
let sendEmailOptions;
const emailAdapter = {
sendVerificationEmail: options => {
@@ -351,7 +351,7 @@ describe('Email Verification Token Expiration: ', () => {
expect(verifySpy).toHaveBeenCalled();
});
it('can conditionally send emails and allow conditional login', async () => {
it_id('b3549300-bed7-4a5e-bed5-792dbfead960')(it)('can conditionally send emails and allow conditional login', async () => {
let sendEmailOptions;
const emailAdapter = {
sendVerificationEmail: options => {
@@ -397,7 +397,7 @@ describe('Email Verification Token Expiration: ', () => {
expect(verifySpy).toHaveBeenCalledTimes(5);
});
it('can conditionally send user email verification', async () => {
it_id('d812de87-33d1-495e-a6e8-3485f6dc3589')(it)('can conditionally send user email verification', async () => {
const emailAdapter = {
sendVerificationEmail: () => {},
sendPasswordResetEmail: () => Promise.resolve(),
@@ -431,7 +431,7 @@ describe('Email Verification Token Expiration: ', () => {
expect(emailSpy).toHaveBeenCalledTimes(0);
});
it('provides full user object in email verification function on email and username change', async () => {
it_id('d98babc1-feb8-4b5e-916c-57dc0a6ed9fb')(it)('provides full user object in email verification function on email and username change', async () => {
const emailAdapter = {
sendVerificationEmail: () => {},
sendPasswordResetEmail: () => Promise.resolve(),
@@ -467,7 +467,7 @@ describe('Email Verification Token Expiration: ', () => {
await user.save(null, { useMasterKey: true });
});
it('beforeSave options do not change existing behaviour', async () => {
it_id('a8c1f820-822f-4a37-9d08-a968cac8369d')(it)('beforeSave options do not change existing behaviour', async () => {
let sendEmailOptions;
const emailAdapter = {
sendVerificationEmail: options => {
@@ -509,7 +509,7 @@ describe('Email Verification Token Expiration: ', () => {
expect(emailSpy).toHaveBeenCalled();
});
it('unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful', done => {
it_id('36d277eb-ec7c-4a39-9108-435b68228741')(it)('unsets the _email_verify_token_expires_at and _email_verify_token fields in the User class if email verification is successful', done => {
const user = new Parse.User();
let sendEmailOptions;
const emailAdapter = {
@@ -567,7 +567,7 @@ describe('Email Verification Token Expiration: ', () => {
});
});
it('clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show email verify email success', done => {
it_id('4f444704-ec4b-4dff-b947-614b1c6971c4')(it)('clicking on the email verify link by an email VERIFIED user that was setup before enabling the expire email verify token should show email verify email success', done => {
const user = new Parse.User();
let sendEmailOptions;
const emailAdapter = {
@@ -679,7 +679,7 @@ describe('Email Verification Token Expiration: ', () => {
});
});
it('setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set', done => {
it_id('b6c87f35-d887-477d-bc86-a9217a424f53')(it)('setting the email on the user should set a new email verification token and new expiration date for the token when expire email verify token flag is set', done => {
const user = new Parse.User();
let userBeforeEmailReset;
@@ -755,7 +755,7 @@ describe('Email Verification Token Expiration: ', () => {
});
});
it('should send a new verification email when a resend is requested and the user is UNVERIFIED', done => {
it_id('28f2140d-48bd-44ac-a141-ba60ea8d9713')(it)('should send a new verification email when a resend is requested and the user is UNVERIFIED', done => {
const user = new Parse.User();
let sendEmailOptions;
let sendVerificationEmailCallCount = 0;
@@ -916,7 +916,7 @@ describe('Email Verification Token Expiration: ', () => {
done();
});
it('should match codes with emailVerifyTokenReuseIfValid', async done => {
it_id('0e66b7f6-2c07-4117-a8b9-605aa31a1e29')(it)('should match codes with emailVerifyTokenReuseIfValid', async done => {
let sendEmailOptions;
let sendVerificationEmailCallCount = 0;
const emailAdapter = {
@@ -982,7 +982,7 @@ describe('Email Verification Token Expiration: ', () => {
done();
});
it('should not send a new verification email when a resend is requested and the user is VERIFIED', done => {
it_id('1ed9a6c2-bebc-4813-af30-4f4a212544c2')(it)('should not send a new verification email when a resend is requested and the user is VERIFIED', done => {
const user = new Parse.User();
let sendEmailOptions;
let sendVerificationEmailCallCount = 0;
@@ -1223,7 +1223,7 @@ describe('Email Verification Token Expiration: ', () => {
});
});
it('emailVerified should be set to false after changing from an already verified email', done => {
it_id('b082d387-4974-4d45-a0d9-0c85ca2d7cbf')(it)('emailVerified should be set to false after changing from an already verified email', done => {
const user = new Parse.User();
let sendEmailOptions;
const emailAdapter = {
+69 -13
View File
@@ -21,11 +21,14 @@ const mockAdapter = {
// Small additional tests to improve overall coverage
describe('FilesController', () => {
it('should properly expand objects', done => {
it('should properly expand objects with sync getFileLocation', async () => {
const config = Config.get(Parse.applicationId);
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
gridFSAdapter.getFileLocation = (config, filename) => {
return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename);
}
const filesController = new FilesController(gridFSAdapter);
const result = filesController.expandFilesInObject(config, function () {});
const result = await filesController.expandFilesInObject(config, function () { });
expect(result).toBeUndefined();
@@ -37,12 +40,69 @@ describe('FilesController', () => {
const anObject = {
aFile: fullFile,
};
filesController.expandFilesInObject(config, anObject);
await filesController.expandFilesInObject(config, anObject);
expect(anObject.aFile.url).toEqual('http://an.url');
done();
});
it('should properly expand objects with async getFileLocation', async () => {
const config = Config.get(Parse.applicationId);
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
gridFSAdapter.getFileLocation = async (config, filename) => {
await Promise.resolve();
return config.mount + '/files/' + config.applicationId + '/' + encodeURIComponent(filename);
}
const filesController = new FilesController(gridFSAdapter);
const result = await filesController.expandFilesInObject(config, function () { });
expect(result).toBeUndefined();
const fullFile = {
type: '__type',
url: 'http://an.url',
};
const anObject = {
aFile: fullFile,
};
await filesController.expandFilesInObject(config, anObject);
expect(anObject.aFile.url).toEqual('http://an.url');
});
it('should call getFileLocation when config.fileKey is undefined', async () => {
const config = {};
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
const fullFile = {
name: 'mock-name',
__type: 'File',
};
gridFSAdapter.getFileLocation = jasmine.createSpy('getFileLocation').and.returnValue(Promise.resolve('mock-url'));
const filesController = new FilesController(gridFSAdapter);
const anObject = { aFile: fullFile };
await filesController.expandFilesInObject(config, anObject);
expect(gridFSAdapter.getFileLocation).toHaveBeenCalledWith(config, fullFile.name);
expect(anObject.aFile.url).toEqual('mock-url');
});
it('should call getFileLocation when config.fileKey is defined', async () => {
const config = { fileKey: 'mock-key' };
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
const fullFile = {
name: 'mock-name',
__type: 'File',
};
gridFSAdapter.getFileLocation = jasmine.createSpy('getFileLocation').and.returnValue(Promise.resolve('mock-url'));
const filesController = new FilesController(gridFSAdapter);
const anObject = { aFile: fullFile };
await filesController.expandFilesInObject(config, anObject);
expect(gridFSAdapter.getFileLocation).toHaveBeenCalledWith(config, fullFile.name);
expect(anObject.aFile.url).toEqual('mock-url');
});
it_only_db('mongo')('should pass databaseOptions to GridFSBucketAdapter', async () => {
await reconfigureServer({
databaseURI: 'mongodb://localhost:27017/parse',
@@ -101,7 +161,7 @@ describe('FilesController', () => {
done();
});
it('should add a unique hash to the file name when the preserveFileName option is false', done => {
it('should add a unique hash to the file name when the preserveFileName option is false', async () => {
const config = Config.get(Parse.applicationId);
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
spyOn(gridFSAdapter, 'createFile');
@@ -112,17 +172,15 @@ describe('FilesController', () => {
preserveFileName: false,
});
filesController.createFile(config, fileName);
await filesController.createFile(config, fileName);
expect(gridFSAdapter.createFile).toHaveBeenCalledTimes(1);
expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toMatch(
`^.{32}_${regexEscapedFileName}$`
);
done();
});
it('should not add a unique hash to the file name when the preserveFileName option is true', done => {
it('should not add a unique hash to the file name when the preserveFileName option is true', async () => {
const config = Config.get(Parse.applicationId);
const gridFSAdapter = new GridFSBucketAdapter('mongodb://localhost:27017/parse');
spyOn(gridFSAdapter, 'createFile');
@@ -132,12 +190,10 @@ describe('FilesController', () => {
preserveFileName: true,
});
filesController.createFile(config, fileName);
await filesController.createFile(config, fileName);
expect(gridFSAdapter.createFile).toHaveBeenCalledTimes(1);
expect(gridFSAdapter.createFile.calls.mostRecent().args[0]).toEqual(fileName);
done();
});
it('should handle adapter without getMetadata', async () => {
+12 -8
View File
@@ -45,8 +45,12 @@ describe('Idempotency', () => {
});
});
afterEach(() => {
jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 10000;
});
// Tests
it('should enforce idempotency for cloud code function', async () => {
it_id('e25955fd-92eb-4b22-b8b7-38980e5cb223')(it)('should enforce idempotency for cloud code function', async () => {
let counter = 0;
Parse.Cloud.define('myFunction', () => {
counter++;
@@ -69,7 +73,7 @@ describe('Idempotency', () => {
expect(counter).toBe(1);
});
it('should delete request entry after TTL', async () => {
it_id('be2fbe16-8178-485e-9a12-6fb541096480')(it)('should delete request entry after TTL', async () => {
let counter = 0;
Parse.Cloud.define('myFunction', () => {
counter++;
@@ -119,7 +123,7 @@ describe('Idempotency', () => {
}
);
it('should enforce idempotency for cloud code jobs', async () => {
it_id('e976d0cc-a57f-45d4-9472-b9b052db6490')(it)('should enforce idempotency for cloud code jobs', async () => {
let counter = 0;
Parse.Cloud.job('myJob', () => {
counter++;
@@ -141,7 +145,7 @@ describe('Idempotency', () => {
expect(counter).toBe(1);
});
it('should enforce idempotency for class object creation', async () => {
it_id('7c84a3d4-e1b6-4a0d-99f1-af3cf1a6b3d8')(it)('should enforce idempotency for class object creation', async () => {
let counter = 0;
Parse.Cloud.afterSave('MyClass', () => {
counter++;
@@ -163,7 +167,7 @@ describe('Idempotency', () => {
expect(counter).toBe(1);
});
it('should enforce idempotency for user object creation', async () => {
it_id('a030f2dd-5d21-46ac-b53d-9d714f35d72a')(it)('should enforce idempotency for user object creation', async () => {
let counter = 0;
Parse.Cloud.afterSave('_User', () => {
counter++;
@@ -189,7 +193,7 @@ describe('Idempotency', () => {
expect(counter).toBe(1);
});
it('should enforce idempotency for installation object creation', async () => {
it_id('064c469b-091c-4ba9-9043-be461f26a3eb')(it)('should enforce idempotency for installation object creation', async () => {
let counter = 0;
Parse.Cloud.afterSave('_Installation', () => {
counter++;
@@ -215,7 +219,7 @@ describe('Idempotency', () => {
expect(counter).toBe(1);
});
it('should not interfere with calls of different request ID', async () => {
it_id('f11670b6-fa9c-4f21-a268-ae4b6bbff7fd')(it)('should not interfere with calls of different request ID', async () => {
let counter = 0;
Parse.Cloud.afterSave('MyClass', () => {
counter++;
@@ -236,7 +240,7 @@ describe('Idempotency', () => {
expect(counter).toBe(100);
});
it('should re-throw any other error unchanged when writing request entry fails for any other reason', async () => {
it_id('0ecd2cd2-dafb-4a2b-bb2b-9ad4c9aca777')(it)('should re-throw any other error unchanged when writing request entry fails for any other reason', async () => {
spyOn(rest, 'create').and.rejectWith(new Parse.Error(0, 'some other error'));
Parse.Cloud.define('myFunction', () => {});
const params = {
+3 -3
View File
@@ -75,7 +75,7 @@ describe_only(() => {
/**
* Verifies simple passwords in GET login requests with special characters are scrubbed from the verbose log
*/
it('does scrub simple passwords on GET login', done => {
it_id('e36d6141-2a20-41d0-85fc-d1534c3e4bae')(it)('does scrub simple passwords on GET login', done => {
reconfigureServer({
verbose: true,
}).then(function () {
@@ -105,7 +105,7 @@ describe_only(() => {
/**
* Verifies complex passwords in GET login requests with special characters are scrubbed from the verbose log
*/
it('does scrub complex passwords on GET login', done => {
it_id('24b277c5-250f-4a35-a449-2c8c519d4c03')(it)('does scrub complex passwords on GET login', done => {
reconfigureServer({
verbose: true,
})
@@ -139,7 +139,7 @@ describe_only(() => {
/**
* Verifies fields in POST login requests are NOT present in the verbose log
*/
it('does not have password field in POST login', done => {
it_id('33143ec9-b32d-467c-ba65-ff2bbefdaadd')(it)('does not have password field in POST login', done => {
reconfigureServer({
verbose: true,
}).then(function () {
+5 -5
View File
@@ -32,7 +32,7 @@ describe('middlewares', () => {
AppCache.del(fakeReq.body._ApplicationId);
});
it('should use _ContentType if provided', done => {
it_id('4cc18d90-1763-4725-97fa-f63fb4692fc4')(it)('should use _ContentType if provided', done => {
AppCachePut(fakeReq.body._ApplicationId, {
masterKeyIps: ['127.0.0.1'],
});
@@ -128,7 +128,7 @@ describe('middlewares', () => {
const otherKeys = BodyKeys.filter(
otherKey => otherKey !== infoKey && otherKey !== 'javascriptKey'
);
it(`it should pull ${bodyKey} into req.info`, done => {
it_id('f9abd7ac-b1f4-4607-b9b0-365ff0559d84')(it)(`it should pull ${bodyKey} into req.info`, done => {
AppCachePut(fakeReq.body._ApplicationId, {
masterKeyIps: ['0.0.0.0/0'],
});
@@ -147,7 +147,7 @@ describe('middlewares', () => {
});
});
it('should not succeed and log if the ip does not belong to masterKeyIps list', async () => {
it_id('4a0bce41-c536-4482-a873-12ed023380e2')(it)('should not succeed and log if the ip does not belong to masterKeyIps list', async () => {
const logger = require('../lib/logger').logger;
spyOn(logger, 'error').and.callFake(() => {});
AppCachePut(fakeReq.body._ApplicationId, {
@@ -197,7 +197,7 @@ describe('middlewares', () => {
);
});
it('should succeed if the ip does belong to masterKeyIps list', async () => {
it_id('2f7fadec-a87c-4626-90d1-65c75653aea9')(it)('should succeed if the ip does belong to masterKeyIps list', async () => {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['10.0.0.1'],
@@ -208,7 +208,7 @@ describe('middlewares', () => {
expect(fakeReq.auth.isMaster).toBe(true);
});
it('should allow any ip to use masterKey if masterKeyIps is empty', async () => {
it_id('2b251fd4-d43c-48f4-ada9-c8458e40c12a')(it)('should allow any ip to use masterKey if masterKeyIps is empty', async () => {
AppCachePut(fakeReq.body._ApplicationId, {
masterKey: 'masterKey',
masterKeyIps: ['0.0.0.0/0'],
+4 -4
View File
@@ -738,7 +738,7 @@ describe('Pages Router', () => {
);
});
it('localizes end-to-end for verify email: success', async () => {
it_id('2845c2ea-23ba-45d2-a33f-63181d419bca')(it)('localizes end-to-end for verify email: success', async () => {
await reconfigureServer(config);
const sendVerificationEmail = spyOn(
config.emailAdapter,
@@ -767,7 +767,7 @@ describe('Pages Router', () => {
);
});
it('localizes end-to-end for verify email: invalid verification link - link send success', async () => {
it_id('f2272b94-b4ac-474f-8e47-1ca74de136f5')(it)('localizes end-to-end for verify email: invalid verification link - link send success', async () => {
await reconfigureServer(config);
const sendVerificationEmail = spyOn(
config.emailAdapter,
@@ -821,7 +821,7 @@ describe('Pages Router', () => {
);
});
it('localizes end-to-end for verify email: invalid verification link - link send fail', async () => {
it_id('1d46d36a-e455-4ae7-8717-e0d286e95f02')(it)('localizes end-to-end for verify email: invalid verification link - link send fail', async () => {
await reconfigureServer(config);
const sendVerificationEmail = spyOn(
config.emailAdapter,
@@ -1183,7 +1183,7 @@ describe('Pages Router', () => {
);
});
it('email verification works with custom endpoint', async () => {
it_id('81c1c28e-5dfd-4ffb-a09b-283156c08483')(it)('email verification works with custom endpoint', async () => {
config.pages.pagesEndpoint = 'customEndpoint';
await reconfigureServer(config);
const sendVerificationEmail = spyOn(
+4 -4
View File
@@ -111,7 +111,7 @@ const setup = function () {
};
describe('Parse.Push', () => {
it('should properly send push', async () => {
it_id('d1e591c4-2b21-466b-9ee2-5be467b6b771')(it)('should properly send push', async () => {
const { sendToInstallationSpy } = await setup();
const pushStatusId = await Parse.Push.send({
where: {
@@ -126,7 +126,7 @@ describe('Parse.Push', () => {
expect(sendToInstallationSpy.calls.count()).toEqual(10);
});
it('should properly send push with lowercaseIncrement', async () => {
it_id('2a58e3c7-b6f3-4261-a384-6c893b2ac3f3')(it)('should properly send push with lowercaseIncrement', async () => {
await setup();
const pushStatusId = await Parse.Push.send({
where: {
@@ -140,7 +140,7 @@ describe('Parse.Push', () => {
await pushCompleted(pushStatusId);
});
it('should not allow clients to query _PushStatus', async () => {
it_id('e21780b6-2cdd-467e-8013-81030f3288e9')(it)('should not allow clients to query _PushStatus', async () => {
await setup();
const pushStatusId = await Parse.Push.send({
where: {
@@ -166,7 +166,7 @@ describe('Parse.Push', () => {
}
});
it('should allow master key to query _PushStatus', async () => {
it_id('924cf5f5-f684-4925-978a-e52c0c457366')(it)('should allow master key to query _PushStatus', async () => {
await setup();
const pushStatusId = await Parse.Push.send({
where: {
+8 -8
View File
@@ -163,7 +163,7 @@ describe('miscellaneous', function () {
expect(numCreated).toBe(1);
});
it('ensure that if people already have duplicate users, they can still sign up new users', async done => {
it_id('be1b9ac7-5e5f-4e91-b044-2bd8fb7622ad')(it)('ensure that if people already have duplicate users, they can still sign up new users', async done => {
try {
await Parse.User.logOut();
} catch (e) {
@@ -209,7 +209,7 @@ describe('miscellaneous', function () {
});
});
it('ensure that if people already have duplicate emails, they can still sign up new users', done => {
it_id('d00f907e-41b9-40f6-8168-63e832199a8c')(it)('ensure that if people already have duplicate emails, they can still sign up new users', done => {
const config = Config.get('test');
// Remove existing data to clear out unique index
TestUtils.destroyAllDataPermanently()
@@ -289,7 +289,7 @@ describe('miscellaneous', function () {
}, fail);
});
it('increment with a user object', function (done) {
it_id('33db6efe-7c02-496c-8595-0ef627a94103')(it)('increment with a user object', function (done) {
createTestUser()
.then(user => {
user.increment('foo');
@@ -317,7 +317,7 @@ describe('miscellaneous', function () {
);
});
it('save various data types', function (done) {
it_id('bef99522-bcfd-4f79-ba9e-3c3845550401')(it)('save various data types', function (done) {
const obj = new TestObject();
obj.set('date', new Date());
obj.set('array', [1, 2, 3]);
@@ -951,7 +951,7 @@ describe('miscellaneous', function () {
);
});
it('return the updated fields on PUT', async () => {
it_id('e9e718a9-4465-4158-b13e-f146855a8892')(it)('return the updated fields on PUT', async () => {
const obj = new Parse.Object('GameScore');
const pointer = new Parse.Object('Child');
await pointer.save();
@@ -1023,7 +1023,7 @@ describe('miscellaneous', function () {
expect(body.updatedAt).not.toBeUndefined();
});
it('should response should not change with triggers', async () => {
it_id('ea358b59-03c0-45c9-abc7-1fdd67573029')(it)('should response should not change with triggers', async () => {
const obj = new Parse.Object('GameScore');
const pointer = new Parse.Object('Child');
Parse.Cloud.beforeSave('GameScore', request => {
@@ -1491,7 +1491,7 @@ describe('miscellaneous', function () {
});
});
it('properly returns incremented values (#1554)', done => {
it_id('b2cd9cf2-13fa-4acd-aaa9-6f81fc1858db')(it)('properly returns incremented values (#1554)', done => {
const headers = {
'Content-Type': 'application/json',
'X-Parse-Application-Id': 'test',
@@ -1680,7 +1680,7 @@ describe('miscellaneous', function () {
});
});
it('purge all objects in class', done => {
it_id('8f99ee20-3da7-45ec-b867-ea0eb87524a9')(it)('purge all objects in class', done => {
const object = new Parse.Object('TestObject');
object.set('foo', 'bar');
const object2 = new Parse.Object('TestObject');
+11 -11
View File
@@ -93,7 +93,7 @@ describe('Parse.GeoPoint testing', () => {
);
});
it('geo line', async done => {
it_id('bbd9e2f6-7f61-458f-98f2-4a563586cd8d')(it)('geo line', async done => {
const line = [];
for (let i = 0; i < 10; ++i) {
const obj = new TestObject();
@@ -143,7 +143,7 @@ describe('Parse.GeoPoint testing', () => {
);
});
it('geo max distance medium', async () => {
it_id('e1e86b38-b8a4-4109-8330-a324fe628e0c')(it)('geo max distance medium', async () => {
const objects = [];
[0, 1, 2].map(function (i) {
const obj = new TestObject();
@@ -207,7 +207,7 @@ describe('Parse.GeoPoint testing', () => {
done();
});
it('geo max distance in km california', async () => {
it_id('05f1a454-56b1-4f2e-908e-408a9222cbae')(it)('geo max distance in km california', async () => {
await makeSomeGeoPoints();
const sfo = new Parse.GeoPoint(37.6189722, -122.3748889);
const query = new Parse.Query(TestObject);
@@ -246,7 +246,7 @@ describe('Parse.GeoPoint testing', () => {
equal(results.length, 3);
});
it('geo max distance in miles california', async () => {
it_id('9ee376ad-dd6c-4c17-ad28-c7899a4411f1')(it)('geo max distance in miles california', async () => {
await makeSomeGeoPoints();
const sfo = new Parse.GeoPoint(37.6189722, -122.3748889);
const query = new Parse.Query(TestObject);
@@ -276,7 +276,7 @@ describe('Parse.GeoPoint testing', () => {
equal(results.length, 0);
});
it('returns nearest location', async () => {
it_id('9e35a89e-bc2c-4ec5-b25a-8d1890a55233')(it)('returns nearest location', async () => {
await makeSomeGeoPoints();
const sfo = new Parse.GeoPoint(37.6189722, -122.3748889);
const query = new Parse.Query(TestObject);
@@ -286,7 +286,7 @@ describe('Parse.GeoPoint testing', () => {
equal(results[1].get('name'), 'Sacramento');
});
it('works with geobox queries', done => {
it_id('6df434b0-142d-4302-bbc6-a6ec5a9d9c68')(it)('works with geobox queries', done => {
const inbound = new Parse.GeoPoint(1.5, 1.5);
const onbound = new Parse.GeoPoint(10, 10);
const outbound = new Parse.GeoPoint(20, 20);
@@ -356,7 +356,7 @@ describe('Parse.GeoPoint testing', () => {
});
});
it('supports withinPolygon open path', done => {
it_id('d9fbc5c6-f767-47d6-bb44-3858eb9df15a')(it)('supports withinPolygon open path', done => {
const inbound = new Parse.GeoPoint(1.5, 1.5);
const onbound = new Parse.GeoPoint(10, 10);
const outbound = new Parse.GeoPoint(20, 20);
@@ -394,7 +394,7 @@ describe('Parse.GeoPoint testing', () => {
}, done.fail);
});
it('supports withinPolygon closed path', done => {
it_id('3ec537bd-839a-4c93-a48b-b4a249820074')(it)('supports withinPolygon closed path', done => {
const inbound = new Parse.GeoPoint(1.5, 1.5);
const onbound = new Parse.GeoPoint(10, 10);
const outbound = new Parse.GeoPoint(20, 20);
@@ -433,7 +433,7 @@ describe('Parse.GeoPoint testing', () => {
}, done.fail);
});
it('supports withinPolygon Polygon object', done => {
it_id('0a248e11-3598-480a-9ab5-8a0b259258e4')(it)('supports withinPolygon Polygon object', done => {
const inbound = new Parse.GeoPoint(1.5, 1.5);
const onbound = new Parse.GeoPoint(10, 10);
const outbound = new Parse.GeoPoint(20, 20);
@@ -752,7 +752,7 @@ describe('Parse.GeoPoint testing', () => {
equal(count, 1);
});
it('withinKilometers complex supports count', async () => {
it_id('0b073d31-0d41-41e7-bd60-f636ffb759dc')(it)('withinKilometers complex supports count', async () => {
const inside = new Parse.GeoPoint(10, 10);
const middle = new Parse.GeoPoint(20, 20);
const outside = new Parse.GeoPoint(30, 30);
@@ -770,7 +770,7 @@ describe('Parse.GeoPoint testing', () => {
equal(count, 2);
});
it('fails to fetch geopoints that are specifically not at (0,0)', async () => {
it_id('26c9a13d-3d71-452e-a91c-9a4589be021c')(it)('fails to fetch geopoints that are specifically not at (0,0)', async () => {
const tmp = new TestObject({
location: new Parse.GeoPoint({ latitude: 0, longitude: 0 }),
});
+32 -2
View File
@@ -28,7 +28,7 @@ describe('a GlobalConfig', () => {
},
query,
{
params: { companies: ['US', 'DK'], internalParam: 'internal' },
params: { companies: ['US', 'DK'], counter: 20, internalParam: 'internal' },
masterKeyOnly: { internalParam: true },
}
)
@@ -114,6 +114,34 @@ describe('a GlobalConfig', () => {
});
});
it_only_db('mongo')('can addUnique', async () => {
await Parse.Config.save({ companies: { __op: 'AddUnique', objects: ['PA', 'RS', 'E'] } });
const config = await Parse.Config.get();
const companies = config.get('companies');
expect(companies).toEqual(['US', 'DK', 'PA', 'RS', 'E']);
});
it_only_db('mongo')('can add to array', async () => {
await Parse.Config.save({ companies: { __op: 'Add', objects: ['PA'] } });
const config = await Parse.Config.get();
const companies = config.get('companies');
expect(companies).toEqual(['US', 'DK', 'PA']);
});
it_only_db('mongo')('can remove from array', async () => {
await Parse.Config.save({ companies: { __op: 'Remove', objects: ['US'] } });
const config = await Parse.Config.get();
const companies = config.get('companies');
expect(companies).toEqual(['DK']);
});
it('can increment', async () => {
await Parse.Config.save({ counter: { __op: 'Increment', amount: 49 } });
const config = await Parse.Config.get();
const counter = config.get('counter');
expect(counter).toEqual(69);
});
it('can add and retrive files', done => {
request({
method: 'PUT',
@@ -157,7 +185,7 @@ describe('a GlobalConfig', () => {
});
});
it('properly handles delete op', done => {
it_id('5ebbd0cf-d1a5-49d9-aac7-5216abc5cb62')(it)('properly handles delete op', done => {
request({
method: 'PUT',
url: 'http://localhost:8378/1/config',
@@ -165,6 +193,7 @@ describe('a GlobalConfig', () => {
body: {
params: {
companies: { __op: 'Delete' },
counter: { __op: 'Delete' },
internalParam: { __op: 'Delete' },
foo: 'bar',
},
@@ -183,6 +212,7 @@ describe('a GlobalConfig', () => {
try {
expect(response.status).toEqual(200);
expect(body.params.companies).toBeUndefined();
expect(body.params.counter).toBeUndefined();
expect(body.params.foo).toBe('bar');
expect(Object.keys(body.params).length).toBe(1);
} catch (e) {
+2 -2
View File
@@ -500,7 +500,7 @@ describe('ParseGraphQLSchema', () => {
});
});
describe('alias', () => {
it('Should be able to define alias for get and find query', async () => {
it_id('45282d26-f4c7-4d2d-a7b6-cd8741d5322f')(it)('Should be able to define alias for get and find query', async () => {
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
@@ -535,7 +535,7 @@ describe('ParseGraphQLSchema', () => {
expect(Object.keys(queries1)).toContain('precious_data');
});
it('Should be able to define alias for mutation', async () => {
it_id('f04b46e3-a25d-401d-a315-3298cfee1df8')(it)('Should be able to define alias for mutation', async () => {
const parseGraphQLSchema = new ParseGraphQLSchema({
databaseController,
parseGraphQLController,
+22 -22
View File
@@ -130,7 +130,7 @@ describe('ParseGraphQLServer', () => {
set: () => {},
};
it("should return schema and context with req's info, config and auth", async () => {
it_id('0696675e-060f-414f-bc77-9d57f31807f5')(it)('should return schema and context with req\'s info, config and auth', async () => {
const options = await parseGraphQLServer._getGraphQLOptions();
expect(options.schema).toEqual(parseGraphQLServer.parseGraphQLSchema.graphQLSchema);
const contextResponse = await options.context({ req, res });
@@ -1395,7 +1395,7 @@ describe('ParseGraphQLServer', () => {
await resetGraphQLCache();
});
it('should only include types in the enabledForClasses list', async () => {
it_id('d6a23a2f-ca18-4b15-bc73-3e636f99e6bc')(it)('should only include types in the enabledForClasses list', async () => {
const schemaController = await parseServer.config.databaseController.loadSchema();
await schemaController.addClassIfNotExists('SuperCar', {
foo: { type: 'String' },
@@ -1426,7 +1426,7 @@ describe('ParseGraphQLServer', () => {
expect(data.userType).toBeNull();
expect(data.superCarType).toBeTruthy();
});
it('should not include types in the disabledForClasses list', async () => {
it_id('1db2aceb-d24e-4929-ba43-8dbb5d0395e1')(it)('should not include types in the disabledForClasses list', async () => {
const schemaController = await parseServer.config.databaseController.loadSchema();
await schemaController.addClassIfNotExists('SuperCar', {
foo: { type: 'String' },
@@ -1457,7 +1457,7 @@ describe('ParseGraphQLServer', () => {
expect(data.superCarType).toBeNull();
expect(data.userType).toBeTruthy();
});
it('should remove query operations when disabled', async () => {
it_id('85c2e02f-0239-4819-b66e-392e0125f6c5')(it)('should remove query operations when disabled', async () => {
const superCar = new Parse.Object('SuperCar');
await superCar.save({ foo: 'bar' });
const customer = new Parse.Object('Customer');
@@ -1563,7 +1563,7 @@ describe('ParseGraphQLServer', () => {
).toBeRejected();
});
it('should remove mutation operations, create, update and delete, when disabled', async () => {
it_id('972161a6-8108-4e99-a1a5-71d0267d26c2')(it)('should remove mutation operations, create, update and delete, when disabled', async () => {
const superCar1 = new Parse.Object('SuperCar');
await superCar1.save({ foo: 'bar' });
const customer1 = new Parse.Object('Customer');
@@ -1735,7 +1735,7 @@ describe('ParseGraphQLServer', () => {
).toBeRejected();
});
it('should only allow the supplied create and update fields for a class', async () => {
it_id('4af763b1-ff86-43c7-ba30-060a1c07e730')(it)('should only allow the supplied create and update fields for a class', async () => {
const schemaController = await parseServer.config.databaseController.loadSchema();
await schemaController.addClassIfNotExists('SuperCar', {
engine: { type: 'String' },
@@ -1823,7 +1823,7 @@ describe('ParseGraphQLServer', () => {
expect(updatedSuperCar).toBeTruthy();
});
it('should handle required fields from the Parse class', async () => {
it_id('fc9237e9-3e63-4b55-9c1d-e6269f613a93')(it)('should handle required fields from the Parse class', async () => {
const schemaController = await parseServer.config.databaseController.loadSchema();
await schemaController.addClassIfNotExists('SuperCar', {
engine: { type: 'String', required: true },
@@ -1875,7 +1875,7 @@ describe('ParseGraphQLServer', () => {
expect(__type2.fields.find(o => o.name === 'doors').type.kind).toEqual('NON_NULL');
});
it('should only allow the supplied output fields for a class', async () => {
it_id('83b6895a-7dfd-4e3b-a5ce-acdb1fa39705')(it)('should only allow the supplied output fields for a class', async () => {
const schemaController = await parseServer.config.databaseController.loadSchema();
await schemaController.addClassIfNotExists('SuperCar', {
@@ -1992,7 +1992,7 @@ describe('ParseGraphQLServer', () => {
expect(getSuperCar.objectId).toBe(superCar.id);
});
it('should only allow the supplied constraint fields for a class', async () => {
it_id('67dfcf94-92fb-45a3-a012-3b22c81899ba')(it)('should only allow the supplied constraint fields for a class', async () => {
try {
const schemaController = await parseServer.config.databaseController.loadSchema();
@@ -2067,7 +2067,7 @@ describe('ParseGraphQLServer', () => {
}
});
it('should only allow the supplied sort fields for a class', async () => {
it_id('a3bdbd5d-8779-42fe-91a1-7a7f90a6177b')(it)('should only allow the supplied sort fields for a class', async () => {
const schemaController = await parseServer.config.databaseController.loadSchema();
await schemaController.addClassIfNotExists('SuperCar', {
@@ -4921,7 +4921,7 @@ describe('ParseGraphQLServer', () => {
).toEqual(['someValue1', 'someValue2']);
});
it('should support full text search', async () => {
it_id('accc59be-fd13-46c5-a103-ec63f2ad6670')(it)('should support full text search', async () => {
try {
const obj = new Parse.Object('FullTextSearchTest');
obj.set('field1', 'Parse GraphQL Server');
@@ -5032,7 +5032,7 @@ describe('ParseGraphQLServer', () => {
}
});
it('should support order, skip and first arguments', async () => {
it_id('0fd03d3c-a2c8-4fac-95cc-2391a3032ca2')(it)('should support order, skip and first arguments', async () => {
const promises = [];
for (let i = 0; i < 100; i++) {
const obj = new Parse.Object('SomeClass');
@@ -5079,7 +5079,7 @@ describe('ParseGraphQLServer', () => {
]);
});
it('should support pagination', async () => {
it_id('588a70c6-2932-4d3b-a838-a74c59d8cffb')(it)('should support pagination', async () => {
const numberArray = (first, last) => {
const array = [];
for (let i = first; i <= last; i++) {
@@ -5221,7 +5221,7 @@ describe('ParseGraphQLServer', () => {
expect(result.data.someClasses.pageInfo.hasNextPage).toEqual(true);
});
it('should support count', async () => {
it_id('4f6a5f20-9642-4cf0-b31d-e739672a9096')(it)('should support count', async () => {
await prepareData();
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
@@ -5324,7 +5324,7 @@ describe('ParseGraphQLServer', () => {
expect(result.data.find.count).toEqual(2);
});
it('should respect max limit', async () => {
it_id('942b57be-ca8a-4a5b-8104-2adef8743b1a')(it)('should respect max limit', async () => {
parseServer = await global.reconfigureServer({
maxLimit: 10,
});
@@ -5365,7 +5365,7 @@ describe('ParseGraphQLServer', () => {
expect(result.data.find.count).toEqual(100);
});
it('should support keys argument', async () => {
it_id('952634f0-0ad5-4a08-8da2-187c1bd9ee94')(it)('should support keys argument', async () => {
await prepareData();
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
@@ -5771,7 +5771,7 @@ describe('ParseGraphQLServer', () => {
).toEqual([object3.id, object1.id, object2.id]);
});
it('should support including relation', async () => {
it_id('47a6adf3-1cb4-4d92-b74c-e480363f9cb5')(it)('should support including relation', async () => {
await prepareData();
await parseGraphQLServer.parseGraphQLSchema.schemaCache.clear();
@@ -6676,7 +6676,7 @@ describe('ParseGraphQLServer', () => {
});
});
it('should unset fields when null used on update/create', async () => {
it_id('f722e98e-1fd7-45c5-ade3-5177e3d542e8')(it)('should unset fields when null used on update/create', async () => {
const customerSchema = new Parse.Schema('Customer');
customerSchema.addString('aString');
customerSchema.addBoolean('aBoolean');
@@ -8320,7 +8320,7 @@ describe('ParseGraphQLServer', () => {
expect(schema.fields.updatedAt.type).toEqual('Date');
});
it('should support ACL', async () => {
it_id('93e748f6-ad9b-4c31-8e1e-c5685e2382fb')(it)('should support ACL', async () => {
const someClass = new Parse.Object('SomeClass');
await someClass.save();
@@ -9074,7 +9074,7 @@ describe('ParseGraphQLServer', () => {
expect(result2.companies.edges[0].node.objectId).toEqual(company1.id);
});
it('should support relational where query', async () => {
it_id('f4312f2c-90bb-4583-b033-02078ae0ce84')(it)('should support relational where query', async () => {
const president = new Parse.Object('President');
president.set('name', 'James');
await president.save();
@@ -10374,7 +10374,7 @@ describe('ParseGraphQLServer', () => {
}
});
it('should support Bytes', async () => {
it_id('43303db7-c5a7-4bc0-91c3-57e03fffa225')(it)('should support Bytes', async () => {
try {
const someFieldValue = 'aGVsbG8gd29ybGQ=';
@@ -10461,7 +10461,7 @@ describe('ParseGraphQLServer', () => {
}
});
it('should support Geo Points', async () => {
it_id('6a253e47-6959-4427-b841-c0c1fa77cf01')(it)('should support Geo Points', async () => {
try {
const someFieldValue = {
__typename: 'GeoPoint',
+12 -12
View File
@@ -54,7 +54,7 @@ describe('Hooks', () => {
);
});
it('should CRUD a function registration', done => {
it_id('26c9a13d-3d71-452e-a91c-9a4589be021c')(it)('should CRUD a function registration', done => {
// Create
Parse.Hooks.createFunction('My-Test-Function', 'http://someurl')
.then(response => {
@@ -98,7 +98,7 @@ describe('Hooks', () => {
});
});
it('should CRUD a trigger registration', done => {
it_id('7a81069e-2ee9-47fb-8e27-1120eda09e99')(it)('should CRUD a trigger registration', done => {
// Create
Parse.Hooks.createTrigger('MyClass', 'beforeDelete', 'http://someurl')
.then(
@@ -188,7 +188,7 @@ describe('Hooks', () => {
});
});
it('should fail trying to create two times the same function', done => {
it_id('f7ad092f-81dc-4729-afd1-3b02db2f0948')(it)('should fail trying to create two times the same function', done => {
Parse.Hooks.createFunction('my_new_function', 'http://url.com')
.then(() => jasmine.timeout())
.then(
@@ -224,7 +224,7 @@ describe('Hooks', () => {
);
});
it('should fail trying to create two times the same trigger', done => {
it_id('4db8c249-9174-4e8e-b959-55c8ea959a02')(it)('should fail trying to create two times the same trigger', done => {
Parse.Hooks.createTrigger('MyClass', 'beforeSave', 'http://url.com')
.then(
() => {
@@ -359,7 +359,7 @@ describe('Hooks', () => {
});
});
it('should create hooks and properly preload them', done => {
it_id('96d99414-b739-4e36-b3f4-8135e0be83ea')(it)('should create hooks and properly preload them', done => {
const promises = [];
for (let i = 0; i < 5; i++) {
promises.push(
@@ -410,7 +410,7 @@ describe('Hooks', () => {
);
});
it('should run the function on the test server', done => {
it_id('fe7d41eb-e570-4804-ac1f-8b6c407fdafe')(it)('should run the function on the test server', done => {
app.post('/SomeFunction', function (req, res) {
res.json({ success: 'OK!' });
});
@@ -439,7 +439,7 @@ describe('Hooks', () => {
);
});
it('should run the function on the test server (error handling)', done => {
it_id('63985b4c-a212-4a86-aa0e-eb4600bb485b')(it)('should run the function on the test server (error handling)', done => {
app.post('/SomeFunctionError', function (req, res) {
res.json({ error: { code: 1337, error: 'hacking that one!' } });
});
@@ -473,7 +473,7 @@ describe('Hooks', () => {
);
});
it('should provide X-Parse-Webhook-Key when defined', done => {
it_id('bacc1754-2a3a-4a7a-8d0e-f80af36da1ef')(it)('should provide X-Parse-Webhook-Key when defined', done => {
app.post('/ExpectingKey', function (req, res) {
if (req.get('X-Parse-Webhook-Key') === 'hook') {
res.json({ success: 'correct key provided' });
@@ -506,7 +506,7 @@ describe('Hooks', () => {
);
});
it('should not pass X-Parse-Webhook-Key if not provided', done => {
it_id('eeb67946-42c6-4581-89af-2abb4927913e')(it)('should not pass X-Parse-Webhook-Key if not provided', done => {
reconfigureServer({ webhookKey: undefined }).then(() => {
app.post('/ExpectingKeyAlso', function (req, res) {
if (req.get('X-Parse-Webhook-Key') === 'hook') {
@@ -545,7 +545,7 @@ describe('Hooks', () => {
});
});
it('should run the beforeSave hook on the test server', done => {
it_id('21decb65-4b93-4791-85a3-ab124a9ea3ac')(it)('should run the beforeSave hook on the test server', done => {
let triggerCount = 0;
app.post('/BeforeSaveSome', function (req, res) {
triggerCount++;
@@ -576,7 +576,7 @@ describe('Hooks', () => {
});
});
it('beforeSave hooks should correctly handle responses containing entire object', done => {
it_id('52e3152b-5514-4418-9e76-1f394368b8fb')(it)('beforeSave hooks should correctly handle responses containing entire object', done => {
app.post('/BeforeSaveSome2', function (req, res) {
const object = Parse.Object.fromJSON(req.body.object);
object.set('hello', 'world');
@@ -600,7 +600,7 @@ describe('Hooks', () => {
});
});
it('should run the afterSave hook on the test server', done => {
it_id('d27a7587-abb5-40d5-9805-051ee91de474')(it)('should run the afterSave hook on the test server', done => {
let triggerCount = 0;
let newObjectId;
app.post('/AfterSaveSome', function (req, res) {
+4 -4
View File
@@ -406,7 +406,7 @@ describe('Installations', () => {
});
});
it('updating with new channels', done => {
it_id('95955e90-04bc-4437-920e-b84bc30dba01')(it)('updating with new channels', done => {
const input = {
installationId: '12345678-abcd-abcd-abcd-123456789abc',
deviceType: 'android',
@@ -856,7 +856,7 @@ describe('Installations', () => {
});
});
it('update is linking two existing objects w/ increment', done => {
it_id('22311bc7-3f4f-42c1-a958-57083929e80d')(it)('update is linking two existing objects w/ increment', done => {
const installId = '12345678-abcd-abcd-abcd-123456789abc';
const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
let input = {
@@ -969,7 +969,7 @@ describe('Installations', () => {
});
});
it('update is linking two existing with installation id w/ op', done => {
it_id('f2975078-eab7-4287-a932-288842e3cfb9')(it)('update is linking two existing with installation id w/ op', done => {
const installId = '12345678-abcd-abcd-abcd-123456789abc';
const t = '0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef';
let input = {
@@ -1239,7 +1239,7 @@ describe('Installations', () => {
});
});
it('can use push with beforeSave', async () => {
it_id('e581faea-c1b4-4c64-af8c-52287ce6cd06')(it)('can use push with beforeSave', async () => {
const input = {
deviceToken: '11433856eed2f1285fb3aa11136718c1198ed5647875096952c66bf8cb976306',
deviceType: 'ios',
+4 -4
View File
@@ -4,7 +4,7 @@ const UserController = require('../lib/Controllers/UserController').UserControll
const Config = require('../lib/Config');
const ParseServer = require('../lib/index').ParseServer;
const triggers = require('../lib/triggers');
const { resolvingPromise, sleep } = require('./support/testUtils');
const { resolvingPromise, sleep } = require('../lib/TestUtils');
const validatorFail = () => {
throw 'you are not authorized';
};
@@ -878,7 +878,7 @@ describe('ParseLiveQuery', function () {
await expectAsync(query.subscribe()).toBeRejectedWith(new Error('Invalid session token'));
});
it('handle invalid websocket payload length', async done => {
it_id('4ccc9508-ae6a-46ec-932a-9f5e49ab3b9e')(it)('handle invalid websocket payload length', async done => {
await reconfigureServer({
liveQuery: {
classNames: ['TestObject'],
@@ -915,7 +915,7 @@ describe('ParseLiveQuery', function () {
}, 1000);
});
it('should execute live query update on email validation', async done => {
it_id('39a9191f-26dd-4e05-a379-297a67928de8')(it)('should execute live query update on email validation', async done => {
const emailAdapter = {
sendVerificationEmail: () => {},
sendPasswordResetEmail: () => Promise.resolve(),
@@ -1145,7 +1145,7 @@ describe('ParseLiveQuery', function () {
await object.save();
});
it('does shutdown liveQuery server', async () => {
it_id('2f95d8a9-7675-45ba-a4a6-e45cb7efb1fb')(it)('does shutdown liveQuery server', async () => {
await reconfigureServer({ appId: 'test_app_id' });
const config = {
appId: 'hello_test',
+1 -1
View File
@@ -603,7 +603,7 @@ describe('Parse.Object testing', () => {
expect(result.get('items')).toEqual(obj.get('items'));
});
it('can query array nested fields', async () => {
it_id('44097c6f-d0ca-4dc5-aa8a-3dd2d9ac645a')(it)('can query array nested fields', async () => {
const objects = [];
for (let i = 0; i < 10; i++) {
const obj = new TestObject();
+1 -1
View File
@@ -62,7 +62,7 @@ describe('Parse.Polygon testing', () => {
}, done.fail);
});
it('polygon equalTo (open/closed) path', done => {
it_id('3019353b-d5b3-4e53-bcb1-716418328bdd')(it)('polygon equalTo (open/closed) path', done => {
const openPoints = [
[0, 0],
[0, 1],
+59 -59
View File
@@ -95,7 +95,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('group by field', done => {
it_id('add7050f-65d5-4a13-b526-5bd1ee09c7f1')(it)('group by field', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$group: { _id: '$name' },
@@ -115,7 +115,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('group by pipeline operator', async () => {
it_id('0ab0d776-e45d-419a-9b35-3d11933b77d1')(it)('group by pipeline operator', async () => {
const options = Object.assign({}, masterKeyOptions, {
body: {
pipeline: {
@@ -133,7 +133,7 @@ describe('Parse.Query Aggregate testing', () => {
expect(resp.results[2].objectId).not.toBe(undefined);
});
it('group by empty object', done => {
it_id('b6b42145-7eb4-47aa-ada6-8c1444420e07')(it)('group by empty object', done => {
const obj = new TestObject();
const pipeline = [
{
@@ -152,7 +152,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('group by empty string', done => {
it_id('0f5f6869-e675-41b9-9ad2-52b201124fb0')(it)('group by empty string', done => {
const obj = new TestObject();
const pipeline = [
{
@@ -171,7 +171,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('group by empty array', done => {
it_id('b9c4f1b4-47f4-4ff4-88fb-586711f57e4a')(it)('group by empty array', done => {
const obj = new TestObject();
const pipeline = [
{
@@ -190,7 +190,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('group by multiple columns ', done => {
it_id('bf5ee3e5-986c-4994-9c8d-79310283f602')(it)('group by multiple columns ', done => {
const obj1 = new TestObject();
const obj2 = new TestObject();
const obj3 = new TestObject();
@@ -216,7 +216,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('group by date object', done => {
it_id('3e652c61-78e1-4541-83ac-51ad1def9874')(it)('group by date object', done => {
const obj1 = new TestObject();
const obj2 = new TestObject();
const obj3 = new TestObject();
@@ -246,7 +246,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('group by date object transform', done => {
it_id('5d3a0f73-1f49-46f3-9be5-caf1eaefec79')(it)('group by date object transform', done => {
const obj1 = new TestObject();
const obj2 = new TestObject();
const obj3 = new TestObject();
@@ -276,7 +276,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('group by number', done => {
it_id('1f9b10f7-dc0e-467f-b506-a303b9c36258')(it)('group by number', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$group: { _id: '$score' },
@@ -296,7 +296,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it_exclude_dbs(['postgres'])('group and multiply transform', done => {
it_id('c7695018-03de-49e4-8a72-d4d956f70deb')(it_exclude_dbs(['postgres']))('group and multiply transform', done => {
const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 });
const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 });
const pipeline = [
@@ -319,7 +319,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it_exclude_dbs(['postgres'])('project and multiply transform', done => {
it_id('2d278175-7594-4b29-bef4-04c778b7a42f')(it_exclude_dbs(['postgres']))('project and multiply transform', done => {
const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 });
const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 });
const pipeline = [
@@ -351,7 +351,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it_exclude_dbs(['postgres'])('project without objectId transform', done => {
it_id('9c9d9318-3a9e-4c2a-8a09-d3aa52c7505b')(it_exclude_dbs(['postgres']))('project without objectId transform', done => {
const obj1 = new TestObject({ name: 'item a', quantity: 2, price: 10 });
const obj2 = new TestObject({ name: 'item b', quantity: 5, price: 5 });
const pipeline = [
@@ -383,7 +383,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it_exclude_dbs(['postgres'])('project updatedAt only transform', done => {
it_id('f92c82ac-1993-4758-b718-45689dfc4154')(it_exclude_dbs(['postgres']))('project updatedAt only transform', done => {
const pipeline = [
{
$project: { _id: 0, updatedAt: 1 },
@@ -401,9 +401,9 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it_exclude_dbs(['postgres'])(
'can group by any date field (it does not work if you have dirty data)', // rows in your collection with non date data in the field that is supposed to be a date
it_id('99566b1d-778d-4444-9deb-c398108e659d')(it_exclude_dbs(['postgres']))('can group by any date field (it does not work if you have dirty data)',
done => {
// rows in your collection with non date data in the field that is supposed to be a date
const obj1 = new TestObject({ dateField2019: new Date(1990, 11, 1) });
const obj2 = new TestObject({ dateField2019: new Date(1990, 5, 1) });
const obj3 = new TestObject({ dateField2019: new Date(1990, 11, 1) });
@@ -472,7 +472,7 @@ describe('Parse.Query Aggregate testing', () => {
}
);
it('group by pointer', done => {
it_id('bf3c2704-b721-4b1b-92fa-e1b129ae4aff')(it)('group by pointer', done => {
const pointer1 = new TestObject();
const pointer2 = new TestObject();
const obj1 = new TestObject({ pointer: pointer1 });
@@ -493,7 +493,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('group sum query', done => {
it_id('9ee9e8c0-a590-4af9-97a9-4b8e5080ffae')(it)('group sum query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$group: { _id: null, total: { $sum: '$score' } },
@@ -509,7 +509,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('group count query', done => {
it_id('39133cd6-5bdf-4917-b672-a9d7a9157b6f')(it)('group count query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$group: { _id: null, total: { $sum: 1 } },
@@ -525,7 +525,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('group min query', done => {
it_id('48685ff3-066f-4353-82e7-87f39d812ff7')(it)('group min query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$group: { _id: null, minScore: { $min: '$score' } },
@@ -541,7 +541,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('group max query', done => {
it_id('581efea6-6525-4e10-96d9-76d32c73e7a9')(it)('group max query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$group: { _id: null, maxScore: { $max: '$score' } },
@@ -557,7 +557,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('group avg query', done => {
it_id('5f880de2-b97f-43d1-89b7-ad903a4be4e2')(it)('group avg query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$group: { _id: null, avgScore: { $avg: '$score' } },
@@ -573,7 +573,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('limit query', done => {
it_id('58e7a1a0-fae1-4993-b336-7bcbd5b7c786')(it)('limit query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$limit: 2,
@@ -587,7 +587,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('sort ascending query', done => {
it_id('c892a3d2-8ae8-4b88-bf2b-3c958e1cacd8')(it)('sort ascending query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$sort: { name: 1 },
@@ -605,7 +605,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('sort decending query', done => {
it_id('79d4bc2e-8b69-42ec-8526-20d17e968ab3')(it)('sort decending query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$sort: { name: -1 },
@@ -623,7 +623,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('skip query', done => {
it_id('b3d97d48-bd6b-444d-be64-cc1fd4738266')(it)('skip query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$skip: 2,
@@ -637,7 +637,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('match comparison date query', done => {
it_id('4a7daee3-5ba1-4c8b-b406-1846a73a64c8')(it)('match comparison date query', done => {
const today = new Date();
const yesterday = new Date();
const tomorrow = new Date();
@@ -658,7 +658,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('should aggregate with Date object (directAccess)', async () => {
it_id('d98c8c20-6dac-4d74-8228-85a1ae46a7d0')(it)('should aggregate with Date object (directAccess)', async () => {
const rest = require('../lib/rest');
const auth = require('../lib/Auth');
const TestObject = Parse.Object.extend('TestObject');
@@ -675,7 +675,7 @@ describe('Parse.Query Aggregate testing', () => {
expect(resp.results.length).toBe(1);
});
it('match comparison query', done => {
it_id('3d73d23a-fce1-4ac0-972a-50f6a550f348')(it)('match comparison query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$match: { score: { $gt: 15 } },
@@ -690,7 +690,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('match multiple comparison query', done => {
it_id('11772059-6c93-41ac-8dfe-e55b6c97e16f')(it)('match multiple comparison query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$match: { score: { $gt: 5, $lt: 15 } },
@@ -707,7 +707,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('match complex comparison query', done => {
it_id('ca2efb04-8f73-40ca-a5fc-79d0032bc398')(it)('match complex comparison query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$match: { score: { $gt: 5, $lt: 15 }, views: { $gt: 850, $lt: 1000 } },
@@ -723,7 +723,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('match comparison and equality query', done => {
it_id('5ef9dcbe-fe54-4db2-b8fb-58c87c6ff072')(it)('match comparison and equality query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$match: { score: { $gt: 5, $lt: 15 }, views: 900 },
@@ -739,7 +739,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('match $or query', done => {
it_id('c910a6af-58df-46aa-bbf8-da014a04cdcd')(it)('match $or query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$match: {
@@ -762,7 +762,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('match objectId query', done => {
it_id('0f768dc2-0675-4e45-a763-5ca9c895fa5f')(it)('match objectId query', done => {
const obj1 = new TestObject();
const obj2 = new TestObject();
Parse.Object.saveAll([obj1, obj2])
@@ -778,7 +778,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('match field query', done => {
it_id('27349e04-0d9d-453f-ad85-1a811631582d')(it)('match field query', done => {
const obj1 = new TestObject({ name: 'TestObject1' });
const obj2 = new TestObject({ name: 'TestObject2' });
Parse.Object.saveAll([obj1, obj2])
@@ -794,7 +794,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('match pointer query', done => {
it_id('9222e025-d450-4699-8d5b-c5cf9a64fb24')(it)('match pointer query', done => {
const pointer1 = new PointerObject();
const pointer2 = new PointerObject();
const obj1 = new TestObject({ pointer: pointer1 });
@@ -817,7 +817,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it_exclude_dbs(['postgres'])('match exists query', done => {
it_id('3a1e2cdc-52c7-4060-bc90-b06d557d85ce')(it_exclude_dbs(['postgres']))('match exists query', done => {
const pipeline = [{ $match: { score: { $exists: true } } }];
const query = new Parse.Query(TestObject);
query.aggregate(pipeline).then(results => {
@@ -826,7 +826,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('match date query - createdAt', done => {
it_id('0adea3f4-73f7-4b48-a7dd-c764ceb947ec')(it)('match date query - createdAt', done => {
const obj1 = new TestObject();
const obj2 = new TestObject();
@@ -845,7 +845,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('match date query - updatedAt', done => {
it_id('cdc0eecb-f547-4881-84cc-c06fb46a636a')(it)('match date query - updatedAt', done => {
const obj1 = new TestObject();
const obj2 = new TestObject();
@@ -864,7 +864,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('match date query - empty', done => {
it_id('621fe00a-1127-4341-a8e1-fc579b7ed8bd')(it)('match date query - empty', done => {
const obj1 = new TestObject();
const obj2 = new TestObject();
@@ -882,7 +882,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it_exclude_dbs(['postgres'])('match pointer with operator query', done => {
it_id('802ffc99-861b-4b72-90a6-0c666a2e3fd8')(it_exclude_dbs(['postgres']))('match pointer with operator query', done => {
const pointer = new PointerObject();
const obj1 = new TestObject({ pointer });
@@ -905,7 +905,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it_exclude_dbs(['postgres'])('match null values', async () => {
it_id('28090280-7c3e-47f8-8bf6-bebf8566a36c')(it_exclude_dbs(['postgres']))('match null values', async () => {
const obj1 = new Parse.Object('MyCollection');
obj1.set('language', 'en');
obj1.set('otherField', 1);
@@ -955,7 +955,7 @@ describe('Parse.Query Aggregate testing', () => {
).toEqual([1, 2, 3, 4]);
});
it('project query', done => {
it_id('df63d1f5-7c37-4ed9-8bc5-20d82f29f509')(it)('project query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$project: { name: 1 },
@@ -975,7 +975,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('multiple project query', done => {
it_id('69224bbb-8ea0-4ab4-af23-398b6432f668')(it)('multiple project query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$project: { name: 1, score: 1, sender: 1 },
@@ -995,7 +995,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('project pointer query', done => {
it_id('97ce4c7c-8d9f-4ffd-9352-394bc9867bab')(it)('project pointer query', done => {
const pointer = new PointerObject();
const obj = new TestObject({ pointer, name: 'hello' });
@@ -1018,7 +1018,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('project with group query', done => {
it_id('3940aac3-ac49-4279-8083-af9096de636f')(it)('project with group query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$project: { score: 1 },
@@ -1074,7 +1074,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('distinct query', done => {
it_id('985e7a66-d4f5-4f72-bd54-ee44670e0ab0')(it)('distinct query', done => {
const options = Object.assign({}, masterKeyOptions, {
body: { distinct: 'score' },
});
@@ -1088,7 +1088,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('distinct query with where', done => {
it_id('ef157f86-c456-4a4c-8dac-81910bd0f716')(it)('distinct query with where', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
distinct: 'score',
@@ -1105,7 +1105,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('distinct query with where string', done => {
it_id('7f5275cc-2c34-42bc-8a09-43378419c326')(it)('distinct query with where string', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
distinct: 'score',
@@ -1120,7 +1120,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('distinct nested', done => {
it_id('383b7248-e457-4373-8d5c-f9359384347e')(it)('distinct nested', done => {
const options = Object.assign({}, masterKeyOptions, {
body: { distinct: 'sender.group' },
});
@@ -1134,7 +1134,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('distinct pointer', done => {
it_id('20f14464-adb7-428c-ac7a-5a91a1952a64')(it)('distinct pointer', done => {
const pointer1 = new PointerObject();
const pointer2 = new PointerObject();
const obj1 = new TestObject({ pointer: pointer1 });
@@ -1153,7 +1153,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it('distinct class does not exist return empty', done => {
it_id('91e6cb94-2837-44b7-b057-0c4965057caa')(it)('distinct class does not exist return empty', done => {
const options = Object.assign({}, masterKeyOptions, {
body: { distinct: 'unknown' },
});
@@ -1165,7 +1165,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('distinct field does not exist return empty', done => {
it_id('bd15daaf-8dc7-458c-81e2-170026f4a8a7')(it)('distinct field does not exist return empty', done => {
const options = Object.assign({}, masterKeyOptions, {
body: { distinct: 'unknown' },
});
@@ -1182,7 +1182,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('distinct array', done => {
it_id('21988fce-8326-425f-82f0-cd444ca3671b')(it)('distinct array', done => {
const options = Object.assign({}, masterKeyOptions, {
body: { distinct: 'size' },
});
@@ -1197,13 +1197,13 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('distinct objectId', async () => {
it_id('633fde06-c4af-474b-9841-3ccabc24dd4f')(it)('distinct objectId', async () => {
const query = new Parse.Query(TestObject);
const results = await query.distinct('objectId');
expect(results.length).toBe(4);
});
it('distinct createdAt', async () => {
it_id('8f9706f4-2703-42f1-b524-f2f7e72bbfe7')(it)('distinct createdAt', async () => {
const object1 = new TestObject({ createdAt_test: true });
await object1.save();
const object2 = new TestObject({ createdAt_test: true });
@@ -1214,7 +1214,7 @@ describe('Parse.Query Aggregate testing', () => {
expect(results.length).toBe(2);
});
it('distinct updatedAt', async () => {
it_id('3562e600-8ce5-4d6d-96df-8ff969e81421')(it)('distinct updatedAt', async () => {
const object1 = new TestObject({ updatedAt_test: true });
await object1.save();
const object2 = new TestObject();
@@ -1227,7 +1227,7 @@ describe('Parse.Query Aggregate testing', () => {
expect(results.length).toBe(2);
});
it('distinct null field', done => {
it_id('5012cfb1-b0aa-429d-a94f-d32d8aa0b7f9')(it)('distinct null field', done => {
const options = Object.assign({}, masterKeyOptions, {
body: { distinct: 'distinctField' },
});
@@ -1256,7 +1256,7 @@ describe('Parse.Query Aggregate testing', () => {
.catch(done.fail);
});
it('does not return sensitive hidden properties', done => {
it_id('d9c19419-e99d-4d9f-b7f3-418e49ee47dd')(it)('does not return sensitive hidden properties', done => {
const options = Object.assign({}, masterKeyOptions, {
body: {
$match: {
@@ -1305,7 +1305,7 @@ describe('Parse.Query Aggregate testing', () => {
});
});
it_exclude_dbs(['postgres'])('aggregate allow multiple of same stage', async done => {
it_id('0a23e791-e9b5-457a-9bf9-9c5ecf406f42')(it_exclude_dbs(['postgres']))('aggregate allow multiple of same stage', async done => {
await reconfigureServer({ silent: false });
const pointer1 = new TestObject({ value: 1 });
const pointer2 = new TestObject({ value: 2 });
+9 -9
View File
@@ -30,7 +30,7 @@ const fullTextHelper = async () => {
};
describe('Parse.Query Full Text Search testing', () => {
it('fullTextSearch: $search', async () => {
it_id('77ba6779-6584-4e09-8e7e-31f89e741d6a')(it)('fullTextSearch: $search', async () => {
await fullTextHelper();
const query = new Parse.Query('TestObject');
query.fullText('subject', 'coffee');
@@ -38,7 +38,7 @@ describe('Parse.Query Full Text Search testing', () => {
expect(results.length).toBe(3);
});
it('fullTextSearch: $search, sort', async () => {
it_id('d1992ea6-6d92-4bfa-a487-2a49fbcf8f0d')(it)('fullTextSearch: $search, sort', async () => {
await fullTextHelper();
const query = new Parse.Query('TestObject');
query.fullText('subject', 'coffee');
@@ -51,7 +51,7 @@ describe('Parse.Query Full Text Search testing', () => {
expect(results[2].get('score'));
});
it('fulltext descending by $score', async () => {
it_id('07172595-50de-4be2-984a-d3136bebb22e')(it)('fulltext descending by $score', async () => {
await fullTextHelper();
const query = new Parse.Query('TestObject');
query.fullText('subject', 'coffee');
@@ -68,7 +68,7 @@ describe('Parse.Query Full Text Search testing', () => {
expect(second.get('score') >= third.get('score')).toBeTrue();
});
it('fullTextSearch: $language', async () => {
it_id('8e821973-3fae-4e7c-8152-766228a18cdd')(it)('fullTextSearch: $language', async () => {
await fullTextHelper();
const query = new Parse.Query('TestObject');
query.fullText('subject', 'leche', { language: 'spanish' });
@@ -76,7 +76,7 @@ describe('Parse.Query Full Text Search testing', () => {
expect(resp.length).toBe(2);
});
it('fullTextSearch: $diacriticSensitive', async () => {
it_id('7d3da216-9582-40ee-a2fe-8316feaf5c0c')(it)('fullTextSearch: $diacriticSensitive', async () => {
await fullTextHelper();
const query = new Parse.Query('TestObject');
query.fullText('subject', 'CAFÉ', { diacriticSensitive: true });
@@ -84,7 +84,7 @@ describe('Parse.Query Full Text Search testing', () => {
expect(resp.length).toBe(1);
});
it('fullTextSearch: $search, invalid input', async () => {
it_id('dade10c8-2b9c-4f43-bb3f-a13bbd82ac22')(it)('fullTextSearch: $search, invalid input', async () => {
await fullTextHelper();
const invalidQuery = async () => {
const where = {
@@ -114,7 +114,7 @@ describe('Parse.Query Full Text Search testing', () => {
);
});
it('fullTextSearch: $language, invalid input', async () => {
it_id('ff7c6b1c-4712-4847-bb76-f4e1f641f7b5')(it)('fullTextSearch: $language, invalid input', async () => {
await fullTextHelper();
const query = new Parse.Query('TestObject');
query.fullText('subject', 'leche', { language: true });
@@ -123,7 +123,7 @@ describe('Parse.Query Full Text Search testing', () => {
);
});
it('fullTextSearch: $caseSensitive, invalid input', async () => {
it_id('de262dbc-ec75-4ec6-9217-fbb90146c272')(it)('fullTextSearch: $caseSensitive, invalid input', async () => {
await fullTextHelper();
const query = new Parse.Query('TestObject');
query.fullText('subject', 'leche', { caseSensitive: 'string' });
@@ -132,7 +132,7 @@ describe('Parse.Query Full Text Search testing', () => {
);
});
it('fullTextSearch: $diacriticSensitive, invalid input', async () => {
it_id('b7b7b3a9-8d6c-4f98-a0ff-0113593d06d4')(it)('fullTextSearch: $diacriticSensitive, invalid input', async () => {
await fullTextHelper();
const query = new Parse.Query('TestObject');
query.fullText('subject', 'leche', { diacriticSensitive: 'string' });
+11 -11
View File
@@ -590,7 +590,7 @@ describe('Parse.Query testing', () => {
});
});
it('containsAll object array queries', function (done) {
it_id('25bb35a6-e953-4d6d-a31c-66324d5ae076')(it)('containsAll object array queries', function (done) {
const MessageSet = Parse.Object.extend({ className: 'MessageSet' });
const messageList = [];
@@ -705,7 +705,7 @@ describe('Parse.Query testing', () => {
});
});
it('containsAllStartingWith values must be all of type starting with regex', done => {
it_id('3ea6ae04-bcc2-453d-8817-4c64d059c2f6')(it)('containsAllStartingWith values must be all of type starting with regex', done => {
const object = new Parse.Object('Object');
object.set('strings', ['the', 'brown', 'lazy', 'fox', 'jumps']);
@@ -884,7 +884,7 @@ describe('Parse.Query testing', () => {
);
});
it('containedBy pointer array', done => {
it_id('01a15195-dde2-4368-b996-d746a4ede3a1')(it)('containedBy pointer array', done => {
const objects = Array.from(Array(10).keys()).map(idx => {
const obj = new Parse.Object('Object');
obj.set('key', idx);
@@ -1668,7 +1668,7 @@ describe('Parse.Query testing', () => {
.catch(done.fail);
});
it('can order on an object number field', function (done) {
it_id('65c8238d-cf02-49d0-a919-8a17f5a58280')(it)('can order on an object number field', function (done) {
const testSet = [
{ sortField: { value: 10 } },
{ sortField: { value: 1 } },
@@ -1689,7 +1689,7 @@ describe('Parse.Query testing', () => {
.catch(done.fail);
});
it('can order on an object number field (level 2)', function (done) {
it_id('d8f0bead-b931-4d66-8b0c-28c5705e463c')(it)('can order on an object number field (level 2)', function (done) {
const testSet = [
{ sortField: { value: { field: 10 } } },
{ sortField: { value: { field: 1 } } },
@@ -2111,7 +2111,7 @@ describe('Parse.Query testing', () => {
.then(done);
});
it('Use a regex that requires all modifiers', function (done) {
it_id('823852f6-1de5-45ba-a2b9-ed952fcc6012')(it)('Use a regex that requires all modifiers', function (done) {
const thing = new TestObject();
thing.set('myString', 'PArSe\nCom');
Parse.Object.saveAll([thing]).then(function () {
@@ -3786,7 +3786,7 @@ describe('Parse.Query testing', () => {
});
});
it('notEqual with array of pointers', done => {
it_id('56b09b92-c756-4bae-8c32-1c32b5b4c397')(it)('notEqual with array of pointers', done => {
const children = [];
const parents = [];
const promises = [];
@@ -4003,7 +4003,7 @@ describe('Parse.Query testing', () => {
);
});
it('should properly interpret a query v2', done => {
it_id('7079f0ef-47b3-4a1e-aac0-32654dadaa27')(it)('should properly interpret a query v2', done => {
const user = new Parse.User();
user.set('username', 'foo');
user.set('password', 'bar');
@@ -4082,7 +4082,7 @@ describe('Parse.Query testing', () => {
});
});
it('should find objects with array of pointers', done => {
it_id('d95818c0-9e3c-41e6-be20-e7bafb59eefb')(it)('should find objects with array of pointers', done => {
const objects = [];
while (objects.length != 5) {
const object = new Parse.Object('ContainedObject');
@@ -5013,7 +5013,7 @@ describe('Parse.Query testing', () => {
equal(results[0].get('name'), group2.get('name'));
});
it('withJSON supports geoWithin.centerSphere', done => {
it_id('8886b994-fbb8-487d-a863-43bbd2b24b73')(it)('withJSON supports geoWithin.centerSphere', done => {
const inbound = new Parse.GeoPoint(1.5, 1.5);
const onbound = new Parse.GeoPoint(10, 10);
const outbound = new Parse.GeoPoint(20, 20);
@@ -5114,7 +5114,7 @@ describe('Parse.Query testing', () => {
.catch(() => done());
});
it('can add new config to existing config', async () => {
it_id('02d4e7e6-859a-4ab6-878d-135ccc77040e')(it)('can add new config to existing config', async () => {
await request({
method: 'PUT',
url: 'http://localhost:8378/1/config',
+1 -1
View File
@@ -126,7 +126,7 @@ describe('Parse Role testing', () => {
);
});
it('should not recursively load the same role multiple times', done => {
it_id('b03abe32-e8e4-4666-9b81-9c804aa53400')(it)('should not recursively load the same role multiple times', done => {
const rootRole = 'RootRole';
const roleNames = ['FooRole', 'BarRole', 'BazRole'];
const allRoles = [rootRole].concat(roleNames);
+17 -26
View File
@@ -2,16 +2,17 @@ const ParseServerRESTController = require('../lib/ParseServerRESTController')
.ParseServerRESTController;
const ParseServer = require('../lib/ParseServer').default;
const Parse = require('parse/node').Parse;
const TestUtils = require('../lib/TestUtils');
let RESTController;
describe('ParseServerRESTController', () => {
let createSpy;
beforeEach(() => {
RESTController = ParseServerRESTController(
Parse.applicationId,
ParseServer.promiseRouter({ appId: Parse.applicationId })
);
createSpy = spyOn(databaseAdapter, 'createObject').and.callThrough();
});
it('should handle a get request', async () => {
@@ -133,24 +134,11 @@ describe('ParseServerRESTController', () => {
process.env.PARSE_SERVER_TEST_DB === 'postgres'
) {
describe('transactions', () => {
beforeEach(async () => {
await TestUtils.destroyAllDataPermanently(true);
if (process.env.MONGODB_TOPOLOGY === 'replicaset') {
await reconfigureServer({
databaseAdapter: undefined,
databaseURI:
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset',
});
} else {
await reconfigureServer();
}
});
it('should handle a batch request with transaction = true', async () => {
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
await myObject.save();
await myObject.destroy();
spyOn(databaseAdapter, 'createObject').and.callThrough();
createSpy.calls.reset();
const response = await RESTController.request('POST', 'batch', {
requests: [
{
@@ -173,10 +161,10 @@ describe('ParseServerRESTController', () => {
expect(response[1].success.createdAt).toBeDefined();
const query = new Parse.Query('MyObject');
const results = await query.find();
expect(databaseAdapter.createObject.calls.count() % 2).toBe(0);
for (let i = 0; i + 1 < databaseAdapter.createObject.calls.length; i = i + 2) {
expect(databaseAdapter.createObject.calls.argsFor(i)[3]).toBe(
databaseAdapter.createObject.calls.argsFor(i + 1)[3]
expect(createSpy.calls.count()).toBe(2);
for (let i = 0; i + 1 < createSpy.calls.length; i = i + 2) {
expect(createSpy.calls.argsFor(i)[3]).toBe(
createSpy.calls.argsFor(i + 1)[3]
);
}
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
@@ -184,9 +172,11 @@ describe('ParseServerRESTController', () => {
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
await myObject.save();
await myObject.save({ key: 'stringField' });
await myObject.destroy();
createSpy.calls.reset();
try {
// Saving a number to a string field should fail
await RESTController.request('POST', 'batch', {
requests: [
{
@@ -294,20 +284,21 @@ 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();
await myObject.save({ key: 'stringField' });
await myObject.destroy();
const myObject2 = new Parse.Object('MyObject2'); // This is important because transaction only works on pre-existing collections
await myObject2.save();
await myObject2.save({ key: 'stringField' });
await myObject2.destroy();
spyOn(databaseAdapter, 'createObject').and.callThrough();
createSpy.calls.reset();
let myObjectCalls = 0;
Parse.Cloud.beforeSave('MyObject', async () => {
myObjectCalls++;
if (myObjectCalls === 2) {
try {
// Saving a number to a string field should fail
await RESTController.request('POST', 'batch', {
requests: [
{
@@ -459,14 +450,14 @@ describe('ParseServerRESTController', () => {
const results3 = await query3.find();
expect(results3.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
expect(databaseAdapter.createObject.calls.count() >= 13).toEqual(true);
expect(createSpy.calls.count() >= 13).toEqual(true);
let transactionalSession;
let transactionalSession2;
let myObjectDBCalls = 0;
let myObject2DBCalls = 0;
let myObject3DBCalls = 0;
for (let i = 0; i < databaseAdapter.createObject.calls.count(); i++) {
const args = databaseAdapter.createObject.calls.argsFor(i);
for (let i = 0; i < createSpy.calls.count(); i++) {
const args = createSpy.calls.argsFor(i);
switch (args[0]) {
case 'MyObject':
myObjectDBCalls++;
+6 -6
View File
@@ -2257,7 +2257,7 @@ describe('Parse.User testing', () => {
});
describe('case insensitive signup not allowed', () => {
it('signup should fail with duplicate case insensitive username with basic setter', async () => {
it_id('464eddc2-7a46-413d-888e-b43b040f1511')(it)('signup should fail with duplicate case insensitive username with basic setter', async () => {
const user = new Parse.User();
user.set('username', 'test1');
user.set('password', 'test');
@@ -2271,7 +2271,7 @@ describe('Parse.User testing', () => {
);
});
it('signup should fail with duplicate case insensitive username with field specific setter', async () => {
it_id('1cef005b-d5f0-4699-af0c-bb0af27d2437')(it)('signup should fail with duplicate case insensitive username with field specific setter', async () => {
const user = new Parse.User();
user.setUsername('test1');
user.setPassword('test');
@@ -2285,7 +2285,7 @@ describe('Parse.User testing', () => {
);
});
it('signup should fail with duplicate case insensitive email', async () => {
it_id('12735529-98d1-42c0-b437-3b47fe78ddde')(it)('signup should fail with duplicate case insensitive email', async () => {
const user = new Parse.User();
user.setUsername('test1');
user.setPassword('test');
@@ -2301,7 +2301,7 @@ describe('Parse.User testing', () => {
);
});
it('edit should fail with duplicate case insensitive email', async () => {
it_id('66e51d52-2420-4b62-8a0d-c7e1b384763e')(it)('edit should fail with duplicate case insensitive email', async () => {
const user = new Parse.User();
user.setUsername('test1');
user.setPassword('test');
@@ -2952,7 +2952,7 @@ describe('Parse.User testing', () => {
});
});
it('should send email when upgrading from anon', async done => {
it_id('1be98368-19ac-4c77-8531-762a114f43fb')(it)('should send email when upgrading from anon', async done => {
await reconfigureServer();
let emailCalled = false;
let emailOptions;
@@ -3018,7 +3018,7 @@ describe('Parse.User testing', () => {
});
});
it('should not send email when email is not a string', async done => {
it_id('bf668670-39fa-44d3-a9a9-cad52f36d272')(it)('should not send email when email is not a string', async done => {
let emailCalled = false;
let emailOptions;
const emailAdapter = {
+6 -6
View File
@@ -3,7 +3,7 @@
const request = require('../lib/request');
describe('Password Policy: ', () => {
it('should show the invalid link page if the user clicks on the password reset link after the token expires', done => {
it_id('b400a867-9f05-496f-af79-933aa588dde5')(it)('should show the invalid link page if the user clicks on the password reset link after the token expires', done => {
const user = new Parse.User();
let sendEmailOptions;
const emailAdapter = {
@@ -150,7 +150,7 @@ describe('Password Policy: ', () => {
done();
});
it('should keep reset token with resetTokenReuseIfValid', async done => {
it_id('7d98e1f2-ae89-4038-9ea7-5254854ea42e')(it)('should keep reset token with resetTokenReuseIfValid', async done => {
const sendEmailOptions = [];
const emailAdapter = {
sendVerificationEmail: () => Promise.resolve(),
@@ -1164,7 +1164,7 @@ describe('Password Policy: ', () => {
});
});
it('should succeed if logged in before password expires', done => {
it_id('d7d0a93e-efe6-48c0-b622-0f7fb570ccc1')(it)('should succeed if logged in before password expires', done => {
const user = new Parse.User();
reconfigureServer({
appName: 'passwordPolicy',
@@ -1197,7 +1197,7 @@ describe('Password Policy: ', () => {
});
});
it('should fail if logged in after password expires', done => {
it_id('22428408-8763-445d-9833-2b2d92008f62')(it)('should fail if logged in after password expires', done => {
const user = new Parse.User();
reconfigureServer({
appName: 'passwordPolicy',
@@ -1236,7 +1236,7 @@ describe('Password Policy: ', () => {
});
});
it('should apply password expiry policy to existing user upon first login after policy is enabled', done => {
it_id('cc97a109-e35f-4f94-b942-3a6134921cdd')(it)('should apply password expiry policy to existing user upon first login after policy is enabled', done => {
const user = new Parse.User();
reconfigureServer({
appName: 'passwordPolicy',
@@ -1304,7 +1304,7 @@ describe('Password Policy: ', () => {
});
});
it('should reset password timestamp when password is reset', done => {
it_id('d1e6ab9d-c091-4fea-b952-08b7484bfc89')(it)('should reset password timestamp when password is reset', done => {
const user = new Parse.User();
const emailAdapter = {
sendVerificationEmail: () => Promise.resolve(),
+13 -13
View File
@@ -226,7 +226,7 @@ describe('Pointer Permissions', () => {
});
});
it('should query on pointer permission enabled column', done => {
it_id('f38c35e7-d804-4d32-986d-2579e25d2461')(it)('should query on pointer permission enabled column', done => {
const config = Config.get(Parse.applicationId);
const user = new Parse.User();
const user2 = new Parse.User();
@@ -1070,7 +1070,7 @@ describe('Pointer Permissions', () => {
}
});
it('should work with write', async done => {
it_id('1bbb9ed6-5558-4ce5-a238-b1a2015d273f')(it)('should work with write', async done => {
const config = Config.get(Parse.applicationId);
const user = new Parse.User();
const user2 = new Parse.User();
@@ -1203,7 +1203,7 @@ describe('Pointer Permissions', () => {
done();
});
it('should query on pointer permission enabled column', async done => {
it_id('8a7d188c-b75c-4eac-90b6-9b0b11f873ae')(it)('should query on pointer permission enabled column', async done => {
const config = Config.get(Parse.applicationId);
const user = new Parse.User();
const user2 = new Parse.User();
@@ -2058,7 +2058,7 @@ describe('Pointer Permissions', () => {
done();
});
it('should fail for user not listed', async done => {
it_id('9ba681d5-59f5-4996-b36d-6647d23e6a44')(it)('should fail for user not listed', async done => {
await updateCLP({
get: {
pointerFields: ['owner'],
@@ -2218,7 +2218,7 @@ describe('Pointer Permissions', () => {
done();
});
it('should fail for user not listed', async done => {
it_id('bcdb158d-c0b6-45e3-84ab-a3636f7cb470')(it)('should fail for user not listed', async done => {
await updateCLP({
update: {
pointerFields: ['owner'],
@@ -2270,7 +2270,7 @@ describe('Pointer Permissions', () => {
done();
});
it('should fail for user not listed', async done => {
it_id('70aa3853-6e26-4c38-a927-2ddb24ced7d4')(it)('should fail for user not listed', async done => {
await updateCLP({
delete: {
pointerFields: ['owner'],
@@ -2517,7 +2517,7 @@ describe('Pointer Permissions', () => {
done();
});
it('should fail for user not listed', async done => {
it_id('84a42339-c7b5-4735-a431-57b46535b073')(it)('should fail for user not listed', async done => {
await updateCLP({
get: {
pointerFields: ['moderators'],
@@ -2685,7 +2685,7 @@ describe('Pointer Permissions', () => {
done();
});
it('should be allowed (multiple users in array)', async done => {
it_id('2b19234a-a471-48b4-bd1a-27bd286d066f')(it)('should be allowed (multiple users in array)', async done => {
await updateCLP({
update: {
pointerFields: ['moderators'],
@@ -2698,7 +2698,7 @@ describe('Pointer Permissions', () => {
done();
});
it('should fail for user not listed', async done => {
it_id('1abb9f4a-fb24-48c7-8025-3001d6cf8737')(it)('should fail for user not listed', async done => {
await updateCLP({
update: {
pointerFields: ['moderators'],
@@ -2764,7 +2764,7 @@ describe('Pointer Permissions', () => {
done();
});
it('should fail for user not listed', async done => {
it_id('3175a0e3-e51e-4b84-a2e6-50bbcc582123')(it)('should fail for user not listed', async done => {
await updateCLP({
delete: {
pointerFields: ['owners'],
@@ -2874,7 +2874,7 @@ describe('Pointer Permissions', () => {
done();
});
it('should be restricted when updating object without addField permission', async done => {
it_id('51e896e9-73b3-404f-b5ff-bdb99005a9f7')(it)('should be restricted when updating object without addField permission', async done => {
await updateCLP({
update: {
'*': true,
@@ -2946,7 +2946,7 @@ describe('Pointer Permissions', () => {
await initialize();
});
it('should not limit the scope of grouped read permissions', async done => {
it_id('b43db366-8cce-4a11-9cf2-eeee9603d40b')(it)('should not limit the scope of grouped read permissions', async done => {
await updateCLP({
get: {
pointerFields: ['owner'],
@@ -2967,7 +2967,7 @@ describe('Pointer Permissions', () => {
done();
});
it('should not limit the scope of grouped write permissions', async done => {
it_id('bbb1686d-0e2a-4365-8b64-b5faa3e7b9cf')(it)('should not limit the scope of grouped write permissions', async done => {
await updateCLP({
update: {
pointerFields: ['owner'],
+8 -8
View File
@@ -168,7 +168,7 @@ describe('PushController', () => {
done();
});
it('properly increment badges', async () => {
it_id('01e3e1b8-fad2-4249-b664-5a3efaab8cb1')(it)('properly increment badges', async () => {
const pushAdapter = {
send: function (body, installations) {
const badge = body.data.badge;
@@ -233,7 +233,7 @@ describe('PushController', () => {
}
});
it('properly increment badges by more than 1', async () => {
it_id('14afcedf-e65d-41cd-981e-07f32df84c14')(it)('properly increment badges by more than 1', async () => {
const pushAdapter = {
send: function (body, installations) {
const badge = body.data.badge;
@@ -295,7 +295,7 @@ describe('PushController', () => {
}
});
it('properly set badges to 1', async () => {
it_id('758dd579-aa91-4010-9033-8d48d3463644')(it)('properly set badges to 1', async () => {
const pushAdapter = {
send: function (body, installations) {
const badge = body.data.badge;
@@ -350,7 +350,7 @@ describe('PushController', () => {
}
});
it('properly set badges to 1 with complex query #2903 #3022', async () => {
it_id('75c39ae3-06ac-4354-b321-931e81c5a927')(it)('properly set badges to 1 with complex query #2903 #3022', async () => {
const payload = {
data: {
alert: 'Hello World!',
@@ -406,7 +406,7 @@ describe('PushController', () => {
expect(results.length).toBe(5);
});
it('properly creates _PushStatus', async () => {
it_id('667f31c0-b458-4f61-ab57-668c04e3cc0b')(it)('properly creates _PushStatus', async () => {
const pushStatusAfterSave = {
handler: function () {},
};
@@ -521,7 +521,7 @@ describe('PushController', () => {
expect(succeedCount).toBe(1);
});
it('properly creates _PushStatus without serverURL', async () => {
it_id('30e0591a-56de-4720-8c60-7d72291b532a')(it)('properly creates _PushStatus without serverURL', async () => {
const pushStatusAfterSave = {
handler: function () {},
};
@@ -615,7 +615,7 @@ describe('PushController', () => {
}
});
it('should support full RESTQuery for increment', async () => {
it_id('53551fc3-b975-4774-92e6-7e5f3c05e105')(it)('should support full RESTQuery for increment', async () => {
const payload = {
data: {
alert: 'Hello World!',
@@ -1030,7 +1030,7 @@ describe('PushController', () => {
// No installation is in es so only 1 call for fr, and another for default
});
it('should update audiences', async () => {
it_id('ef2e5569-50c3-40c2-ab49-175cdbd5f024')(it)('should update audiences', async () => {
const pushAdapter = {
send: function (body, installations) {
return successfulTransmissions(body, installations);
+1 -1
View File
@@ -271,7 +271,7 @@ describe('PushWorker', () => {
toAwait.then(done).catch(done);
});
it('tracks push status per UTC offsets', done => {
it_id('764d28ab-241b-4b96-8ce9-e03541850e3f')(it)('tracks push status per UTC offsets', done => {
const config = Config.get('test');
const handler = pushStatusHandler(config);
const spy = spyOn(rest, 'update').and.callThrough();
+38 -33
View File
@@ -16,8 +16,13 @@ const emailAdapter = {
const appName = 'test';
const publicServerURL = 'http://localhost:8378/1';
describe('Regex Vulnerabilities', function () {
beforeEach(async function () {
describe('Regex Vulnerabilities', () => {
let objectId;
let sessionToken;
let partialSessionToken;
let user;
beforeEach(async () => {
await reconfigureServer({
maintenanceKey: 'test2',
verifyUserEmails: true,
@@ -38,13 +43,13 @@ describe('Regex Vulnerabilities', function () {
email: 'someemail@somedomain.com',
}),
});
this.objectId = signUpResponse.data.objectId;
this.sessionToken = signUpResponse.data.sessionToken;
this.partialSessionToken = this.sessionToken.slice(0, 3);
objectId = signUpResponse.data.objectId;
sessionToken = signUpResponse.data.sessionToken;
partialSessionToken = sessionToken.slice(0, 3);
});
describe('on session token', function () {
it('should not work with regex', async function () {
describe('on session token', () => {
it('should not work with regex', async () => {
try {
await request({
url: `${serverURL}/users/me`,
@@ -53,7 +58,7 @@ describe('Regex Vulnerabilities', function () {
body: JSON.stringify({
...keys,
_SessionToken: {
$regex: this.partialSessionToken,
$regex: partialSessionToken,
},
_method: 'GET',
}),
@@ -65,43 +70,43 @@ describe('Regex Vulnerabilities', function () {
}
});
it('should work with plain token', async function () {
it('should work with plain token', async () => {
const meResponse = await request({
url: `${serverURL}/users/me`,
method: 'POST',
headers,
body: JSON.stringify({
...keys,
_SessionToken: this.sessionToken,
_SessionToken: sessionToken,
_method: 'GET',
}),
});
expect(meResponse.data.objectId).toEqual(this.objectId);
expect(meResponse.data.sessionToken).toEqual(this.sessionToken);
expect(meResponse.data.objectId).toEqual(objectId);
expect(meResponse.data.sessionToken).toEqual(sessionToken);
});
});
describe('on verify e-mail', function () {
describe('on verify e-mail', () => {
beforeEach(async function () {
const userQuery = new Parse.Query(Parse.User);
this.user = await userQuery.get(this.objectId, { useMasterKey: true });
user = await userQuery.get(objectId, { useMasterKey: true });
});
it('should not work with regex', async function () {
expect(this.user.get('emailVerified')).toEqual(false);
it('should not work with regex', async () => {
expect(user.get('emailVerified')).toEqual(false);
await request({
url: `${serverURL}/apps/test/verify_email?username=someemail@somedomain.com&token[$regex]=`,
method: 'GET',
});
await this.user.fetch({ useMasterKey: true });
expect(this.user.get('emailVerified')).toEqual(false);
await user.fetch({ useMasterKey: true });
expect(user.get('emailVerified')).toEqual(false);
});
it('should work with plain token', async function () {
expect(this.user.get('emailVerified')).toEqual(false);
it_id('92bbb86d-bcda-49fa-8d79-aa0501078044')(it)('should work with plain token', async () => {
expect(user.get('emailVerified')).toEqual(false);
const current = await request({
method: 'GET',
url: `http://localhost:8378/1/classes/_User/${this.user.id}`,
url: `http://localhost:8378/1/classes/_User/${user.id}`,
json: true,
headers: {
'X-Parse-Application-Id': 'test',
@@ -115,18 +120,18 @@ describe('Regex Vulnerabilities', function () {
url: `${serverURL}/apps/test/verify_email?username=someemail@somedomain.com&token=${current._email_verify_token}`,
method: 'GET',
});
await this.user.fetch({ useMasterKey: true });
expect(this.user.get('emailVerified')).toEqual(true);
await user.fetch({ useMasterKey: true });
expect(user.get('emailVerified')).toEqual(true);
});
});
describe('on password reset', function () {
beforeEach(async function () {
this.user = await Parse.User.logIn('someemail@somedomain.com', 'somepassword');
describe('on password reset', () => {
beforeEach(async () => {
user = await Parse.User.logIn('someemail@somedomain.com', 'somepassword');
});
it('should not work with regex', async function () {
expect(this.user.id).toEqual(this.objectId);
it('should not work with regex', async () => {
expect(user.id).toEqual(objectId);
await request({
url: `${serverURL}/requestPasswordReset`,
method: 'POST',
@@ -137,7 +142,7 @@ describe('Regex Vulnerabilities', function () {
email: 'someemail@somedomain.com',
}),
});
await this.user.fetch({ useMasterKey: true });
await user.fetch({ useMasterKey: true });
const passwordResetResponse = await request({
url: `${serverURL}/apps/test/request_password_reset?username=someemail@somedomain.com&token[$regex]=`,
method: 'GET',
@@ -162,8 +167,8 @@ describe('Regex Vulnerabilities', function () {
}
});
it('should work with plain token', async function () {
expect(this.user.id).toEqual(this.objectId);
it('should work with plain token', async () => {
expect(user.id).toEqual(objectId);
await request({
url: `${serverURL}/requestPasswordReset`,
method: 'POST',
@@ -176,7 +181,7 @@ describe('Regex Vulnerabilities', function () {
});
const current = await request({
method: 'GET',
url: `http://localhost:8378/1/classes/_User/${this.user.id}`,
url: `http://localhost:8378/1/classes/_User/${user.id}`,
json: true,
headers: {
'X-Parse-Application-Id': 'test',
@@ -204,7 +209,7 @@ describe('Regex Vulnerabilities', function () {
},
});
const userAgain = await Parse.User.logIn('someemail@somedomain.com', 'newpassword');
expect(userAgain.id).toEqual(this.objectId);
expect(userAgain.id).toEqual(objectId);
});
});
});
+2 -2
View File
@@ -392,7 +392,7 @@ describe('RestQuery.each', () => {
beforeEach(() => {
config = Config.get('test');
});
it('should run each', async () => {
it_id('3416c90b-ee2e-4bb5-9231-46cd181cd0a2')(it)('should run each', async () => {
const objects = [];
while (objects.length != 10) {
objects.push(new Parse.Object('Object', { value: objects.length }));
@@ -419,7 +419,7 @@ describe('RestQuery.each', () => {
expect(results.length).toBe(7);
});
it('should work with query on relations', async () => {
it_id('0fe22501-4b18-461e-b87d-82ceac4a496e')(it)('should work with query on relations', async () => {
const objectA = new Parse.Object('Letter', { value: 'A' });
const objectB = new Parse.Object('Letter', { value: 'B' });
+2 -2
View File
@@ -205,7 +205,7 @@ describe('Schema Performance', function () {
expect(getAllSpy.calls.count()).toBe(2);
});
it('does reload with schemaCacheTtl', async () => {
it_id('9dd70965-b683-4cb8-b43a-44c1f4def9f4')(it)('does reload with schemaCacheTtl', async () => {
const databaseURI =
process.env.PARSE_SERVER_TEST_DB === 'postgres'
? process.env.PARSE_SERVER_TEST_DATABASE_URI
@@ -241,7 +241,7 @@ describe('Schema Performance', function () {
expect(spy.reloadCalls).toBe(1);
});
it('cannot set invalid databaseOptions', async () => {
it_id('b0ae21f2-c947-48ed-a0db-e8900d45a4c8')(it)('cannot set invalid databaseOptions', async () => {
const expectError = async (key, value, expected) =>
expectAsync(
reconfigureServer({ databaseAdapter: undefined, databaseOptions: { [key]: value } })
+1 -1
View File
@@ -68,7 +68,7 @@ describe('Uniqueness', function () {
});
});
it('fails when attempting to ensure uniqueness of fields that are not currently unique', done => {
it_id('802650a9-a6db-447e-88d0-8aae99100088')(it)('fails when attempting to ensure uniqueness of fields that are not currently unique', done => {
const o1 = new Parse.Object('UniqueFail');
o1.set('key', 'val');
const o2 = new Parse.Object('UniqueFail');
+2 -2
View File
@@ -5,7 +5,7 @@ const Auth = require('../lib/Auth');
describe('UserController', () => {
describe('sendVerificationEmail', () => {
describe('parseFrameURL not provided', () => {
it('uses publicServerURL', async () => {
it_id('61338330-eca7-4c33-8816-7ff05966f43b')(it)('uses publicServerURL', async () => {
await reconfigureServer({
publicServerURL: 'http://www.example.com',
customPages: {
@@ -40,7 +40,7 @@ describe('UserController', () => {
});
describe('parseFrameURL provided', () => {
it('uses parseFrameURL and includes the destination in the link parameter', async () => {
it_id('673c2bb1-049e-4dda-b6be-88c866260036')(it)('uses parseFrameURL and includes the destination in the link parameter', async () => {
await reconfigureServer({
publicServerURL: 'http://www.example.com',
customPages: {
+6 -6
View File
@@ -33,7 +33,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
});
});
it('sends verification email if email verification is enabled', done => {
it_id('5e558687-40f3-496c-9e4f-af6100bd1b2f')(it)('sends verification email if email verification is enabled', done => {
const emailAdapter = {
sendVerificationEmail: () => Promise.resolve(),
sendPasswordResetEmail: () => Promise.resolve(),
@@ -158,7 +158,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
}, 200);
});
it('does send with a simple adapter', done => {
it_id('33d31119-c724-4f5d-83ec-f56815d23df3')(it)('does send with a simple adapter', done => {
let calls = 0;
const emailAdapter = {
sendMail: function (options) {
@@ -305,7 +305,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
expect(verifyUserEmailsSpy).toHaveBeenCalledTimes(2);
});
it('allows user to login only after user clicks on the link to confirm email address if preventLoginWithUnverifiedEmail is set to true', async () => {
it_id('2a5d24be-2ca5-4385-b580-1423bd392e43')(it)('allows user to login only after user clicks on the link to confirm email address if preventLoginWithUnverifiedEmail is set to true', async () => {
let sendEmailOptions;
const emailAdapter = {
sendVerificationEmail: options => {
@@ -381,7 +381,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
});
});
it('does not allow signup with preventSignupWithUnverified', async () => {
it_id('a18a07af-0319-4f15-8237-28070c5948fa')(it)('does not allow signup with preventSignupWithUnverified', async () => {
let sendEmailOptions;
const emailAdapter = {
sendVerificationEmail: options => {
@@ -616,7 +616,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
});
});
it('receives the app name and user in the adapter', done => {
it_id('45f550a2-a2b2-4b2b-b533-ccbf96139cc9')(it)('receives the app name and user in the adapter', done => {
let emailSent = false;
const emailAdapter = {
sendVerificationEmail: options => {
@@ -644,7 +644,7 @@ describe('Custom Pages, Email Verification, Password Reset', () => {
});
});
it('when you click the link in the email it sets emailVerified to true and redirects you', done => {
it_id('ea37ef62-aad8-4a17-8dfe-35e5b2986f0f')(it)('when you click the link in the email it sets emailVerified to true and redirects you', done => {
const user = new Parse.User();
let sendEmailOptions;
const emailAdapter = {
+1 -1
View File
@@ -174,7 +174,7 @@ describe_only(() => {
describe_only(() => {
return process.env.PARSE_SERVER_LOG_LEVEL !== 'debug';
})('verbose logs', () => {
it('mask sensitive information in _User class', done => {
it_id('9ca72994-d255-4c11-a5a2-693c99ee2cdb')(it)('mask sensitive information in _User class', done => {
reconfigureServer({ verbose: true })
.then(() => createTestUser())
.then(() => {
+27 -39
View File
@@ -1,6 +1,5 @@
const batch = require('../lib/batch');
const request = require('../lib/request');
const TestUtils = require('../lib/TestUtils');
const originalURL = '/parse/batch';
const serverURL = 'http://localhost:1234/parse';
@@ -18,9 +17,13 @@ const headers = {
};
describe('batch', () => {
let createSpy;
beforeEach(async () => {
createSpy = spyOn(databaseAdapter, 'createObject').and.callThrough();
});
it('should return the proper url', () => {
const internalURL = batch.makeBatchRoutingPathFunction(originalURL)('/parse/classes/Object');
expect(internalURL).toEqual('/classes/Object');
});
@@ -133,8 +136,6 @@ describe('batch', () => {
});
it('should handle a batch request without transaction', async () => {
spyOn(databaseAdapter, 'createObject').and.callThrough();
const response = await request({
method: 'POST',
headers: headers,
@@ -154,7 +155,6 @@ describe('batch', () => {
],
}),
});
expect(response.data.length).toEqual(2);
expect(response.data[0].success.objectId).toBeDefined();
expect(response.data[0].success.createdAt).toBeDefined();
@@ -162,15 +162,13 @@ describe('batch', () => {
expect(response.data[1].success.createdAt).toBeDefined();
const query = new Parse.Query('MyObject');
const results = await query.find();
expect(databaseAdapter.createObject.calls.count()).toBe(2);
expect(databaseAdapter.createObject.calls.argsFor(0)[3]).toEqual(null);
expect(databaseAdapter.createObject.calls.argsFor(1)[3]).toEqual(null);
expect(createSpy.calls.count()).toBe(2);
expect(createSpy.calls.argsFor(0)[3]).toEqual(null);
expect(createSpy.calls.argsFor(1)[3]).toEqual(null);
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
});
it('should handle a batch request with transaction = false', async () => {
spyOn(databaseAdapter, 'createObject').and.callThrough();
const response = await request({
method: 'POST',
headers: headers,
@@ -196,11 +194,12 @@ describe('batch', () => {
expect(response.data[0].success.createdAt).toBeDefined();
expect(response.data[1].success.objectId).toBeDefined();
expect(response.data[1].success.createdAt).toBeDefined();
const query = new Parse.Query('MyObject');
const results = await query.find();
expect(databaseAdapter.createObject.calls.count()).toBe(2);
expect(databaseAdapter.createObject.calls.argsFor(0)[3]).toEqual(null);
expect(databaseAdapter.createObject.calls.argsFor(1)[3]).toEqual(null);
expect(createSpy.calls.count()).toBe(2);
expect(createSpy.calls.argsFor(0)[3]).toEqual(null);
expect(createSpy.calls.argsFor(1)[3]).toEqual(null);
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
});
@@ -209,24 +208,11 @@ describe('batch', () => {
process.env.PARSE_SERVER_TEST_DB === 'postgres'
) {
describe('transactions', () => {
beforeEach(async () => {
await TestUtils.destroyAllDataPermanently(true);
if (process.env.MONGODB_TOPOLOGY === 'replicaset') {
await reconfigureServer({
databaseAdapter: undefined,
databaseURI:
'mongodb://localhost:27017/parseServerMongoAdapterTestDatabase?replicaSet=replicaset',
});
} else {
await reconfigureServer();
}
});
it('should handle a batch request with transaction = true', async () => {
const myObject = new Parse.Object('MyObject'); // This is important because transaction only works on pre-existing collections
await myObject.save();
await myObject.destroy();
spyOn(databaseAdapter, 'createObject').and.callThrough();
createSpy.calls.reset();
const response = await request({
method: 'POST',
headers: headers,
@@ -254,10 +240,10 @@ describe('batch', () => {
expect(response.data[1].success.createdAt).toBeDefined();
const query = new Parse.Query('MyObject');
const results = await query.find();
expect(databaseAdapter.createObject.calls.count() % 2).toBe(0);
for (let i = 0; i + 1 < databaseAdapter.createObject.calls.length; i = i + 2) {
expect(databaseAdapter.createObject.calls.argsFor(i)[3]).toBe(
databaseAdapter.createObject.calls.argsFor(i + 1)[3]
expect(createSpy.calls.count()).toBe(2);
for (let i = 0; i + 1 < createSpy.calls.length; i = i + 2) {
expect(createSpy.calls.argsFor(i)[3]).toBe(
createSpy.calls.argsFor(i + 1)[3]
);
}
expect(results.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
@@ -265,9 +251,11 @@ describe('batch', () => {
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
await myObject.save();
await myObject.save({ key: 'stringField' });
await myObject.destroy();
createSpy.calls.reset();
try {
// Saving a number to a string field should fail
await request({
method: 'POST',
headers: headers,
@@ -380,20 +368,20 @@ 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();
await myObject.save({ key: 'stringField' });
await myObject.destroy();
const myObject2 = new Parse.Object('MyObject2'); // This is important because transaction only works on pre-existing collections
await myObject2.save();
await myObject2.save({ key: 'stringField' });
await myObject2.destroy();
spyOn(databaseAdapter, 'createObject').and.callThrough();
createSpy.calls.reset();
let myObjectCalls = 0;
Parse.Cloud.beforeSave('MyObject', async () => {
myObjectCalls++;
if (myObjectCalls === 2) {
try {
// Saving a number to a string field should fail
await request({
method: 'POST',
headers: headers,
@@ -560,14 +548,14 @@ describe('batch', () => {
const results3 = await query3.find();
expect(results3.map(result => result.get('key')).sort()).toEqual(['value1', 'value2']);
expect(databaseAdapter.createObject.calls.count() >= 13).toEqual(true);
expect(createSpy.calls.count() >= 13).toEqual(true);
let transactionalSession;
let transactionalSession2;
let myObjectDBCalls = 0;
let myObject2DBCalls = 0;
let myObject3DBCalls = 0;
for (let i = 0; i < databaseAdapter.createObject.calls.count(); i++) {
const args = databaseAdapter.createObject.calls.argsFor(i);
for (let i = 0; i < createSpy.calls.count(); i++) {
const args = createSpy.calls.argsFor(i);
switch (args[0]) {
case 'MyObject':
myObjectDBCalls++;
+14 -8
View File
@@ -14,6 +14,7 @@ if (dns.setDefaultResultOrder) {
jasmine.DEFAULT_TIMEOUT_INTERVAL = process.env.PARSE_SERVER_TEST_TIMEOUT || 10000;
jasmine.getEnv().addReporter(new CurrentSpecReporter());
jasmine.getEnv().addReporter(new SpecReporter());
global.retryFlakyTests();
global.on_db = (db, callback, elseCallback) => {
if (process.env.PARSE_SERVER_TEST_DB == db) {
@@ -287,7 +288,7 @@ afterEach(function (done) {
});
afterAll(() => {
global.displaySlowTests();
global.displayTestStats();
});
const TestObject = Parse.Object.extend({
@@ -466,13 +467,18 @@ try {
}
}
// Disable test if its UUID is found in testExclusionList
global.it_id = (id, func) => {
if (testExclusionList.includes(id)) {
return xit;
} else {
return func || it;
}
/**
* Assign ID to test and run it. Disable test if its UUID is found in testExclusionList.
* @param {String} id The UUID of the test.
*/
global.it_id = id => {
return testFunc => {
if (testExclusionList.includes(id)) {
return xit;
} else {
return testFunc;
}
};
};
global.it_only_db = db => {
+10
View File
@@ -1,6 +1,7 @@
const {
numberParser,
numberOrBoolParser,
numberOrStringParser,
booleanParser,
objectParser,
arrayParser,
@@ -18,6 +19,15 @@ describe('parsers', () => {
}).toThrow();
});
it('parses correctly with numberOrStringParser', () => {
const parser = numberOrStringParser('key');
expect(parser('100d')).toEqual('100d');
expect(parser(100)).toEqual(100);
expect(() => {
parser(undefined);
}).toThrow();
});
it('parses correctly with numberOrBoolParser', () => {
const parser = numberOrBoolParser('key');
expect(parser(true)).toEqual(true);
+2 -2
View File
@@ -247,7 +247,7 @@ describe('rest create', () => {
});
});
it('handles array, object, date', done => {
it_id('6c30306f-328c-47f2-88a7-2deffaee997f')(it)('handles array, object, date', done => {
const now = new Date();
const obj = {
array: [1, 2, 3],
@@ -787,7 +787,7 @@ describe('rest create', () => {
);
});
it('cannot get object in _GlobalConfig if not masterKey through pointer', async () => {
it_id('3ce563bf-93aa-4d0b-9af9-c5fb246ac9fc')(it)('cannot get object in _GlobalConfig if not masterKey through pointer', async () => {
await Parse.Config.save({ privateData: 'secret' }, { privateData: true });
const obj2 = new Parse.Object('TestObject');
obj2.set('globalConfigPointer', {
+3 -3
View File
@@ -3650,7 +3650,7 @@ describe('schemas', () => {
});
});
it_exclude_dbs(['postgres'])('get indexes on startup', done => {
it_id('5d0926b2-2d31-459d-a2b1-23ecc32e72a3')(it_exclude_dbs(['postgres']))('get indexes on startup', done => {
const obj = new Parse.Object('TestObject');
obj
.save()
@@ -3673,7 +3673,7 @@ describe('schemas', () => {
});
});
it_exclude_dbs(['postgres'])('get compound indexes on startup', done => {
it_id('9f2ba51a-6a9c-4b25-9da0-51c82ac65f90')(it_exclude_dbs(['postgres']))('get compound indexes on startup', done => {
const obj = new Parse.Object('TestObject');
obj.set('subject', 'subject');
obj.set('comment', 'comment');
@@ -3708,7 +3708,7 @@ describe('schemas', () => {
});
});
it_exclude_dbs(['postgres'])('cannot update to duplicate value on unique index', done => {
it_id('cbd5d897-b938-43a4-8f5a-5d02dd2be9be')(it_exclude_dbs(['postgres']))('cannot update to duplicate value on unique index', done => {
const index = {
code: 1,
};
+84 -7
View File
@@ -1,13 +1,36 @@
// Sets a global variable to the current test spec
// ex: global.currentSpec.description
const { performance } = require('perf_hooks');
global.currentSpec = null;
const timerMap = {};
const duplicates = [];
/**
* Names of tests that fail randomly and are considered flaky. These tests will be retried
* a number of times to reduce the chance of false negatives. The test name must be the same
* as the one displayed in the CI log test output.
*/
const flakyTests = [
// Timeout
"ParseLiveQuery handle invalid websocket payload length",
// Unhandled promise rejection: TypeError: message.split is not a function
"rest query query internal field",
// TypeError: Cannot read properties of undefined (reading 'link')
"UserController sendVerificationEmail parseFrameURL not provided uses publicServerURL",
// TypeError: Cannot read properties of undefined (reading 'link')
"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",
];
/** The minimum execution time in seconds for a test to be considered slow. */
const slowTestLimit = 2;
/** The number of times to retry a flaky test. */
const retries = 5;
const timerMap = {};
const retryMap = {};
const duplicates = [];
class CurrentSpecReporter {
specStarted(spec) {
if (timerMap[spec.fullName]) {
@@ -26,20 +49,74 @@ class CurrentSpecReporter {
global.currentSpec = null;
}
}
global.displaySlowTests = function() {
const times = Object.values(timerMap).sort((a,b) => b - a);
global.displayTestStats = function() {
const times = Object.values(timerMap).sort((a,b) => b - a).filter(time => time >= slowTestLimit);
if (times.length > 0) {
console.log(`Slow tests with execution time >=${slowTestLimit}s:`);
}
times.forEach((time) => {
if (time >= slowTestLimit) {
console.warn(`${time.toFixed(1)}s:`, Object.keys(timerMap).find(key => timerMap[key] === time));
}
console.warn(`${time.toFixed(1)}s:`, Object.keys(timerMap).find(key => timerMap[key] === time));
});
console.log('\n');
duplicates.forEach((spec) => {
console.warn('Duplicate spec: ' + spec);
});
console.log('\n');
Object.keys(retryMap).forEach((spec) => {
console.warn(`Flaky test: ${spec} failed ${retryMap[spec]} times`);
});
console.log('\n');
};
global.retryFlakyTests = function() {
const originalSpecConstructor = jasmine.Spec;
jasmine.Spec = function(attrs) {
const spec = new originalSpecConstructor(attrs);
const originalTestFn = spec.queueableFn.fn;
const runOriginalTest = () => {
if (originalTestFn.length == 0) {
// handle async testing
return originalTestFn();
} else {
// handle done() callback
return new Promise((resolve) => {
originalTestFn(resolve);
});
}
};
spec.queueableFn.fn = async function() {
const isFlaky = flakyTests.includes(spec.result.fullName);
const runs = isFlaky ? retries : 1;
let exceptionCaught;
let returnValue;
for (let i = 0; i < runs; ++i) {
spec.result.failedExpectations = [];
returnValue = undefined;
exceptionCaught = undefined;
try {
returnValue = await runOriginalTest();
} catch (exception) {
exceptionCaught = exception;
}
const failed = !spec.markedPending &&
(exceptionCaught || spec.result.failedExpectations.length != 0);
if (!failed) {
break;
}
if (isFlaky) {
retryMap[spec.result.fullName] = (retryMap[spec.result.fullName] || 0) + 1;
}
}
if (exceptionCaught) {
throw exceptionCaught;
}
return returnValue;
};
return spec;
};
}
module.exports = CurrentSpecReporter;
+1 -1
View File
@@ -2,5 +2,5 @@
"spec_dir": "spec",
"spec_files": ["*spec.js"],
"helpers": ["helper.js"],
"random": false
"random": true
}
-18
View File
@@ -1,18 +0,0 @@
// Create Deferred Promise
function resolvingPromise() {
let res;
let rej;
const promise = new Promise((resolve, reject) => {
res = resolve;
rej = reject;
});
promise.resolve = res;
promise.reject = rej;
return promise;
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
module.exports = { resolvingPromise, sleep };
+46 -1
View File
@@ -1,6 +1,51 @@
const request = require('../lib/request');
describe('Vulnerabilities', () => {
describe('(GHSA-8xq9-g7ch-35hg) Custom object ID allows to acquire role privilege', () => {
beforeAll(async () => {
await reconfigureServer({ allowCustomObjectId: true });
Parse.allowCustomObjectId = true;
});
afterAll(async () => {
await reconfigureServer({ allowCustomObjectId: false });
Parse.allowCustomObjectId = false;
});
it('denies user creation with poisoned object ID', async () => {
await expectAsync(
new Parse.User({ id: 'role:a', username: 'a', password: '123' }).save()
).toBeRejectedWith(new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid object ID.'));
});
describe('existing sessions for users with poisoned object ID', () => {
/** @type {Parse.User} */
let poisonedUser;
/** @type {Parse.User} */
let innocentUser;
beforeAll(async () => {
const parseServer = await global.reconfigureServer();
const databaseController = parseServer.config.databaseController;
[poisonedUser, innocentUser] = await Promise.all(
['role:abc', 'abc'].map(async id => {
// Create the users directly on the db to bypass the user creation check
await databaseController.create('_User', { objectId: id });
// Use the master key to create a session for them to bypass the session check
return Parse.User.loginAs(id);
})
);
});
it('refuses session token of user with poisoned object ID', async () => {
await expectAsync(
new Parse.Query(Parse.User).find({ sessionToken: poisonedUser.getSessionToken() })
).toBeRejectedWith(new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Invalid object ID.'));
await new Parse.Query(Parse.User).find({ sessionToken: innocentUser.getSessionToken() });
});
});
});
describe('Object prototype pollution', () => {
it('denies object prototype to be polluted with keyword "constructor"', async () => {
const headers = {
@@ -203,7 +248,7 @@ describe('Vulnerabilities', () => {
);
});
it('denies creating a hook with polluted data', async () => {
it_id('e8b5f1e1-8326-4c70-b5f4-1e8678dfff8d')(it)('denies creating a hook with polluted data', async () => {
const express = require('express');
const bodyParser = require('body-parser');
const port = 34567;
+2 -2
View File
@@ -59,9 +59,9 @@ export class FilesAdapter {
* @param {Config} config - server configuration
* @param {string} filename
*
* @return {string} Absolute URL
* @return {string | Promise<string>} Absolute URL
*/
getFileLocation(config: Config, filename: string): string {}
getFileLocation(config: Config, filename: string): string | Promise<string> {}
/** Validate a filename for this adapter type
*
+5
View File
@@ -180,6 +180,11 @@ const getAuthForSessionToken = async function ({
throw new Parse.Error(Parse.Error.INVALID_SESSION_TOKEN, 'Session token is expired.');
}
const obj = session.user;
if (typeof obj['objectId'] === 'string' && obj['objectId'].startsWith('role:')) {
throw new Parse.Error(Parse.Error.INTERNAL_SERVER_ERROR, 'Invalid object ID.');
}
delete obj.password;
obj['className'] = '_User';
obj['sessionToken'] = sessionToken;
+12 -12
View File
@@ -15,7 +15,7 @@ export class FilesController extends AdaptableController {
return this.adapter.getFileData(filename);
}
createFile(config, filename, data, contentType, options) {
async createFile(config, filename, data, contentType, options) {
const extname = path.extname(filename);
const hasExtension = extname.length > 0;
@@ -30,13 +30,12 @@ export class FilesController extends AdaptableController {
filename = randomHexString(32) + '_' + filename;
}
const location = this.adapter.getFileLocation(config, filename);
return this.adapter.createFile(filename, data, contentType, options).then(() => {
return Promise.resolve({
url: location,
name: filename,
});
});
const location = await this.adapter.getFileLocation(config, filename);
await this.adapter.createFile(filename, data, contentType, options);
return {
url: location,
name: filename,
}
}
deleteFile(config, filename) {
@@ -55,9 +54,10 @@ export class FilesController extends AdaptableController {
* with the current mount point and app id.
* Object may be a single object or list of REST-format objects.
*/
expandFilesInObject(config, object) {
async expandFilesInObject(config, object) {
if (object instanceof Array) {
object.map(obj => this.expandFilesInObject(config, obj));
const promises = object.map(obj => this.expandFilesInObject(config, obj));
await Promise.all(promises);
return;
}
if (typeof object !== 'object') {
@@ -74,7 +74,7 @@ export class FilesController extends AdaptableController {
// all filenames starting with a "-" seperated UUID should be from files.parse.com
// all other filenames have been migrated or created from Parse Server
if (config.fileKey === undefined) {
fileObject['url'] = this.adapter.getFileLocation(config, filename);
fileObject['url'] = await this.adapter.getFileLocation(config, filename);
} else {
if (filename.indexOf('tfss-') === 0) {
fileObject['url'] =
@@ -83,7 +83,7 @@ export class FilesController extends AdaptableController {
fileObject['url'] =
'http://files.parse.com/' + config.fileKey + '/' + encodeURIComponent(filename);
} else {
fileObject['url'] = this.adapter.getFileLocation(config, filename);
fileObject['url'] = await this.adapter.getFileLocation(config, filename);
}
}
}
+1 -1
View File
@@ -378,7 +378,7 @@ module.exports.ParseServerOptions = {
env: 'PARSE_SERVER_MAX_LOG_FILES',
help:
"Maximum number of logs to keep. If not set, no logs will be removed. This can be a number of files or number of days. If using days, add 'd' as the suffix. (default: null)",
action: parsers.objectParser,
action: parsers.numberOrStringParser('maxLogFiles'),
},
maxUploadSize: {
env: 'PARSE_SERVER_MAX_UPLOAD_SIZE',
+10
View File
@@ -23,6 +23,15 @@ function numberOrBoolParser(key) {
};
}
function numberOrStringParser(key) {
return function (opt) {
if (typeof opt === 'string') {
return opt;
}
return numberParser(key)(opt);
};
}
function objectParser(opt) {
if (typeof opt == 'object') {
return opt;
@@ -69,6 +78,7 @@ function nullParser(opt) {
module.exports = {
numberParser,
numberOrBoolParser,
numberOrStringParser,
nullParser,
booleanParser,
moduleOrObjectParser,
+14 -17
View File
@@ -735,7 +735,7 @@ _UnsafeRestQuery.prototype.replaceEquality = function () {
// Returns a promise for whether it was successful.
// Populates this.response with an object that only has 'results'.
_UnsafeRestQuery.prototype.runFind = function (options = {}) {
_UnsafeRestQuery.prototype.runFind = async function (options = {}) {
if (this.findOptions.limit === 0) {
this.response = { results: [] };
return Promise.resolve();
@@ -749,24 +749,21 @@ _UnsafeRestQuery.prototype.runFind = function (options = {}) {
if (options.op) {
findOptions.op = options.op;
}
return this.config.database
.find(this.className, this.restWhere, findOptions, this.auth)
.then(results => {
if (this.className === '_User' && !findOptions.explain) {
for (var result of results) {
this.cleanResultAuthData(result);
}
}
const results = await this.config.database.find(this.className, this.restWhere, findOptions, this.auth);
if (this.className === '_User' && !findOptions.explain) {
for (var result of results) {
this.cleanResultAuthData(result);
}
}
this.config.filesController.expandFilesInObject(this.config, results);
await this.config.filesController.expandFilesInObject(this.config, results);
if (this.redirectClassName) {
for (var r of results) {
r.className = this.redirectClassName;
}
}
this.response = { results: results };
});
if (this.redirectClassName) {
for (var r of results) {
r.className = this.redirectClassName;
}
}
this.response = { results: results };
};
// Returns a promise for whether it was successful.
+3 -3
View File
@@ -322,7 +322,7 @@ RestWrite.prototype.runBeforeLoginTrigger = async function (userData) {
const extraData = { className: this.className };
// Expand file objects
this.config.filesController.expandFilesInObject(this.config, userData);
await this.config.filesController.expandFilesInObject(this.config, userData);
const user = triggers.inflate(extraData, userData);
@@ -1412,10 +1412,10 @@ RestWrite.prototype.handleInstallation = function () {
// If we short-circuited the object response - then we need to make sure we expand all the files,
// since this might not have a query, meaning it won't return the full result back.
// TODO: (nlutsenko) This should die when we move to per-class based controllers on _Session/_User
RestWrite.prototype.expandFilesForExistingObjects = function () {
RestWrite.prototype.expandFilesForExistingObjects = async function () {
// Check whether we have a short-circuited response - only then run expansion.
if (this.response && this.response.response) {
this.config.filesController.expandFilesInObject(this.config, this.response.response);
await this.config.filesController.expandFilesInObject(this.config, this.response.response);
}
};
+7
View File
@@ -106,6 +106,13 @@ export class ClassesRouter extends PromiseRouter {
}
handleCreate(req) {
if (
this.className(req) === '_User' &&
typeof req.body?.objectId === 'string' &&
req.body.objectId.startsWith('role:')
) {
throw new Parse.Error(Parse.Error.OPERATION_FORBIDDEN, 'Invalid object ID.');
}
return rest.create(
req.config,
req.auth,
+1 -1
View File
@@ -263,7 +263,7 @@ export class FilesRouter {
const { filename } = req.params;
// run beforeDeleteFile trigger
const file = new Parse.File(filename);
file._url = filesController.adapter.getFileLocation(req.config, filename);
file._url = await filesController.adapter.getFileLocation(req.config, filename);
const fileObject = { file, fileSize: null };
await triggers.maybeRunFileTrigger(
triggers.Types.beforeDelete,
+41 -4
View File
@@ -2,6 +2,15 @@
import Parse from 'parse/node';
import PromiseRouter from '../PromiseRouter';
import * as middleware from '../middlewares';
import * as triggers from '../triggers';
const getConfigFromParams = params => {
const config = new Parse.Config();
for (const attr in params) {
config.attributes[attr] = Parse._decode(undefined, params[attr]);
}
return config;
};
export class GlobalConfigRouter extends PromiseRouter {
getGlobalConfig(req) {
@@ -30,7 +39,7 @@ export class GlobalConfigRouter extends PromiseRouter {
});
}
updateGlobalConfig(req) {
async updateGlobalConfig(req) {
if (req.auth.isReadOnly) {
throw new Parse.Error(
Parse.Error.OPERATION_FORBIDDEN,
@@ -45,9 +54,37 @@ export class GlobalConfigRouter extends PromiseRouter {
acc[`masterKeyOnly.${key}`] = masterKeyOnly[key] || false;
return acc;
}, {});
return req.config.database
.update('_GlobalConfig', { objectId: '1' }, update, { upsert: true })
.then(() => ({ response: { result: true } }));
const className = triggers.getClassName(Parse.Config);
const hasBeforeSaveHook = triggers.triggerExists(className, triggers.Types.beforeSave, req.config.applicationId);
const hasAfterSaveHook = triggers.triggerExists(className, triggers.Types.afterSave, req.config.applicationId);
let originalConfigObject;
let updatedConfigObject;
const configObject = new Parse.Config();
configObject.attributes = params;
const results = await req.config.database.find('_GlobalConfig', { objectId: '1' }, { limit: 1 });
const isNew = results.length !== 1;
if (!isNew && (hasBeforeSaveHook || hasAfterSaveHook)) {
originalConfigObject = getConfigFromParams(results[0].params);
}
try {
await triggers.maybeRunGlobalConfigTrigger(triggers.Types.beforeSave, req.auth, configObject, originalConfigObject, req.config, req.context);
if (isNew) {
await req.config.database.update('_GlobalConfig', { objectId: '1' }, update, { upsert: true }, true)
updatedConfigObject = configObject;
} else {
const result = await req.config.database.update('_GlobalConfig', { objectId: '1' }, update, {}, true);
updatedConfigObject = getConfigFromParams(result.params);
}
await triggers.maybeRunGlobalConfigTrigger(triggers.Types.afterSave, req.auth, updatedConfigObject, originalConfigObject, req.config, req.context);
return { response: { result: true } }
} catch (err) {
const error = triggers.resolveError(err, {
code: Parse.Error.SCRIPT_FAILED,
message: 'Script failed. Unknown error.',
});
throw error;
}
}
mountRoutes() {
+1 -1
View File
@@ -264,7 +264,7 @@ export class UsersRouter extends ClassesRouter {
// Remove hidden properties.
UsersRouter.removeHiddenProperties(user);
req.config.filesController.expandFilesInObject(req.config, user);
await req.config.filesController.expandFilesInObject(req.config, user);
// Before login trigger; throws if failure
await maybeRunTrigger(
+16
View File
@@ -26,3 +26,19 @@ export function destroyAllDataPermanently(fast) {
})
);
}
export function resolvingPromise() {
let res;
let rej;
const promise = new Promise((resolve, reject) => {
res = resolve;
rej = reject;
});
promise.resolve = res;
promise.reject = rej;
return promise;
}
export function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
+4
View File
@@ -79,10 +79,14 @@ const getRoute = parseClass => {
_User: 'users',
_Session: 'sessions',
'@File': 'files',
'@Config' : 'config',
}[parseClass] || 'classes';
if (parseClass === '@File') {
return `/${route}/:id?(.*)`;
}
if (parseClass === '@Config') {
return `/${route}`;
}
return `/${route}/${parseClass}/:id?(.*)`;
};
/** @namespace
+35
View File
@@ -1027,3 +1027,38 @@ export async function maybeRunFileTrigger(triggerType, fileObject, config, auth)
}
return fileObject;
}
export async function maybeRunGlobalConfigTrigger(triggerType, auth, configObject, originalConfigObject, config, context) {
const GlobalConfigClassName = getClassName(Parse.Config);
const configTrigger = getTrigger(GlobalConfigClassName, triggerType, config.applicationId);
if (typeof configTrigger === 'function') {
try {
const request = getRequestObject(triggerType, auth, configObject, originalConfigObject, config, context);
await maybeRunValidator(request, `${triggerType}.${GlobalConfigClassName}`, auth);
if (request.skipWithMasterKey) {
return configObject;
}
const result = await configTrigger(request);
logTriggerSuccessBeforeHook(
triggerType,
'Parse.Config',
configObject,
result,
auth,
config.logLevels.triggerBeforeSuccess
);
return result || configObject;
} catch (error) {
logTriggerErrorBeforeHook(
triggerType,
'Parse.Config',
configObject,
auth,
error,
config.logLevels.triggerBeforeError
);
throw error;
}
}
return configObject;
}