From 170df208b986fc5cf83f756090bf1e36be014587 Mon Sep 17 00:00:00 2001 From: Jonas Rittershofer Date: Tue, 29 Mar 2022 20:12:27 +0200 Subject: [PATCH] Some basic API tests Signed-off-by: Jonas Rittershofer --- .github/workflows/phpunit.yml | 27 +- composer.json | 3 +- composer.lock | 598 ++++++++++++- tests/Integration/Api/ApiV2Test.php | 1219 +++++++++++++++++++++++++++ 4 files changed, 1840 insertions(+), 7 deletions(-) create mode 100644 tests/Integration/Api/ApiV2Test.php diff --git a/.github/workflows/phpunit.yml b/.github/workflows/phpunit.yml index 9a277df11..8a9c11038 100644 --- a/.github/workflows/phpunit.yml +++ b/.github/workflows/phpunit.yml @@ -54,9 +54,11 @@ jobs: - name: Set up Nextcloud env: DB_PORT: 4444 + OC_PASS: test run: | mkdir data ./occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password + ./occ user:add --password-from-env --display-name="Test Displayname" test ./occ app:enable --force ${{ env.APP_NAME }} php -S localhost:8080 & @@ -68,14 +70,23 @@ jobs: working-directory: apps/${{ env.APP_NAME }} run: composer test:integration - - name: Upload coverage + - name: Upload Unit coverage if: matrix.php-versions == matrix.coverage-on uses: codecov/codecov-action@v1 with: root_dir: apps/${{ env.APP_NAME }} files: apps/${{ env.APP_NAME }}/tests/clover.unit.xml fail_ci_if_error: true - flags: unittests + flags: unit + + - name: Upload Integration coverage + if: matrix.php-versions == matrix.coverage-on + uses: codecov/codecov-action@v1 + with: + root_dir: apps/${{ env.APP_NAME }} + files: apps/${{ env.APP_NAME }}/tests/clover.integration.xml + fail_ci_if_error: true + flags: integration mysql: runs-on: ubuntu-latest @@ -127,9 +138,11 @@ jobs: - name: Set up Nextcloud env: DB_PORT: 4444 + OC_PASS: test run: | mkdir data ./occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password + ./occ user:add --password-from-env --display-name="Test Displayname" test ./occ app:enable --force ${{ env.APP_NAME }} php -S localhost:8080 & @@ -193,9 +206,11 @@ jobs: - name: Set up Nextcloud env: DB_PORT: 4444 + OC_PASS: test run: | mkdir data ./occ maintenance:install --verbose --database=${{ matrix.databases }} --database-name=nextcloud --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=root --database-pass=rootpassword --admin-user admin --admin-pass password + ./occ user:add --password-from-env --display-name="Test Displayname" test ./occ app:enable --force ${{ env.APP_NAME }} php -S localhost:8080 & @@ -224,7 +239,7 @@ jobs: oracle: image: deepdiver/docker-oracle-xe-11g # "wnameless/oracle-xe-11g-r2" ports: - - "1521:1521" + - 4444:1521/tcp steps: - name: Checkout server @@ -252,10 +267,14 @@ jobs: run: composer i - name: Set up Nextcloud + env: + DB_PORT: 4444 + OC_PASS: test run: | mkdir data - ./occ maintenance:install --verbose --database=oci --database-name=XE --database-host=127.0.0.1 --database-port=1521 --database-user=autotest --database-pass=owncloud --admin-user admin --admin-pass admin + ./occ maintenance:install --verbose --database=oci --database-name=XE --database-host=127.0.0.1 --database-port=$DB_PORT --database-user=autotest --database-pass=owncloud --admin-user admin --admin-pass admin php -f index.php + ./occ user:add --password-from-env --display-name="Test Displayname" test ./occ app:enable --force ${{ env.APP_NAME }} - name: PHPUnit diff --git a/composer.json b/composer.json index 300813ff8..75c174924 100644 --- a/composer.json +++ b/composer.json @@ -21,7 +21,8 @@ "require-dev": { "bamarni/composer-bin-plugin": "^1.5", "christophwurst/nextcloud": "^23.0", - "phpunit/phpunit": "^9" + "phpunit/phpunit": "^9", + "guzzlehttp/guzzle": "^7.4" }, "require": { "league/csv": "^9.7.3" diff --git a/composer.lock b/composer.lock index 38d23258b..26caf24b8 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "cce56b3ebe3471b0b16b1dcf88865be4", + "content-hash": "62733788069011c2d49faa517001cf3a", "packages": [ { "name": "league/csv", @@ -255,6 +255,329 @@ ], "time": "2022-03-03T08:28:38+00:00" }, + { + "name": "guzzlehttp/guzzle", + "version": "7.4.2", + "source": { + "type": "git", + "url": "https://github.com/guzzle/guzzle.git", + "reference": "ac1ec1cd9b5624694c3a40be801d94137afb12b4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/guzzle/zipball/ac1ec1cd9b5624694c3a40be801d94137afb12b4", + "reference": "ac1ec1cd9b5624694c3a40be801d94137afb12b4", + "shasum": "" + }, + "require": { + "ext-json": "*", + "guzzlehttp/promises": "^1.5", + "guzzlehttp/psr7": "^1.8.3 || ^2.1", + "php": "^7.2.5 || ^8.0", + "psr/http-client": "^1.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "provide": { + "psr/http-client-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "ext-curl": "*", + "php-http/client-integration-tests": "^3.0", + "phpunit/phpunit": "^8.5.5 || ^9.3.5", + "psr/log": "^1.1 || ^2.0 || ^3.0" + }, + "suggest": { + "ext-curl": "Required for CURL handler support", + "ext-intl": "Required for Internationalized Domain Name (IDN) support", + "psr/log": "Required for using the Log middleware" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "7.4-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Jeremy Lindblom", + "email": "jeremeamia@gmail.com", + "homepage": "https://github.com/jeremeamia" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle is a PHP HTTP client library", + "keywords": [ + "client", + "curl", + "framework", + "http", + "http client", + "psr-18", + "psr-7", + "rest", + "web service" + ], + "support": { + "issues": "https://github.com/guzzle/guzzle/issues", + "source": "https://github.com/guzzle/guzzle/tree/7.4.2" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/guzzle", + "type": "tidelift" + } + ], + "time": "2022-03-20T14:16:28+00:00" + }, + { + "name": "guzzlehttp/promises", + "version": "1.5.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/promises.git", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/promises/zipball/fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "reference": "fe752aedc9fd8fcca3fe7ad05d419d32998a06da", + "shasum": "" + }, + "require": { + "php": ">=5.5" + }, + "require-dev": { + "symfony/phpunit-bridge": "^4.4 || ^5.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.5-dev" + } + }, + "autoload": { + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "GuzzleHttp\\Promise\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + } + ], + "description": "Guzzle promises library", + "keywords": [ + "promise" + ], + "support": { + "issues": "https://github.com/guzzle/promises/issues", + "source": "https://github.com/guzzle/promises/tree/1.5.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/promises", + "type": "tidelift" + } + ], + "time": "2021-10-22T20:56:57+00:00" + }, + { + "name": "guzzlehttp/psr7", + "version": "2.2.1", + "source": { + "type": "git", + "url": "https://github.com/guzzle/psr7.git", + "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/guzzle/psr7/zipball/c94a94f120803a18554c1805ef2e539f8285f9a2", + "reference": "c94a94f120803a18554c1805ef2e539f8285f9a2", + "shasum": "" + }, + "require": { + "php": "^7.2.5 || ^8.0", + "psr/http-factory": "^1.0", + "psr/http-message": "^1.0", + "ralouphie/getallheaders": "^3.0" + }, + "provide": { + "psr/http-factory-implementation": "1.0", + "psr/http-message-implementation": "1.0" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "http-interop/http-factory-tests": "^0.9", + "phpunit/phpunit": "^8.5.8 || ^9.3.10" + }, + "suggest": { + "laminas/laminas-httphandlerrunner": "Emit PSR-7 responses" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.2-dev" + } + }, + "autoload": { + "psr-4": { + "GuzzleHttp\\Psr7\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Graham Campbell", + "email": "hello@gjcampbell.co.uk", + "homepage": "https://github.com/GrahamCampbell" + }, + { + "name": "Michael Dowling", + "email": "mtdowling@gmail.com", + "homepage": "https://github.com/mtdowling" + }, + { + "name": "George Mponos", + "email": "gmponos@gmail.com", + "homepage": "https://github.com/gmponos" + }, + { + "name": "Tobias Nyholm", + "email": "tobias.nyholm@gmail.com", + "homepage": "https://github.com/Nyholm" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://github.com/sagikazarmark" + }, + { + "name": "Tobias Schultze", + "email": "webmaster@tubo-world.de", + "homepage": "https://github.com/Tobion" + }, + { + "name": "Márk Sági-Kazár", + "email": "mark.sagikazar@gmail.com", + "homepage": "https://sagikazarmark.hu" + } + ], + "description": "PSR-7 message implementation that also provides common utility methods", + "keywords": [ + "http", + "message", + "psr-7", + "request", + "response", + "stream", + "uri", + "url" + ], + "support": { + "issues": "https://github.com/guzzle/psr7/issues", + "source": "https://github.com/guzzle/psr7/tree/2.2.1" + }, + "funding": [ + { + "url": "https://github.com/GrahamCampbell", + "type": "github" + }, + { + "url": "https://github.com/Nyholm", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/guzzlehttp/psr7", + "type": "tidelift" + } + ], + "time": "2022-03-20T21:55:58+00:00" + }, { "name": "myclabs/deep-copy", "version": "1.11.0", @@ -1227,6 +1550,166 @@ }, "time": "2019-01-08T18:20:26+00:00" }, + { + "name": "psr/http-client", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-client.git", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-client/zipball/2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "reference": "2dfb5f6c5eff0e91e20e913f8c5452ed95b86621", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Client\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP clients", + "homepage": "https://github.com/php-fig/http-client", + "keywords": [ + "http", + "http-client", + "psr", + "psr-18" + ], + "support": { + "source": "https://github.com/php-fig/http-client/tree/master" + }, + "time": "2020-06-29T06:28:15+00:00" + }, + { + "name": "psr/http-factory", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-factory.git", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-factory/zipball/12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "reference": "12ac7fcd07e5b077433f5f2bee95b3a771bf61be", + "shasum": "" + }, + "require": { + "php": ">=7.0.0", + "psr/http-message": "^1.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interfaces for PSR-7 HTTP message factories", + "keywords": [ + "factory", + "http", + "message", + "psr", + "psr-17", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-factory/tree/master" + }, + "time": "2019-04-30T12:38:16+00:00" + }, + { + "name": "psr/http-message", + "version": "1.0.1", + "source": { + "type": "git", + "url": "https://github.com/php-fig/http-message.git", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/http-message/zipball/f6561bf28d520154e4b0ec72be95418abe6d9363", + "reference": "f6561bf28d520154e4b0ec72be95418abe6d9363", + "shasum": "" + }, + "require": { + "php": ">=5.3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Http\\Message\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Common interface for HTTP messages", + "homepage": "https://github.com/php-fig/http-message", + "keywords": [ + "http", + "http-message", + "psr", + "psr-7", + "request", + "response" + ], + "support": { + "source": "https://github.com/php-fig/http-message/tree/master" + }, + "time": "2016-08-06T14:39:51+00:00" + }, { "name": "psr/log", "version": "1.1.4", @@ -1277,6 +1760,50 @@ }, "time": "2021-05-03T11:20:27+00:00" }, + { + "name": "ralouphie/getallheaders", + "version": "3.0.3", + "source": { + "type": "git", + "url": "https://github.com/ralouphie/getallheaders.git", + "reference": "120b605dfeb996808c31b6477290a714d356e822" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ralouphie/getallheaders/zipball/120b605dfeb996808c31b6477290a714d356e822", + "reference": "120b605dfeb996808c31b6477290a714d356e822", + "shasum": "" + }, + "require": { + "php": ">=5.6" + }, + "require-dev": { + "php-coveralls/php-coveralls": "^2.1", + "phpunit/phpunit": "^5 || ^6.5" + }, + "type": "library", + "autoload": { + "files": [ + "src/getallheaders.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ralph Khattar", + "email": "ralph.khattar@gmail.com" + } + ], + "description": "A polyfill for getallheaders.", + "support": { + "issues": "https://github.com/ralouphie/getallheaders/issues", + "source": "https://github.com/ralouphie/getallheaders/tree/develop" + }, + "time": "2019-03-08T08:55:37+00:00" + }, { "name": "sebastian/cli-parser", "version": "1.0.1", @@ -2241,6 +2768,73 @@ ], "time": "2020-09-28T06:39:44+00:00" }, + { + "name": "symfony/deprecation-contracts", + "version": "v2.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "reference": "e8b495ea28c1d97b5e0c121748d6f9b53d075c66", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v2.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-01-02T09:53:40+00:00" + }, { "name": "symfony/polyfill-ctype", "version": "v1.25.0", @@ -2442,5 +3036,5 @@ "platform-overrides": { "php": "7.3" }, - "plugin-api-version": "2.3.0" + "plugin-api-version": "2.0.0" } diff --git a/tests/Integration/Api/ApiV2Test.php b/tests/Integration/Api/ApiV2Test.php new file mode 100644 index 000000000..2edccbe9c --- /dev/null +++ b/tests/Integration/Api/ApiV2Test.php @@ -0,0 +1,1219 @@ + + * + * @author Jonas Rittershofer + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +namespace OCA\Forms\Tests\Integration\Api; + +use OCA\Forms\Db\Form; +use OCA\Forms\Db\FormMapper; + +use OCP\DB\QueryBuilder\IQueryBuilder; + +use GuzzleHttp\Client; +use GuzzleHttp\Exception\ClientException; +use Test\TestCase; + +class ApiV2Test extends TestCase { + /** @var GuzzleHttp\Client */ + private $http; + + /** @var FormMapper */ + private $formMapper; + + /** @var Array */ + private $testForms = [ + [ + 'hash' => 'abcdefg', + 'title' => 'Title of a Form', + 'description' => 'Just a simple form.', + 'owner_id' => 'test', + 'access_json' => [ + 'permitAllUsers' => false, + 'showToAllUsers' => false + ], + 'created' => 12345, + 'expires' => 0, + 'is_anonymous' => false, + 'submit_once' => true, + 'questions' => [ + [ + 'type' => 'short', + 'text' => 'First Question?', + 'isRequired' => true, + 'order' => 1, + 'options' => [] + ], + [ + 'type' => 'multiple_unique', + 'text' => 'Second Question?', + 'isRequired' => false, + 'order' => 2, + 'options' => [ + [ + 'text' => 'Option 1' + ], + [ + 'text' => 'Option 2' + ] + ] + ] + ], + 'shares' => [ + [ + 'shareType' => 0, + 'shareWith' => 'user1', + ], + [ + 'shareType' => 3, + 'shareWith' => 'shareHash', + ], + ], + 'submissions' => [ + [ + 'userId' => 'user1', + 'timestamp' => 123456, + 'answers' => [ + [ + 'questionIndex' => 0, + 'text' => 'This is a short answer.' + ], + [ + 'questionIndex' => 1, + 'text' => 'Option 1' + ] + ] + ], + [ + 'userId' => 'user2', + 'timestamp' => 12345, + 'answers' => [ + [ + 'questionIndex' => 0, + 'text' => 'This is another short answer.' + ], + [ + 'questionIndex' => 1, + 'text' => 'Option 2' + ] + ] + ] + ] + ], + [ + 'hash' => 'abcdefghij', + 'title' => 'Title of a second Form', + 'description' => '', + 'owner_id' => 'someUser', + 'access_json' => [ + 'permitAllUsers' => true, + 'showToAllUsers' => true + ], + 'created' => 12345, + 'expires' => 0, + 'is_anonymous' => false, + 'submit_once' => true, + 'questions' => [ + [ + 'type' => 'short', + 'text' => 'Third Question?', + 'isRequired' => false, + 'order' => 1, + 'options' => [] + ], + ], + 'shares' => [ + [ + 'shareType' => 0, + 'shareWith' => 'user2', + ], + ], + 'submissions' => [] + ] + ]; + + /** + * Set up test environment. + * Writing testforms into db, preparing http request + */ + public function setUp(): void { + parent::setUp(); + + $qb = TestCase::$realDatabase->getQueryBuilder(); + + // Write our test forms into db + foreach ($this->testForms as $index => $form) { + $qb->insert('forms_v2_forms') + ->values([ + 'hash' => $qb->createNamedParameter($form['hash'], IQueryBuilder::PARAM_STR), + 'title' => $qb->createNamedParameter($form['title'], IQueryBuilder::PARAM_STR), + 'description' => $qb->createNamedParameter($form['description'], IQueryBuilder::PARAM_STR), + 'owner_id' => $qb->createNamedParameter($form['owner_id'], IQueryBuilder::PARAM_STR), + 'access_json' => $qb->createNamedParameter(json_encode($form['access_json']), IQueryBuilder::PARAM_STR), + 'created' => $qb->createNamedParameter($form['created'], IQueryBuilder::PARAM_INT), + 'expires' => $qb->createNamedParameter($form['expires'], IQueryBuilder::PARAM_INT), + 'is_anonymous' => $qb->createNamedParameter($form['is_anonymous'], IQueryBuilder::PARAM_BOOL), + 'submit_once' => $qb->createNamedParameter($form['submit_once'], IQueryBuilder::PARAM_BOOL) + ]); + $qb->execute(); + $formId = $qb->getLastInsertId(); + $this->testForms[$index]['id'] = $formId; + + // Insert Questions into DB + foreach ($form['questions'] as $qIndex => $question) { + $qb->insert('forms_v2_questions') + ->values([ + 'form_id' => $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT), + 'order' => $qb->createNamedParameter($question['order'], IQueryBuilder::PARAM_INT), + 'type' => $qb->createNamedParameter($question['type'], IQueryBuilder::PARAM_STR), + 'is_required' => $qb->createNamedParameter($question['isRequired'], IQueryBuilder::PARAM_BOOL), + 'text' => $qb->createNamedParameter($question['text'], IQueryBuilder::PARAM_STR) + ]); + $qb->execute(); + $questionId = $qb->getLastInsertId(); + $this->testForms[$index]['questions'][$qIndex]['id'] = $questionId; + + // Insert Options into DB + foreach ($question['options'] as $oIndex => $option) { + $qb->insert('forms_v2_options') + ->values([ + 'question_id' => $qb->createNamedParameter($questionId, IQueryBuilder::PARAM_INT), + 'text' => $qb->createNamedParameter($option['text'], IQueryBuilder::PARAM_STR) + ]); + $qb->execute(); + $this->testForms[$index]['questions'][$qIndex]['options'][$oIndex]['id'] = $qb->getLastInsertId(); + } + } + + // Insert Shares into DB + foreach ($form['shares'] as $sIndex => $share) { + $qb->insert('forms_v2_shares') + ->values([ + 'form_id' => $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT), + 'share_type' => $qb->createNamedParameter($share['shareType'], IQueryBuilder::PARAM_STR), + 'share_with' => $qb->createNamedParameter($share['shareWith'], IQueryBuilder::PARAM_STR) + ]); + $qb->execute(); + $this->testForms[$index]['shares'][$sIndex]['id'] = $qb->getLastInsertId(); + } + + // Insert Submissions into DB + foreach ($form['submissions'] as $suIndex => $submission) { + $qb->insert('forms_v2_submissions') + ->values([ + 'form_id' => $qb->createNamedParameter($formId, IQueryBuilder::PARAM_INT), + 'user_id' => $qb->createNamedParameter($submission['userId'], IQueryBuilder::PARAM_STR), + 'timestamp' => $qb->createNamedParameter($submission['timestamp'], IQueryBuilder::PARAM_INT) + ]); + $qb->execute(); + $submissionId = $qb->getLastInsertId(); + $this->testForms[$index]['submissions'][$suIndex]['id'] = $submissionId; + + foreach ($submission['answers'] as $aIndex => $answer) { + $qb->insert('forms_v2_answers') + ->values([ + 'submission_id' => $qb->createNamedParameter($submissionId, IQueryBuilder::PARAM_INT), + 'question_id' => $qb->createNamedParameter($this->testForms[$index]['questions'][$answer['questionIndex']]['id'], IQueryBuilder::PARAM_INT), + 'text' => $qb->createNamedParameter($answer['text'], IQueryBuilder::PARAM_STR) + ]); + $qb->execute(); + $this->testForms[$index]['submissions'][$suIndex]['answers'][$aIndex]['id'] = $qb->getLastInsertId(); + } + } + } + + // Set up http Client + $this->http = new Client([ + 'base_uri' => 'http://localhost:8080/ocs/v2.php/apps/forms/', + 'auth' => ['test', 'test'], + 'headers' => [ + 'OCS-ApiRequest' => 'true', + 'Accept' => 'application/json' + ], + ]); + } + + /** Clean up database from testforms */ + public function tearDown(): void { + $qb = TestCase::$realDatabase->getQueryBuilder(); + + foreach ($this->testForms as $form) { + $qb->delete('forms_v2_forms') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($form['id'], IQueryBuilder::PARAM_INT))); + $qb->execute(); + + foreach ($form['questions'] as $question) { + $qb->delete('forms_v2_questions') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($question['id'], IQueryBuilder::PARAM_INT))); + $qb->execute(); + + foreach ($question['options'] as $option) { + $qb->delete('forms_v2_options') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($option['id'], IQueryBuilder::PARAM_INT))); + $qb->execute(); + } + } + + foreach ($form['shares'] as $share) { + $qb->delete('forms_v2_shares') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($share['id'], IQueryBuilder::PARAM_INT))); + $qb->execute(); + } + + if (isset($form['submissions'])) { + foreach ($form['submissions'] as $submission) { + $qb->delete('forms_v2_submissions') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($submission['id'], IQueryBuilder::PARAM_INT))); + $qb->execute(); + + foreach ($submission['answers'] as $answer) { + $qb->delete('forms_v2_answers') + ->where($qb->expr()->eq('id', $qb->createNamedParameter($answer['id'], IQueryBuilder::PARAM_INT))); + $qb->execute(); + } + } + } + } + + parent::tearDown(); + } + + // Small Wrapper for OCS-Response + private function OcsResponse2Data($resp) { + $arr = json_decode($resp->getBody()->getContents(), true); + return $arr['ocs']['data']; + } + + // Unset Id, as we can not control it on the tests. + private function arrayUnsetId(array $arr): array { + foreach ($arr as $index => $elem) { + unset($arr[$index]['id']); + } + return $arr; + } + + public function dataGetForms() { + return [ + 'getTestforms' => [ + 'expected' => [[ + 'hash' => 'abcdefg', + 'title' => 'Title of a Form', + 'expires' => 0, + 'permissions' => [ + 'edit', + 'results', + 'submit' + ], + 'partial' => true + ]] + ] + ]; + } + /** + * @dataProvider dataGetForms + * + * @param array $expected + */ + public function testGetForms(array $expected): void { + $resp = $this->http->request('GET', 'api/v2/forms'); + + $data = $this->OcsResponse2Data($resp); + $data = $this->arrayUnsetId($data); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($expected, $data); + } + + public function dataGetSharedForms() { + return [ + 'getTestforms' => [ + 'expected' => [[ + 'hash' => 'abcdefghij', + 'title' => 'Title of a second Form', + 'expires' => 0, + 'permissions' => [ + 'submit' + ], + 'partial' => true + ]] + ] + ]; + } + /** + * @dataProvider dataGetSharedForms + * + * @param array $expected + */ + public function testGetSharedForms(array $expected): void { + $resp = $this->http->request('GET', 'api/v2/shared_forms'); + + $data = $this->OcsResponse2Data($resp); + $data = $this->arrayUnsetId($data); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($expected, $data); + } + + public function dataGetPartialForm() { + return [ + 'getPartialForm' => [ + 'expected' => [ + 'hash' => 'abcdefghij', + 'title' => 'Title of a second Form', + 'expires' => 0, + 'permissions' => [ + 'submit' + ], + 'partial' => true + ] + ] + ]; + } + /** + * @dataProvider dataGetPartialForm + * + * @param array $expected + */ + public function testGetPartialForm(array $expected): void { + $resp = $this->http->request('GET', "api/v2/partial_form/{$this->testForms[1]['hash']}"); + + $data = $this->OcsResponse2Data($resp); + unset($data['id']); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($expected, $data); + } + + public function dataGetNewForm() { + return [ + 'getNewForm' => [ + 'expected' => [ + // 'hash' => Some random, cannot be checked. + 'title' => '', + 'description' => '', + 'ownerId' => 'test', + // 'created' => Hard to check exactly. + 'access' => [ + 'permitAllUsers' => false, + 'showToAllUsers' => false + ], + 'expires' => 0, + 'isAnonymous' => false, + 'submitOnce' => true, + 'canSubmit' => true, + 'permissions' => [ + 'edit', + 'results', + 'submit' + ], + 'questions' => [], + 'shares' => [], + ] + ] + ]; + } + /** + * @dataProvider dataGetNewForm + * + * @param array $expected + */ + public function testGetNewForm(array $expected): void { + $resp = $this->http->request('POST', 'api/v2/form'); + $data = $this->OcsResponse2Data($resp); + + // Store for deletion on tearDown + $this->testForms[] = $data; + + // Cannot control id + unset($data['id']); + // Check general behaviour of hash + $this->assertMatchesRegularExpression('/^[a-zA-Z0-9]{16}$/', $data['hash']); + unset($data['hash']); + // Check general behaviour of created (Created in the last 10 seconds) + $this->assertTrue(time() - $data['created'] < 10); + unset($data['created']); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($expected, $data); + } + + public function dataGetFullForm() { + return [ + 'getFullForm' => [ + 'expected' => [ + 'hash' => 'abcdefg', + 'title' => 'Title of a Form', + 'description' => 'Just a simple form.', + 'ownerId' => 'test', + 'created' => 12345, + 'access' => [ + 'permitAllUsers' => false, + 'showToAllUsers' => false + ], + 'expires' => 0, + 'isAnonymous' => false, + 'submitOnce' => true, + 'canSubmit' => true, + 'permissions' => [ + 'edit', + 'results', + 'submit' + ], + 'questions' => [ + [ + 'type' => 'short', + 'text' => 'First Question?', + 'isRequired' => true, + 'order' => 1, + 'options' => [] + ], + [ + 'type' => 'multiple_unique', + 'text' => 'Second Question?', + 'isRequired' => false, + 'order' => 2, + 'options' => [ + [ + 'text' => 'Option 1' + ], + [ + 'text' => 'Option 2' + ] + ] + ] + ], + 'shares' => [ + [ + 'shareType' => 0, + 'shareWith' => 'user1', + 'displayName' => '' + ], + [ + 'shareType' => 3, + 'shareWith' => 'shareHash', + 'displayName' => '' + ], + ], + ] + ] + ]; + } + /** + * @dataProvider dataGetFullForm + * + * @param array $expected + */ + public function testGetFullForm(array $expected): void { + $resp = $this->http->request('GET', "api/v2/form/{$this->testForms[0]['id']}"); + $data = $this->OcsResponse2Data($resp); + + // Cannot control ids, but check general consistency. + foreach ($data['questions'] as $qIndex => $question) { + $this->assertEquals($data['id'], $question['formId']); + unset($data['questions'][$qIndex]['formId']); + + foreach ($question['options'] as $oIndex => $option) { + $this->assertEquals($question['id'], $option['questionId']); + unset($data['questions'][$qIndex]['options'][$oIndex]['questionId']); + unset($data['questions'][$qIndex]['options'][$oIndex]['id']); + } + unset($data['questions'][$qIndex]['id']); + } + foreach ($data['shares'] as $sIndex => $share) { + $this->assertEquals($data['id'], $share['formId']); + unset($data['shares'][$sIndex]['formId']); + unset($data['shares'][$sIndex]['id']); + } + unset($data['id']); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($expected, $data); + } + + public function dataCloneForm() { + $fullFormExpected = $this->dataGetFullForm()['getFullForm']['expected']; + // Compared to full form expected, update changed properties + $fullFormExpected['title'] = 'Title of a Form - Copy'; + $fullFormExpected['shares'] = []; + // Compared to full form expected, unset unpredictable properties. These will be checked logically. + unset($fullFormExpected['id']); + unset($fullFormExpected['hash']); + unset($fullFormExpected['created']); + foreach ($fullFormExpected['questions'] as $qIndex => $question) { + unset($fullFormExpected['questions'][$qIndex]['formId']); + } + + return [ + 'updateFormProps' => [ + 'expected' => $fullFormExpected + ] + ]; + } + /** + * @dataProvider dataCloneForm + * + * @param array $expected + */ + public function testCloneForm(array $expected): void { + $resp = $this->http->request('POST', "api/v2/form/clone/{$this->testForms[0]['id']}"); + $data = $this->OcsResponse2Data($resp); + + // Store for deletion on tearDown + $this->testForms[] = $data; + + // Cannot control ids, but check general consistency. + foreach ($data['questions'] as $qIndex => $question) { + $this->assertEquals($data['id'], $question['formId']); + unset($data['questions'][$qIndex]['formId']); + + foreach ($question['options'] as $oIndex => $option) { + $this->assertEquals($question['id'], $option['questionId']); + unset($data['questions'][$qIndex]['options'][$oIndex]['questionId']); + unset($data['questions'][$qIndex]['options'][$oIndex]['id']); + } + unset($data['questions'][$qIndex]['id']); + } + foreach ($data['shares'] as $sIndex => $share) { + $this->assertEquals($data['id'], $share['formId']); + unset($data['shares'][$sIndex]['formId']); + unset($data['shares'][$sIndex]['id']); + } + // Check not just returning source-form (id must differ). + $this->assertGreaterThan($this->testForms[0]['id'], $data['id']); + unset($data['id']); + + // Check general behaviour of hash + $this->assertMatchesRegularExpression('/^[a-zA-Z0-9]{16}$/', $data['hash']); + unset($data['hash']); + // Check general behaviour of created (Created in the last 10 seconds) + $this->assertTrue(time() - $data['created'] < 10); + unset($data['created']); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($expected, $data); + } + + public function dataUpdateFormProperties() { + $fullFormExpected = $this->dataGetFullForm()['getFullForm']['expected']; + $fullFormExpected['title'] = 'This is my NEW Title!'; + $fullFormExpected['access'] = [ + 'permitAllUsers' => true, + 'showToAllUsers' => true + ]; + return [ + 'updateFormProps' => [ + 'expected' => $fullFormExpected + ] + ]; + } + /** + * @dataProvider dataUpdateFormProperties + * + * @param array $expected + */ + public function testUpdateFormProperties(array $expected): void { + $resp = $this->http->request('POST', 'api/v2/form/update', [ + 'json' => [ + 'id' => $this->testForms[0]['id'], + 'keyValuePairs' => [ + 'title' => 'This is my NEW Title!', + 'access' => [ + 'permitAllUsers' => true, + 'showToAllUsers' => true + ] + ] + ] + ]); + $data = $this->OcsResponse2Data($resp); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($this->testForms[0]['id'], $data); + + // Check if form equals updated form. + $this->testGetFullForm($expected); + } + + public function testDeleteForm() { + $resp = $this->http->request('DELETE', "api/v2/form/{$this->testForms[0]['id']}"); + $data = $this->OcsResponse2Data($resp); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($this->testForms[0]['id'], $data); + + // Check if not existent anymore. + try { + $this->http->request('GET', "api/v2/form/{$this->testForms[0]['id']}"); + } catch (ClientException $e) { + $resp = $e->getResponse(); + } + $this->assertEquals(400, $resp->getStatusCode()); + } + + public function dataCreateNewQuestion() { + return [ + 'newQuestion' => [ + 'expected' => [ + // 'formId' => 3, // Checked during test + // 'order' => 3, // Checked during test + 'type' => 'short', + 'isRequired' => false, + 'text' => 'Already some Question?', + 'options' => [] + ] + ] + ]; + } + /** + * @dataProvider dataCreateNewQuestion + * + * @param array $expected + */ + public function testCreateNewQuestion(array $expected): void { + $resp = $this->http->request('POST', 'api/v2/question', [ + 'json' => [ + 'formId' => $this->testForms[0]['id'], + 'type' => 'short', + 'text' => 'Already some Question?' + ] + ]); + $data = $this->OcsResponse2Data($resp); + + // Store for deletion on tearDown + $this->testForms[0]['questions'][] = $data; + + // Check formId & order + $this->assertEquals($this->testForms[0]['id'], $data['formId']); + unset($data['formId']); + $this->assertEquals(sizeof($this->testForms[0]['questions']), $data['order']); + unset($data['order']); + unset($data['id']); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($expected, $data); + } + + public function dataUpdateQuestionProperties() { + $fullFormExpected = $this->dataGetFullForm()['getFullForm']['expected']; + $fullFormExpected['questions'][0]['text'] = 'Still first Question!'; + $fullFormExpected['questions'][0]['isRequired'] = false; + + return [ + 'updateQuestionProps' => [ + 'fullFormExpected' => $fullFormExpected + ] + ]; + } + /** + * @dataProvider dataUpdateQuestionProperties + * + * @param array $fullFormExpected + */ + public function testUpdateQuestionProperties(array $fullFormExpected): void { + $resp = $this->http->request('POST', 'api/v2/question/update', [ + 'json' => [ + 'id' => $this->testForms[0]['questions'][0]['id'], + 'keyValuePairs' => [ + 'isRequired' => false, + 'text' => 'Still first Question!' + ] + ] + ]); + $data = $this->OcsResponse2Data($resp); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($this->testForms[0]['questions'][0]['id'], $data); + + // Check if form equals updated form. + $this->testGetFullForm($fullFormExpected); + } + + public function dataReorderQuestions() { + $fullFormExpected = $this->dataGetFullForm()['getFullForm']['expected']; + $fullFormExpected['questions'][0]['order'] = 2; + $fullFormExpected['questions'][1]['order'] = 1; + + // Exchange questions, as they will be returned in new order. + $tmp = $fullFormExpected['questions'][0]; + $fullFormExpected['questions'][0] = $fullFormExpected['questions'][1]; + $fullFormExpected['questions'][1] = $tmp; + + return [ + 'updateQuestionProps' => [ + 'fullFormExpected' => $fullFormExpected + ] + ]; + } + /** + * @dataProvider dataReorderQuestions + * + * @param array $fullFormExpected + */ + public function testReorderQuestions(array $fullFormExpected): void { + $resp = $this->http->request('POST', 'api/v2/question/reorder', [ + 'json' => [ + 'formId' => $this->testForms[0]['id'], + 'newOrder' => [ + $this->testForms[0]['questions'][1]['id'], + $this->testForms[0]['questions'][0]['id'] + ] + ] + ]); + $data = $this->OcsResponse2Data($resp); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals([ + $this->testForms[0]['questions'][0]['id'] => [ 'order' => 2 ], + $this->testForms[0]['questions'][1]['id'] => [ 'order' => 1 ] + ], $data); + + // Check if form equals updated form. + $this->testGetFullForm($fullFormExpected); + } + + public function dataDeleteQuestion() { + $fullFormExpected = $this->dataGetFullForm()['getFullForm']['expected']; + array_splice($fullFormExpected['questions'], 0, 1); + $fullFormExpected['questions'][0]['order'] = 1; + + return [ + 'deleteQuestion' => [ + 'fullFormExpected' => $fullFormExpected + ] + ]; + } + /** + * @dataProvider dataDeleteQuestion + * + * @param array $fullFormExpected + */ + public function testDeleteQuestion(array $fullFormExpected) { + $resp = $this->http->request('DELETE', "api/v2/question/{$this->testForms[0]['questions'][0]['id']}"); + $data = $this->OcsResponse2Data($resp); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($this->testForms[0]['questions'][0]['id'], $data); + + $this->testGetFullForm($fullFormExpected); + } + + public function dataCreateNewOption() { + return [ + 'newOption' => [ + 'expected' => [ + // 'questionId' => Done dynamically below. + 'text' => 'A new Option.' + ] + ] + ]; + } + /** + * @dataProvider dataCreateNewOption + * + * @param array $expected + */ + public function testCreateNewOption(array $expected): void { + $resp = $this->http->request('POST', 'api/v2/option', [ + 'json' => [ + 'questionId' => $this->testForms[0]['questions'][1]['id'], + 'text' => 'A new Option.' + ] + ]); + $data = $this->OcsResponse2Data($resp); + + // Store for deletion on tearDown + $this->testForms[0]['questions'][1]['options'][] = $data; + + // Check questionId + $this->assertEquals($this->testForms[0]['questions'][1]['id'], $data['questionId']); + unset($data['questionId']); + unset($data['id']); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($expected, $data); + } + + public function dataUpdateOptionProperties() { + $fullFormExpected = $this->dataGetFullForm()['getFullForm']['expected']; + $fullFormExpected['questions'][1]['options'][0]['text'] = 'New option Text.'; + + return [ + 'updateOptionProps' => [ + 'fullFormExpected' => $fullFormExpected + ] + ]; + } + /** + * @dataProvider dataUpdateOptionProperties + * + * @param array $fullFormExpected + */ + public function testUpdateOptionProperties(array $fullFormExpected): void { + $resp = $this->http->request('POST', 'api/v2/option/update', [ + 'json' => [ + 'id' => $this->testForms[0]['questions'][1]['options'][0]['id'], + 'keyValuePairs' => [ + 'text' => 'New option Text.' + ] + ] + ]); + $data = $this->OcsResponse2Data($resp); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($this->testForms[0]['questions'][1]['options'][0]['id'], $data); + + // Check if form equals updated form. + $this->testGetFullForm($fullFormExpected); + } + + public function dataDeleteOption() { + $fullFormExpected = $this->dataGetFullForm()['getFullForm']['expected']; + array_splice($fullFormExpected['questions'][1]['options'], 0, 1); + + return [ + 'deleteOption' => [ + 'fullFormExpected' => $fullFormExpected + ] + ]; + } + /** + * @dataProvider dataDeleteOption + * + * @param array $fullFormExpected + */ + public function testDeleteOption(array $fullFormExpected) { + $resp = $this->http->request('DELETE', "api/v2/option/{$this->testForms[0]['questions'][1]['options'][0]['id']}"); + $data = $this->OcsResponse2Data($resp); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($this->testForms[0]['questions'][1]['options'][0]['id'], $data); + + $this->testGetFullForm($fullFormExpected); + } + + public function dataAddShare() { + return [ + 'addAShare' => [ + 'expected' => [ + // 'formId' => Checked dynamically + 'shareType' => 0, + 'shareWith' => 'test', + 'displayName' => 'Test Displayname' + ] + ] + ]; + } + /** + * @dataProvider dataAddShare + * + * @param array $expected + */ + public function testAddShare(array $expected) { + $resp = $this->http->request('POST', 'api/v2/share', [ + 'json' => [ + 'formId' => $this->testForms[0]['id'], + 'shareType' => 0, + 'shareWith' => 'test' + ] + ]); + $data = $this->OcsResponse2Data($resp); + + // Store for cleanup + $this->testForms[0]['shares'][] = $data; + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($this->testForms[0]['id'], $data['formId']); + unset($data['formId']); + unset($data['id']); + $this->assertEquals($expected, $data); + } + + public function dataDeleteShare() { + $fullFormExpected = $this->dataGetFullForm()['getFullForm']['expected']; + array_splice($fullFormExpected['shares'], 0, 1); + + return [ + 'deleteShare' => [ + 'fullFormExpected' => $fullFormExpected + ] + ]; + } + /** + * @dataProvider dataDeleteShare + * + * @param array $fullFormExpected + */ + public function testDeleteShare(array $fullFormExpected) { + $resp = $this->http->request('DELETE', "api/v2/share/{$this->testForms[0]['shares'][0]['id']}"); + $data = $this->OcsResponse2Data($resp); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($this->testForms[0]['shares'][0]['id'], $data); + + $this->testGetFullForm($fullFormExpected); + } + + public function dataGetSubmissions() { + return [ + 'getSubmissions' => [ + 'expected' => [ + 'submissions' => [ + [ + // 'formId' => Checked dynamically + 'userId' => 'user1', + 'userDisplayName' => 'user1', + 'timestamp' => 123456, + 'answers' => [ + [ + // 'submissionId' => Checked dynamically + // 'questionId' => Checked dynamically + 'text' => 'This is a short answer.' + ], + [ + // 'submissionId' => Checked dynamically + // 'questionId' => Checked dynamically + 'text' => 'Option 1' + ] + ] + ], + [ + // 'formId' => Checked dynamically + 'userId' => 'user2', + 'userDisplayName' => 'user2', + 'timestamp' => 12345, + 'answers' => [ + [ + // 'submissionId' => Checked dynamically + // 'questionId' => Checked dynamically + 'text' => 'This is another short answer.' + ], + [ + // 'submissionId' => Checked dynamically + // 'questionId' => Checked dynamically + 'text' => 'Option 2' + ] + ] + ] + ], + 'questions' => $this->dataGetFullForm()['getFullForm']['expected']['questions'] + ] + ] + ]; + } + /** + * @dataProvider dataGetSubmissions + * + * @param array $expected + */ + public function testGetSubmissions(array $expected) { + $resp = $this->http->request('GET', "api/v2/submissions/{$this->testForms[0]['hash']}"); + $data = $this->OcsResponse2Data($resp); + + // Cannot control ids, but check general consistency. + foreach ($data['submissions'] as $sIndex => $submission) { + $this->assertEquals($this->testForms[0]['id'], $submission['formId']); + unset($data['submissions'][$sIndex]['formId']); + + foreach ($submission['answers'] as $aIndex => $answer) { + $this->assertEquals($submission['id'], $answer['submissionId']); + $this->assertEquals($this->testForms[0]['questions'][ + $this->testForms[0]['submissions'][$sIndex]['answers'][$aIndex]['questionIndex'] + ]['id'], $answer['questionId']); + unset($data['submissions'][$sIndex]['answers'][$aIndex]['submissionId']); + unset($data['submissions'][$sIndex]['answers'][$aIndex]['questionId']); + unset($data['submissions'][$sIndex]['answers'][$aIndex]['id']); + } + unset($data['submissions'][$sIndex]['id']); + } + foreach ($data['questions'] as $qIndex => $question) { + $this->assertEquals($this->testForms[0]['id'], $question['formId']); + unset($data['questions'][$qIndex]['formId']); + + foreach ($question['options'] as $oIndex => $option) { + $this->assertEquals($question['id'], $option['questionId']); + unset($data['questions'][$qIndex]['options'][$oIndex]['questionId']); + unset($data['questions'][$qIndex]['options'][$oIndex]['id']); + } + unset($data['questions'][$qIndex]['id']); + } + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($expected, $data); + } + + public function dataExportSubmissions() { + return [ + 'exportSubmissions' => [ + 'expected' => ' + "User display name","Timestamp","First Question?","Second Question?" + "user1","Friday, January 2, 1970 at 10:17:36 AM GMT+0:00","This is a short answer.","Option 1" + "user2","Thursday, January 1, 1970 at 3:25:45 AM GMT+0:00","This is another short answer.","Option 2"' + ] + ]; + } + /** + * @dataProvider dataExportSubmissions + * + * @param array $expected + */ + public function testExportSubmissions(string $expected) { + $resp = $this->http->request('GET', "api/v2/submissions/export/{$this->testForms[0]['hash']}"); + $data = substr($resp->getBody()->getContents(), 3); // Some strange Character removed at the beginning + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals('attachment; filename="Title of a Form (responses).csv"', $resp->getHeaders()['Content-Disposition'][0]); + $this->assertEquals('text/csv;charset=UTF-8', $resp->getHeaders()['Content-type'][0]); + $arr_txt_expected = preg_split('/,/', str_replace(["\t", "\n"], '', $expected)); + $arr_txt_data = preg_split('/,/', str_replace(["\t", "\n"], '', $data)); + $this->assertEquals($arr_txt_expected, $arr_txt_data); + } + + public function testExportToCloud() { + $resp = $this->http->request('POST', 'api/v2/submissions/export', [ + 'json' => [ + 'hash' => $this->testForms[0]['hash'], + 'path' => '' + ]] + ); + $data = $this->OcsResponse2Data($resp); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals('Title of a Form (responses).csv', $data); + } + + public function dataDeleteSubmissions() { + $submissionsExpected = $this->dataGetSubmissions()['getSubmissions']['expected']; + $submissionsExpected['submissions'] = []; + + return [ + 'deleteSubmissions' => [ + 'submissionsExpected' => $submissionsExpected + ] + ]; + } + /** + * @dataProvider dataDeleteSubmissions + * + * @param array $submissionsExpected + */ + public function testDeleteSubmissions(array $submissionsExpected) { + $resp = $this->http->request('DELETE', "api/v2/submissions/{$this->testForms[0]['id']}"); + $data = $this->OcsResponse2Data($resp); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($this->testForms[0]['id'], $data); + + $this->testGetSubmissions($submissionsExpected); + } + + public function dataInsertSubmission() { + $submissionsExpected = $this->dataGetSubmissions()['getSubmissions']['expected']; + $submissionsExpected['submissions'][] = [ + 'userId' => 'test' + ]; + + return [ + 'insertSubmission' => [ + 'submissionsExpected' => $submissionsExpected + ] + ]; + } + /** + * @dataProvider dataInsertSubmission + * + * @param array $submissionsExpected + */ + public function testInsertSubmission(array $submissionsExpected) { + $resp = $this->http->request('POST', 'api/v2/submission/insert', [ + 'json' => [ + 'formId' => $this->testForms[0]['id'], + 'answers' => [ + $this->testForms[0]['questions'][0]['id'] => ['ShortAnswer!'], + $this->testForms[0]['questions'][1]['id'] => [ + $this->testForms[0]['questions'][1]['options'][0]['id'] + ] + ] + ] + ]); + $data = $this->OcsResponse2Data($resp); + + $this->assertEquals(200, $resp->getStatusCode()); + + // Check stored submissions + $resp = $this->http->request('GET', "api/v2/submissions/{$this->testForms[0]['hash']}"); + $data = $this->OcsResponse2Data($resp); + + // Store for deletion + $this->testForms[0]['submissions'][] = $data['submissions'][0]; + + // Check Ids + foreach ($data['submissions'][0]['answers'] as $aIndex => $answer) { + $this->assertEquals($data['submissions'][0]['id'], $answer['submissionId']); + unset($data['submissions'][0]['answers'][$aIndex]['id']); + unset($data['submissions'][0]['answers'][$aIndex]['submissionId']); + } + unset($data['submissions'][0]['id']); + // Check general behaviour of timestamp (Insert in the last 10 seconds) + $this->assertTrue(time() - $data['submissions'][0]['timestamp'] < 10); + unset($data['submissions'][0]['timestamp']); + + $this->assertEquals([ + 'userId' => 'test', + 'userDisplayName' => 'Test Displayname', + 'formId' => $this->testForms[0]['id'], + 'answers' => [ + [ + 'questionId' => $this->testForms[0]['questions'][0]['id'], + 'text' => 'ShortAnswer!' + ], + [ + 'questionId' => $this->testForms[0]['questions'][1]['id'], + 'text' => 'Option 1' + ] + ] + ], $data['submissions'][0]); + } + + public function dataDeleteSingleSubmission() { + $submissionsExpected = $this->dataGetSubmissions()['getSubmissions']['expected']; + array_splice($submissionsExpected['submissions'], 0, 1); + + return [ + 'deleteSingleSubmission' => [ + 'submissionsExpected' => $submissionsExpected + ] + ]; + } + /** + * @dataProvider dataDeleteSingleSubmission + * + * @param array $submissionsExpected + */ + public function testDeleteSingleSubmission(array $submissionsExpected) { + $resp = $this->http->request('DELETE', "api/v2/submission/{$this->testForms[0]['submissions'][0]['id']}"); + $data = $this->OcsResponse2Data($resp); + + $this->assertEquals(200, $resp->getStatusCode()); + $this->assertEquals($this->testForms[0]['submissions'][0]['id'], $data); + + $this->testGetSubmissions($submissionsExpected); + } +};