diff --git a/.env b/.env new file mode 100644 index 000000000..619f471ca --- /dev/null +++ b/.env @@ -0,0 +1,64 @@ +## +## Manually dump .env files in .env.local.php with +## `$ composer symfony:dump-env prod` +## + +## Project environment +APP_ENV=dev + +## Enable debug +APP_DEBUG=true + +## Locale +LOCALE=fr + +## Framework secret +APP_SECRET=ThisTokenIsNotSoSecretChangeIt + +## Symfony/swiftmailer +MAILER_TRANSPORT=smtp +MAILER_HOST=smtp +MAILER_PORT=1025 +MAILER_CRYPT= +MAILER_AUTH= +MAILER_USER= +MAILER_PASSWORD= +MAILER_URL=${MAILER_TRANSPORT}://${MAILER_HOST}:${MAILER_PORT}?encryption=${MAILER_CRYPT}&auth_mode=${MAILER_AUTH}&username=${MAILER_USER}&password=${MAILER_PASSWORD} + +## Notifications +NOTIFICATION_HOST=localhost:8001 +NOTIFICATION_FROM_EMAIL=admin@chill.social +NOTIFICATION_FROM_NAME=Chill + +## Gelf +GELF_HOST=gelf +GELF_PORT=12201 + +## OVH OpenStack Storage User/Role +OS_USERNAME= +OS_PASSWORD= +OS_TENANT_ID= +OS_REGION_NAME=GRA +OS_AUTH_URL=https://auth.cloud.ovh.net/v2.0/ + +## OVH OpenStack Storage Container +ASYNC_UPLOAD_TEMP_URL_KEY= +ASYNC_UPLOAD_TEMP_URL_BASE_PATH= +ASYNC_UPLOAD_TEMP_URL_CONTAINER= + +## Redis Cache +REDIS_HOST=redis +REDIS_PORT=6379 +REDIS_URL=redis://${REDIS_HOST}:${REDIS_PORT} + +## Twilio +TWILIO_SID=~ +TWILIO_SECRET=~ + +## DOCKER IMAGES REGISTRY +#IMAGE_PHP= +#IMAGE_NGINX= + +## DOCKER IMAGES VERSION +#VERSION=test +VERSION=prod diff --git a/.env.test b/.env.test new file mode 100644 index 000000000..c9ff04a55 --- /dev/null +++ b/.env.test @@ -0,0 +1,6 @@ +# variables for .env environement +# those variables suits for gitlab-ci +# Run tests from root to adapt your own environment +KERNEL_CLASS='App\Kernel' +APP_SECRET='$ecretf0rt3st' +DATABASE_URL=postgresql://postgres:postgres@db:5432/postgres?serverVersion=12&charset=utf8 diff --git a/.gitignore b/.gitignore index 20a848c58..34dec8d5d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,21 @@ .composer/* composer.phar +composer.lock + docs/build/ +###> symfony/framework-bundle ### +/.env.local +/.env.local.php +/.env.*.local +/config/secrets/prod/prod.decrypt.private.php +/public/bundles/ +/var/ +/vendor/ +/bin/ +###< symfony/framework-bundle ### + +###> phpunit/phpunit ### +/phpunit.xml +.phpunit.result.cache +###< phpunit/phpunit ### diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 000000000..94601773d --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,37 @@ +--- +image: registry.gitlab.com/chill-projet/chill-app/php-base-image:7.4 + +# Select what we should cache between builds +cache: + paths: + - tests/app/vendor/ + +before_script: + # add extensions to postgres + - PGPASSWORD=$POSTGRES_PASSWORD psql -U $POSTGRES_USER -h db -c "CREATE EXTENSION IF NOT EXISTS unaccent; CREATE EXTENSION IF NOT EXISTS pg_trgm;" + # Install and run Composer + - curl -sS https://getcomposer.org/installer | php + - php composer.phar install + - php tests/app/bin/console doctrine:migrations:migrate -n + - php tests/app/bin/console doctrine:fixtures:load -n + +# Bring in any services we need http://docs.gitlab.com/ee/ci/docker/using_docker_images.html#what-is-a-service +# See http://docs.gitlab.com/ee/ci/services/README.html for examples. +services: + - name: postgres:12 + alias: db + - name: redis + alias: redis + +# Set any variables we need +variables: + # Configure postgres environment variables (https://hub.docker.com/r/_/postgres/) + POSTGRES_USER: postgres + POSTGRES_PASSWORD: postgres + # fetch the chill-app using git submodules + GIT_SUBMODULE_STRATEGY: recursive + +# Run our tests +test: + script: + - bin/phpunit --colors=never diff --git a/.gitmodules b/.gitmodules index 7bc519c88..560ba7980 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "_exts/sphinx-php"] path = _exts/sphinx-php url = https://github.com/fabpot/sphinx-php.git +[submodule "tests/app"] + path = tests/app + url = https://gitlab.com/Chill-projet/chill-app.git diff --git a/composer.json b/composer.json index e6c5108f6..4181af68a 100644 --- a/composer.json +++ b/composer.json @@ -19,6 +19,11 @@ "Chill\\ThirdPartyBundle\\": "src/Bundle/ChillThirdPartyBundle" } }, + "autoload-dev": { + "psr-4": { + "App\\": "tests/app/src/" + } + }, "require": { "champs-libres/async-uploader-bundle": "dev-sf4", "graylog2/gelf-php": "^1.5", @@ -67,6 +72,17 @@ "symfony/stopwatch": "^5.1", "symfony/web-profiler-bundle": "^5.0", "symfony/var-dumper": "4.*", - "symfony/debug-bundle": "^5.1" + "symfony/debug-bundle": "^5.1", + "symfony/phpunit-bridge": "^5.2" + }, + "scripts": { + "auto-scripts": { + "cache:clear": "symfony-cmd", + "assets:install %PUBLIC_DIR%": "symfony-cmd" + } + }, + "config": { + "vendor-dir": "tests/app/vendor", + "bin-dir": "bin" } } diff --git a/docs/source/development/index.rst b/docs/source/development/index.rst index da60cdf9e..af2e3f948 100644 --- a/docs/source/development/index.rst +++ b/docs/source/development/index.rst @@ -29,7 +29,7 @@ As Chill rely on the `symfony `_ framework, reading the fram Timelines Exports Embeddable comments - Testing + Run tests Useful snippets manual/index.rst Assets diff --git a/docs/source/development/make-test-working.rst b/docs/source/development/make-test-working.rst deleted file mode 100644 index 135fe05df..000000000 --- a/docs/source/development/make-test-working.rst +++ /dev/null @@ -1,231 +0,0 @@ -.. Copyright (C) 2014 Champs Libres Cooperative SCRLFS - Permission is granted to copy, distribute and/or modify this document - under the terms of the GNU Free Documentation License, Version 1.3 - or any later version published by the Free Software Foundation; - with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. - A copy of the license is included in the section entitled "GNU - Free Documentation License". - -Make tests working -****************** - -Unit and functional tests are important to ensure that bundle may be deployed securely. - -In reason of the Chill architecture, test should be runnable from the bundle's directory and works correctly: this will allow continuous integration tools to run tests automatically. - -.. note:: - - Integration tools (i.e. `travis-ci `_) works like this : - - * they clone the bundle repository in a virtual machine, using git - * they optionnaly run `composer` to download and install depedencies - * they optionnaly run other command to prepare a database, insert fixtures, ... - * they run test - -On the developer's machine test should be runnable with two or three commands **runned from the bundle directory** : - -.. code-block:: bash - - $ composer install --dev - $ // command to insert fixtures, ... - $ phpunit - -This chapter has been inspired by `this useful blog post `_. - -Bootstrap phpunit for a standalone bundle -========================================== - -Unit tests should run after achieving this step. - - -phpunit.xml ------------ - -A `phpunit.xml.dist` file should be present at the bundle root. - -.. code-block:: xml - - - - - - - - ./Tests - - - - - ./ - - ./Resources - ./Tests - ./vendor - - - - - - -bootstrap.php --------------- - -A file `boostrap.php`, located in the `Tests` directory, will allow phpunit to resolve class autoloading : - -.. code-block:: php - - ` should be added like this, if your `AppKernel.php` file is located in `Tests/Fixtures/App` directory: - -.. code-block:: xml - - - - - - - ./Tests - - - - - ./ - - ./Resources - ./Tests - ./vendor - - - - - - - - - -AppKernel.php -------------- - -This file boostrap the app. It contains three functions. This is the file used in the ChillMain bundle : - -.. code-block:: php - - load(__DIR__.'/config/config_'.$this->getEnvironment().'.yml'); - } - - /** - * @return string - */ - public function getCacheDir() - { - return sys_get_temp_dir().'/ChillMainBundle/cache'; - } - - /** - * @return string - */ - public function getLogDir() - { - return sys_get_temp_dir().'/ChillMainBundle/logs'; - } - } - -config_test.yml ---------------- - -There are only few parameters required for the config file. This is a basic version for ChillMain : - -.. code-block:: yaml - - # config/config_test.yml - imports: - - { resource: config.yml } #here we import a config.yml file, this is not required - - framework: - test: ~ - session: - storage_id: session.storage.filesystem - -.. code-block:: yaml - - # config/config.yml - framework: - secret: Not very secret - router: { resource: "%kernel.root_dir%/config/routing.yml" } - form: true - csrf_protection: true - session: ~ - default_locale: fr - translator: { fallback: fr } - profiler: { only_exceptions: false } - templating: #required for assetic. Remove if not needed - engines: ['twig'] - -.. note:: - - You must adapt config.yml file according to your required bundle. Some options will be missing, other may be removed... - -.. note:: - - If you would like to tests different environments, with differents configuration, you could create differents config_XXX.yml files. - -routing.yml ------------- - -You should add there all routing information needed for your bundle. - -.. code-block: yaml - - chill_main_bundle: - resource: "@CLChillMainBundle/Resources/config/routing.yml" - -That's it. Tests should pass. diff --git a/docs/source/development/run-tests.rst b/docs/source/development/run-tests.rst new file mode 100644 index 000000000..1dd944e58 --- /dev/null +++ b/docs/source/development/run-tests.rst @@ -0,0 +1,68 @@ +.. Copyright (C) 2014 Champs Libres Cooperative SCRLFS + Permission is granted to copy, distribute and/or modify this document + under the terms of the GNU Free Documentation License, Version 1.3 + or any later version published by the Free Software Foundation; + with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. + A copy of the license is included in the section entitled "GNU + Free Documentation License". + +Run tests +********* + +In reason of the Chill architecture, test should be runnable from the bundle's directory and works correctly: this will allow continuous integration tools to run tests automatically. + +From chill app +============== + +This is the most convenient method for developer: run test for chill bundle from the main app. + +.. code-block:: bash + + # run into a container + docker-compose exec --user $(id -u) php bash + # execute all tests suites + bin/phpunit + # .. or execute a single test + bin/phpunit vendor/chill-project/chill-bundles/src/Bundle/ChillMainBundle/Tests/path/to/FileTest.php + +You can also run tests in a single command: + +.. code-block:: bash + + docker-compose exec --user $(id -u) php bin/phpunit + + +Tests from a bundle (chill-bundles) +----------------------------------- + +Those tests needs the whole symfony app to execute Application Tests (which test html page). + +For ease, the app is cloned using a :code:`git submodule`, which clone the main app into :code:`tests/app`, and tests are bootstrapped to this app. The dependencies are also installed into `tests/app/vendor` to ensure compliance with relative path from this symfony application. + +You may boostrap the tests fro the chill bundle this way: + +.. code-block:: bash + + # ensure to be located into the environement (provided by docker suits well) + docker-compose exec --user $(id -u) php bash + # go to chill subdirectory + cd vendor/chill-project/chill-bundles + # install submodule + git submodule init + git submodule update + # install composer and dependencies + curl -sS https://getcomposer.org/installer | php + # run tests + bin/phpunit + +.. note:: + + If you are on a fresh install, you will need to migrate database schema. + + The path to console tool must be adapted to the app. To load migration and add fixtures, one can execute the following commands: + + .. code-block:: bash + + tests/app/bin/console doctrine:migrations:migrate + tests/app/bin/console doctrine:fixtures:load + diff --git a/phpunit.xml.dist b/phpunit.xml.dist new file mode 100644 index 000000000..c005618a8 --- /dev/null +++ b/phpunit.xml.dist @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + src/Bundle/ChillMainBundle/Tests/ + + + + src/Bundle/ChillPersonBundle/Tests/ + + + + + + + + + + + diff --git a/src/Bundle/ChillMainBundle/Test/PrepareClientTrait.php b/src/Bundle/ChillMainBundle/Test/PrepareClientTrait.php index 741f0373b..0ae7c235a 100644 --- a/src/Bundle/ChillMainBundle/Test/PrepareClientTrait.php +++ b/src/Bundle/ChillMainBundle/Test/PrepareClientTrait.php @@ -34,7 +34,7 @@ trait PrepareClientTrait * @return \Symfony\Component\BrowserKit\Client * @throws \LogicException */ - public function getClient( + public function getClientAuthenticated( $username = 'center a_social', $password = 'password' ) { diff --git a/src/Bundle/ChillMainBundle/Tests/Controller/DefaultControllerTest.php b/src/Bundle/ChillMainBundle/Tests/Controller/DefaultControllerTest.php deleted file mode 100644 index 69e7f4e47..000000000 --- a/src/Bundle/ChillMainBundle/Tests/Controller/DefaultControllerTest.php +++ /dev/null @@ -1,13 +0,0 @@ -container = self::$kernel->getContainer(); - $this->prophet = new \Prophecy\Prophet; } @@ -97,7 +90,7 @@ class ExportManagerTest extends KernelTestCase \Symfony\Component\Security\Core\User\UserInterface $user = null ) { - $localUser = $user === NULL ? $this->container->get('doctrine.orm.entity_manager') + $localUser = $user === NULL ? self::$container->get('doctrine.orm.entity_manager') ->getRepository('ChillMainBundle:User') ->findOneBy(array('username' => 'center a_social')) : $user; @@ -106,10 +99,10 @@ class ExportManagerTest extends KernelTestCase $tokenStorage->setToken($token); return new ExportManager( - $logger === NULL ? $this->container->get('logger') : $logger, - $em === NULL ? $this->container->get('doctrine.orm.entity_manager') : $em, - $authorizationChecker === NULL ? $this->container->get('security.authorization_checker') : $authorizationChecker, - $authorizationHelper === NULL ? $this->container->get('chill.main.security.authorization.helper') : $authorizationHelper, + $logger === NULL ? self::$container->get('logger') : $logger, + $em === NULL ? self::$container->get('doctrine.orm.entity_manager') : $em, + $authorizationChecker === NULL ? self::$container->get('security.authorization_checker') : $authorizationChecker, + $authorizationHelper === NULL ? self::$container->get('chill.main.security.authorization.helper') : $authorizationHelper, $tokenStorage) ; } @@ -544,7 +537,7 @@ class ExportManagerTest extends KernelTestCase $export = $this->prophet->prophesize(); $export->willImplement(ExportInterface::class); - $em = $this->container->get('doctrine.orm.entity_manager'); + $em = self::$container->get('doctrine.orm.entity_manager'); $export->initiateQuery( Argument::is(array('foo')), Argument::Type('array'), @@ -627,7 +620,7 @@ class ExportManagerTest extends KernelTestCase //add formatter interface $formatter = new \Chill\MainBundle\Export\Formatter\SpreadSheetFormatter( - $this->container->get('translator'), $exportManager); + self::$container->get('translator'), $exportManager); $exportManager->addFormatter($formatter, 'spreadsheet'); //ob_start(); diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php index 3034da3a9..f66df6a2d 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod.php @@ -42,7 +42,37 @@ use Chill\MainBundle\Entity\User; */ class AccompanyingPeriod { - const INTENSITY = ['occasional', 'regular']; + /** + * Mark an accompanying period as "occasional" + * + * used in INTENSITY + */ + public const INTENSITY_OCCASIONAL = 'occasional'; + + /** + * Mark an accompanying period as "regular" + * + * used in INTENSITY + */ + public const INTENSITY_REGULAR = 'regular'; + + public const INTENSITIES = [self::INTENSITY_OCCASIONAL, self::INTENSITY_REGULAR]; + + /** + * Mark an accompanying period as "draft". + * + * This means that the accompanying period is not yet + * confirmed by the creator + */ + public const STEP_DRAFT = 'DRAFT'; + + /** + * Mark an accompanying period as "confirmed". + * + * This means that the accompanying period **is** + * confirmed by the creator + */ + public const STEP_CONFIRMED = 'CONFIRMED'; /** * @var integer @@ -117,7 +147,7 @@ class AccompanyingPeriod * @var string * @ORM\Column(type="string", length=32, nullable=true) */ - private $step = 'DRAFT'; + private $step = self::STEP_DRAFT; /** * @ORM\ManyToOne(targetEntity=Origin::class) diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/ClosingMotive.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/ClosingMotive.php index 1bb48da00..4e334b2aa 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/ClosingMotive.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/ClosingMotive.php @@ -47,7 +47,7 @@ class ClosingMotive /** * @var array * - * @ORM\Column(type="json_array") + * @ORM\Column(type="json") */ private $name; diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Origin.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Origin.php index 4016cd833..42c75efca 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Origin.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriod/Origin.php @@ -39,7 +39,7 @@ class Origin private $id; /** - * @ORM\Column(type="string", length=255) + * @ORM\Column(type="json") */ private $label; diff --git a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriodParticipation.php b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriodParticipation.php index 9a807b179..c22e84e42 100644 --- a/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriodParticipation.php +++ b/src/Bundle/ChillPersonBundle/Entity/AccompanyingPeriodParticipation.php @@ -50,7 +50,7 @@ class AccompanyingPeriodParticipation private $person; /** - * @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class, inversedBy="participations") + * @ORM\ManyToOne(targetEntity=AccompanyingPeriod::class, inversedBy="participations", cascade={"persist"}) * @ORM\JoinColumn(name="accompanyingperiod_id", referencedColumnName="id", nullable=false) */ private $accompanyingPeriod; diff --git a/src/Bundle/ChillPersonBundle/Form/PersonType.php b/src/Bundle/ChillPersonBundle/Form/PersonType.php index d87fe0196..35697b617 100644 --- a/src/Bundle/ChillPersonBundle/Form/PersonType.php +++ b/src/Bundle/ChillPersonBundle/Form/PersonType.php @@ -123,7 +123,8 @@ class PersonType extends AbstractType 'label' => false, 'delete_empty' => function(PersonPhone $pp = null) { return NULL === $pp || $pp->isEmpty(); - } + }, + 'error_bubbling' => false ]); if ($this->config['email'] === 'visible') { diff --git a/src/Bundle/ChillPersonBundle/Resources/views/Person/edit.html.twig b/src/Bundle/ChillPersonBundle/Resources/views/Person/edit.html.twig index 4546cc56d..a69253695 100644 --- a/src/Bundle/ChillPersonBundle/Resources/views/Person/edit.html.twig +++ b/src/Bundle/ChillPersonBundle/Resources/views/Person/edit.html.twig @@ -85,6 +85,7 @@ {%- endif -%} {%- if form.otherPhoneNumbers is defined -%} {{ form_widget(form.otherPhoneNumbers) }} + {{ form_errors(form.otherPhoneNumbers) }} {%- endif -%} {%- if form.contactInfo is defined -%} {{ form_row(form.contactInfo, {'label': 'Notes on contact information'}) }} diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210419105054.php b/src/Bundle/ChillPersonBundle/migrations/Version20210419105054.php new file mode 100644 index 000000000..c2b752822 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20210419105054.php @@ -0,0 +1,32 @@ +addSql('ALTER TABLE chill_person_closingmotive_id_seq RENAME TO ' + . 'chill_person_accompanying_period_closingmotive_id_seq'); + + } + + public function down(Schema $schema) : void + { + $this->addSql('ALTER TABLE chill_person_accompanying_period_closingmotive_id_seq ' + . 'RENAME TO chill_person_closingmotive_id_seq'); + } +} diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210419105940.php b/src/Bundle/ChillPersonBundle/migrations/Version20210419105940.php new file mode 100644 index 000000000..4214c7069 --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20210419105940.php @@ -0,0 +1,36 @@ +addSql('ALTER TABLE chill_person_accompanying_period_origin ' + . 'ALTER label TYPE JSON USING json_build_object(\'fr\', label)::jsonb'); + $this->addSql('ALTER TABLE chill_person_accompanying_period_origin ' + . 'ALTER label DROP DEFAULT'); + } + + public function down(Schema $schema) : void + { + // this will keep the '"' at first and last character, but is acceptable + $this->addSql('ALTER TABLE chill_person_accompanying_period_origin ' + . 'ALTER label TYPE VARCHAR(255) USING label->\'fr\'::text'); + $this->addSql('ALTER TABLE chill_person_accompanying_period_origin ' + . 'ALTER label DROP DEFAULT'); + } +} diff --git a/src/Bundle/ChillPersonBundle/migrations/Version20210419112619.php b/src/Bundle/ChillPersonBundle/migrations/Version20210419112619.php new file mode 100644 index 000000000..919fe4cff --- /dev/null +++ b/src/Bundle/ChillPersonBundle/migrations/Version20210419112619.php @@ -0,0 +1,29 @@ +addSql('COMMENT ON COLUMN chill_person_accompanying_period_closingmotive.name IS NULL'); + } + + public function down(Schema $schema) : void + { + $this->addSql('COMMENT ON COLUMN chill_person_accompanying_period_closingmotive.name IS \'(DC2Type:json_array)\''); + } +} diff --git a/src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php b/src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php index 4e9ea028e..180491c1f 100644 --- a/src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php +++ b/src/Bundle/ChillReportBundle/DataFixtures/ORM/LoadReports.php @@ -89,7 +89,7 @@ class LoadReports extends AbstractFixture implements OrderedFixtureInterface, Co { $charline = $this->container->get('doctrine.orm.entity_manager') ->getRepository('ChillPersonBundle:Person') - ->findOneBy(array('lastName' => 'Charline')) + ->findOneBy(array('firstName' => 'Charline', 'lastName' => 'Depardieu')) ; $report = (new Report()) diff --git a/tests/app b/tests/app new file mode 160000 index 000000000..5321a3a45 --- /dev/null +++ b/tests/app @@ -0,0 +1 @@ +Subproject commit 5321a3a4506f8db0f143909be307ed068e15df9c