<?php
namespace App\Test\TestCase\Controller;

//use Cake\TestSuite\IntegrationTestCase;
use App\Controller\AppController;
use Cake\ORM\TableRegistry;
//use Cake\ORM\Locator\TableLocator;
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
use App\Controller\MaterielsController;
use App\Controller\SuivisController;
//use App\Controller\SurCategoriesController;
use Cake\Http\Exception\NotImplementedException;
use Cake\Utility\Inflector;
use App\Controller\UsersController;

//use App\Controller\MaterielsController;

/**
 * App\Controller\GeneralController Test Case
 */
//class General extends IntegrationTestCase {
// https://book.cakephp.org/3/en/development/testing.html#controller-integration-testing
// New in version 3.7.0: The IntegrationTestCase class was moved into the IntegrationTestTrait trait.
class General extends TestCase {
    
    use IntegrationTestTrait;

    // Si DEBUG, affiche plus d'infos
    protected $DEBUG=false;
    //protected $DEBUG=true;
    
    // instance du controleur testé
    protected $controller_instance = null;
    // instance spécifique de MaterielsController
    protected $controller_materiels_instance = null;
    
    //TODO: definine this in a superclass
    /*
    const PROFILE_USER = 1;
    const PROFILE_RESPONSABLE = 2;
    const PROFILE_ADMIN = 3;
    const PROFILE_ADMINPLUS = 4;
    const PROFILE_SUPERADMIN = 5;
    */
    protected $ROLES = [
        'USER',
        'RESP',
        'ADMIN',
        //'ADMINP',
        'SUPER'
    ];
    /* 
     * (EP 20200504)
     * Supprimé le cas USER_from_ldap car tous les users sont desormais dans la table users.
     * On n'a donc plus jamais ce cas...
     */
    protected $ROLES6 = [
        //['USER_from_ldap'],
        ['USER'],
        ['RESP'],
        ['ADMIN'],
        //['ADMINP'],
        ['SUPER']
        /*
        */
    ];
    protected $ROLES5 = [
        //['USER_from_ldap'],
        ['USER'],
        ['RESP'],
        ['ADMIN'],
        //['ADMINP'],
        ['SUPER']
        /*
        */
    ];
    protected $ROLES4 = [
        /*
        */
        ['USER'],
        ['RESP'],
        ['ADMIN'],
        ['SUPER']
    ];
    
    const PROFILES = AppController::PROFILES;
    
    // Current role (profile) of the user
    private $CURRENT_ROLE = null;
    
    // Utilisateur courant from DB (Entity)
    private $CURRENT_USER = null;
    
    // Instance de la table materiels 
    private $Materiels = null;
    
    /*
    public $ControllerApp = null;
    public function setUp() {
        parent::setUp();
        
        /*
        $config = TableRegistry::exists('Materiels') ? [] : [
            'className' => 'App\Model\Table\MaterielsTable'
        ];
        $this->Materiels = TableRegistry::get('Materiels', $config);
        
        $config = TableRegistry::exists('Suivis') ? [] : [
            'className' => 'App\Model\Table\SuivisTable'
        ];
        $this->Suivis = TableRegistry::get('Suivis', $config);
        */ /*
        
        $this->ControllerApp = new AppController();
    }
    
    public function tearDown() {
        //unset($this->Materiels);
        //unset($this->Suivis);
        unset($this->ControllerApp);
        
        parent::tearDown();
    }
    */
    
    public function setUp() {
        
        // (EP202010)
        // https://stackoverflow.com/questions/29748911/cakephp-connections-while-testing/45592461
        /*
         * Some tables tests were getting default db instead of test db for no reason. 
         * This solves the problem (we have a main test class from which we extend all tests, do some setting up. We put it there)
         */
        TableRegistry::clear();
        
        parent::setUp();
        //$this->toto = 'titi';
        // Check qu'on lit bien la table configurations de la BD de test !!
        $config = TableRegistry::exists('Configurations') ? [] : [
            'className' => 'App\Model\Table\ConfigurationsTable'
        ];
        $confLabinvent = TableRegistry::getTableLocator()->get('Configurations', $config)->find()->first();
        //assert($confLabinvent->ldap_authenticationType == 'uid', 'On ne lit pas la bonne configuration');
        //debug($confLabinvent->ldap_authenticationType);
        $this->assertTextEquals('uid', $confLabinvent->ldap_authenticationType, 'On ne lit pas la bonne configuration');
        $this->assertTextNotEquals('samaccountname', $confLabinvent->ldap_authenticationType, 'On ne lit pas la bonne configuration');
        $this->assertTextEquals(1, $confLabinvent->test, 'On ne lit pas la bonne configuration');
        $this->assertTextEquals(800, $confLabinvent->prix_inventaire_administratif, 'On ne lit pas la bonne configuration');
        $this->assertTextEquals('TEST', $confLabinvent->labNameShort, 'On ne lit pas la bonne configuration');
    }
    
    
    public function d($msg) {
        if ($this->DEBUG) pr($msg);
    }
    
    /*
     * On recupère une entité quelconque de la BD (fixture), peu importe ce que c'est car on va la modifier
     * Par défaut, on utilise la première entité de la BD (id 1)
     */
    static protected function _getEntityIdOkForTesting() {
        return 1;
    }
    
    // To be overriden by subclasses
    protected function getNewEntityWithAllMandatoryFields() {
        throw new NotImplementedException(__METHOD__);
    }
    
    protected function getEntitiesName() {
        //throw new NotImplementedException("Méthode getEntitiesName() non implémentée !!");
        $test_class_name = get_class($this); // MaterielsControllerTest, SurCategoriesControllerTest, ...
        //debug($test_class_name);
        $entities_name = explode('\\', substr($test_class_name,0,strpos($test_class_name, 'ControllerTest')));
        //debug($entities_name);
        return $entities_name[count($entities_name)-1];
    }
    protected function getNbEntitiesInFixture() {
        $entities_name = $this->getEntitiesName();
        $nb = TableRegistry::getTableLocator()->get($entities_name)->find()->count();
        //$this->d("nb entities in fixture: $nb");
        return $nb;
    }
    protected function getNbEntitiesExpectedInIndexView() {
        return $this->getNbEntitiesInFixture();
    }

    protected function getMaterielsTable() {
        //if (!$this->Materiels) $this->Materiels = TableRegistry::get('Materiels');
        if (!$this->Materiels) $this->Materiels = TableRegistry::getTableLocator()->get('Materiels');
        return $this->Materiels;
        /*
        $m = $this->Materiels->get($id);
        return $m;
        */
    }

    protected function getMaterielsController() {
        // On est dans MaterielsControllerTest => on retourne le controleur courant
        if ($this->getEntitiesName()=='Materiels') return $this->_getController();
        // Sinon, on retourne une instance spécifique pour les materiels
        if (!$this->controller_materiels_instance) $this->controller_materiels_instance = new MaterielsController();
        return $this->controller_materiels_instance;
    }
        
    protected function _getController() {
        //if (!$this->controller_instance) $this->controller_instance = new MaterielsController();
        $entities_name = $this->getEntitiesName(); // ex: 'Materiels', 'Suivis', 'SurCategories'...
        //$entities_name = 'SurCategories';
        //debug("entities_name is $entities_name");
        $controller_name = $this->_getControllerFullNameFromEntitiesName($entities_name);
        //debug("controller_name is $controller_name"); exit;
        if (!$this->controller_instance) $this->controller_instance = new $controller_name();
        return $this->controller_instance;
    }
    protected static function _getControllerInstanceFromName($entities_name) {
        //$controller_name = $this->_getControllerFullNameFromEntitiesName($entities_name);
        $controller_name = self::_getControllerFullNameFromEntitiesName($entities_name);
        return new $controller_name();
    }
    /* 
     * $entities_name is 'Materiels', 'Suivis', 'SurCategories'...
     * @return : App\Controller\SurCategoriesController, ...
     */
    private static function _getControllerFullNameFromEntitiesName($entities_name) {
        //$entities_name = ucfirst(strtolower($entities_name)); // Materiels, Suivis, Emprunts...
        //$entities_name = ucfirst(strtolower($entities_name)); // Materiels, Suivis, Emprunts...
        //$controller_name = 'App\\Controller\\'.$this->getEntitiesName().'Controller'; // ex: 'Materiels' ou 'Suivis'...
        return 'App\\Controller\\'.$entities_name.'Controller'; // ex: 'MaterielsController' ou 'SuivisController'...
        //$controller_name = 'App\\Controller\\'.$this->getEntitiesName().'Controller'; // ex: 'Materiels' ou 'Suivis'...
    }
    
    
    
    // This are data providers used for a lot of tests
    // See https://jtreminio.com/2013/03/unit-testing-tutorial-part-2-assertions-writing-a-useful-test-and-dataprovider
    public function dataProviderRoles4() { return $this->ROLES4; }
    public function dataProviderRoles5() { return $this->ROLES5; }
    // Idem dataProviderRoles5 mais avec USER_from_ldap en plus:
    public function dataProviderRoles6() { return $this->ROLES6; }
    
    protected function getActions() {
        //return ['toto'];
        return $this->_getController()->getActions();
    }
    
    // Combinaison de roles (U,R,A,S) et statuts (C,V,T,A)
    public function dataProviderRoles4AndStatuses4() {
        $roles_and_statuses = [];
        foreach ($this->ROLES4 as $user_role) {
            //debug($user_role[0]);
            foreach ($this->MATOS_STATUSES as $matos_status)
                $roles_and_statuses[] = [$user_role[0], $matos_status[0]];
        }
        //$this->d($roles_and_statuses);
        return $roles_and_statuses;
    }
    
    //public function dataProviderActionsAndRoles4($controller) {
    public function dataProviderActionsAndRoles4() {
        $actions_and_roles = [];
        $actions = $this->getActions();
        //$actions = ['add'];
        //$actions = MaterielsController::getActions();
        //$controller = 'App\\Controller\\'.$controller;
        //debug($this);
        //debug("controller is $controller_name"); exit;
        //$actions = $controller_name::getActions();
        //$actions = SuivisController::getActions();
        //debug($actions); exit;
        //debug("entities is : ".$this->getEntitiesName()); exit;
        //$actions = $this->getController()->getActions();
        //debug("Liste des Actions à tester:");debug($actions); exit;
        //foreach ($actions as $a) echo("$a\n");
        /*
        */
        /*
        $actions = [
            // CRUD
            'add','add_by_copy', 'view','index','edit','delete',
            //'view',
            //'index',
            // Autres
            'statusCreated',
        ];
        */
        $roles4 = [
            'user',
            'resp',
            'admin',
            'super',
            /*
            */
        ];
        //$actions = ['delete'];
        //$actions = ['edit'];
        foreach ($actions as $action)
            //foreach ([0,1] as $i) {
            //foreach ([1] as $i) {
            //foreach ([0] as $i) {
            foreach ([0,1,2,3] as $i) {
                $item = [ $action, $roles4[$i], $this->ROLES4[$i][0] ];
                //debug($item);
                $actions_and_roles[] = $item;
            }
        return $actions_and_roles;
    }
    

    
    /* FONCTIONS UTILITAIRES UTILISÉES PAR (tous) LES TESTS */
 
    public static function getRoleLevel($role) { return AppController::getRoleLevel($role); }
    
    // Definition DIFFERENTE de celle de AppController
    public function getUserRole() {
        //return $this->ControllerMateriels->getUserRole();
        if (! $this->CURRENT_ROLE) {
            $user = $this->getCurrentUser();
            /*
            $user = TableRegistry::get('Users')->find()->where([
                // username = LOGIN (ex: 'epallier') ; pour les tests : user1_SUPER, user5_USER, ...
                'username' => $this->_session['Auth']['User']['uid'][0]
                //'username' => 'user1_SUPER'
                //'username' => $this->_session['Auth']['User']['cn'][0]
            ])->first();
            */
            // Unpriviledged user
            $role = $user ? $user['role'] : "Utilisateur";
            $this->CURRENT_ROLE = $role;
        }
        return $this->CURRENT_ROLE;
    }
    
    protected function getCurrentUser() {
        if (! $this->CURRENT_USER) {
            $this->CURRENT_USER = TableRegistry::get('Users')->find()->where([
                // username = LOGIN (ex: 'epallier') ; pour les tests : user1_SUPER, user5_USER, ...
                'username' => $this->_session['Auth']['User']['uid'][0]
                //'username' => 'user1_SUPER'
                //'username' => $this->_session['Auth']['User']['cn'][0]
            ])->first();
        }
        return $this->CURRENT_USER;
    }
    
    // MEME Definition de celle de AppController (mais n'utilise pas la meme fonction getUserRole())
    public function userHasRole($expectedRole, $ORMORE=false) {
        $role = $this->getUserRole();
        //debug($role);
        if (! $ORMORE) return ($role == $expectedRole);
        return ($this->getRoleLevel($role) >= $this->getRoleLevel($expectedRole));
    }
    // MEME Definition de celle de AppController (mais n'utilise la meme fonction getUserRole())
    public function userHasRoleAtLeast($expectedRole) {
        return $this->userHasRole($expectedRole, true);
    }
    // MEME Definition de celle de AppController (mais n'utilise la meme fonction getUserRole())
    public function USER_IS_ADMIN_AT_LEAST() { return $this->userHasRoleAtLeast('Administration'); }
    public function USER_IS_RESP_AT_LEAST() { return $this->userHasRoleAtLeast('Responsable'); }
    public function USER_IS_SUPERADMIN() { return $this->userHasRole('Super Administrateur'); }
    //public function USER_IS_ADMIN_PLUS() { return $this->userHasRole('Administration Plus'); }
    public function USER_IS_ADMIN() { return $this->userHasRole('Administration'); }
    public function USER_IS_RESP() { return $this->userHasRole('Responsable'); }
    public function USER_IS_USER() { return $this->userHasRole('Utilisateur'); }
    /*
    public function USER_IS_ADMIN_AT_LEAST($role=null) {
        if (is_null($role)) { 
            $role = $this->getUserRole();
            $roles = ['Administration','Administration Plus','Super Administrateur'];
        }
        else {
            $roles = ['ADMIN','ADMINP','SUPER'];
        }
        return in_array($role, $roles);
    }
    */
    
    
    public function authAs($role) {
        switch ($role) {
            case 'USER_from_ldap':
                $this->authUtilisateurFromLdap();
                //$this->CURRENT_ROLE = 'Utilisateur';
                break;
            case 'USER': 
                $this->authUtilisateurFromTable();
                //$this->CURRENT_ROLE = 'Utilisateur';
                break;
            case 'RESP': 
                $this->authResponsable();
                //$this->CURRENT_ROLE = 'Responsable';
                break;
            case 'ADMIN': 
                $this->authAdmin();
                //$this->CURRENT_ROLE = 'Administration';
                break;
            /*
            case 'ADMINP': 
                $this->authAdminPlus();
                //$this->CURRENT_ROLE = 'Administration Plus';
                break;
            */
            case 'SUPER': 
                $this->authSuperAdmin();
                //$this->CURRENT_ROLE = 'Super Administrateur';
                break;
        }
    }
    
    // authUser('login', 'Prenom', 'Nom');
    // ex: $this->authUser('u1SUPER', 'user1', 'SUPER');
    // ex: $this->authUser('user5_USER', 'test9', 'test0');
    private function authUser($login, $givenName='test1', $name='test2') {
        $user = [
            'Auth' => [
                'User' => [
                    // Nom
                    'sn' => [
                        0 => $name
                    ],
                    // Email (normalement prenom.nom@test.fr)
                    'mail' => [
                        0 => 'testa@test.fr'
                    ],
                    // Prenom
                    'givenname' => [
                        0 => $givenName
                    ],
                    // LOGIN (normalement epallier)
                    /*
                    'cn' => [
                        0 => $cn
                    ],
                    */
                    'uid' => [
                        0 => $login
                    ],
                    'userpassword' => [
                        0 => 'test'
                    ]
                ]
            ]
        ];
        $this->session($user);
        //$this->request->getSession()->write($user);
        
        $authType = [
            'authType' => 'cn'
        ];
        $this->session($authType);
        //$this->request->getSession()->write($authType);
        (new UsersController())->statsUpdateForCurrentUserWhen($user['Auth']['User'],'login');
        
    }
    
    public function authSuperAdmin() {
        
        //$this->authUser('login', 'Prenom', 'Nom');
        $this->authUser('u1SUPER', 'user1', 'SUPER');
        ///$this->authUser('user1_SUPER', 'user1', 'SUPER');
        
        //$this->authUser('user1_SUPER', 'test1', 'test2');
        //$this->authUser('testa', 'user1', 'SUPER');
    }
    /*
    public function authSuperAdmin() {
        $user = [
            'Auth' => [
                'User' => [
                    'sn' => [
                        0 => 'test2'
                    ],
                    'mail' => [
                        0 => 'testa@test.fr'
                    ],
                    'givenname' => [
                        0 => 'test1'
                    ],
                    'cn' => [
                        //0 => 'testa'
                        0 => 'user1_SUPER'
                    ],
                    'userpassword' => [
                        0 => 'test'
                    ]
                ]
            ]
        ];
        $this->session($user);

        $authType = [
            'authType' => 'cn'
        ];
        $this->session($authType);
    }
    */

    /*
    public function authAdminPlus() {
        /////$this->authUser('user2_ADMINPLUS', 'test3', 'test4');
        $this->authUser('u2ADMINP', 'user2', 'ADMINP');
    }
    */
    /*
     $user = [
            'Auth' => [
                'User' => [
                    'sn' => [
                        0 => 'test4'
                    ],
                    'mail' => [
                        0 => 'testz@test.fr'
                    ],
                    'givenname' => [
                        0 => 'test3'
                    ],
                    'cn' => [
                        //0 => 'testz'
                        0 => 'user2_ADMINPLUS'
                    ],
                    'userpassword' => [
                        0 => 'test'
                    ]
                ]
            ]
        ];
        $this->session($user);

        $authType = [
            'authType' => 'cn'
        ];
        $this->session($authType);
    }
    */
    
    public function dataProviderRoles2() { return $this->ROLES2; }

    public function authAdmin() {
        //////$this->authUser('user3_ADMIN', 'test5', 'test6');
        $this->authUser('u3ADMIN', 'user3', 'ADMIN');
    }
    /*
        $user = [
            'Auth' => [
                'User' => [
                    'sn' => [
                        0 => 'test6'
                    ],
                    'mail' => [
                        0 => 'teste@test.fr'
                    ],
                    'givenname' => [
                        0 => 'test5'
                    ],
                    'cn' => [
                        //0 => 'teste'
                        0 => 'user3_ADMIN'
                    ],
                    'userpassword' => [
                        0 => 'test'
                    ]
                ]
            ]
        ];
        $this->session($user);

        $authType = [
            'authType' => 'cn'
        ];
        $this->session($authType);
    }
    */

    public function authResponsable() {
        ///////$this->authUser('user4_RESP', 'test7', 'test8');
        $this->authUser('u4RESP', 'user4', 'RESP');
    }
    /*
        $user = [
            'Auth' => [
                'User' => [
                    'sn' => [
                        0 => 'test8'
                    ],
                    'mail' => [
                        0 => 'testr@test.fr'
                    ],
                    'givenname' => [
                        0 => 'test7'
                    ],
                    'cn' => [
                        //0 => 'testr'
                        0 => 'user4_RESP'
                    ],
                    'userpassword' => [
                        0 => 'test'
                    ]
                ]
            ]
        ];
        $this->session($user);

        $authType = [
            'authType' => 'cn'
        ];
        $this->session($authType);
    }
    */

    public function authUtilisateur() { $this->authUtilisateurFromTable(); }
    public function authUtilisateurFromTable() {
        /////$this->authUser('user5_USER', 'test9', 'test0');
        $this->authUser('u5USER', 'user5', 'USER');
    }
    public function authUtilisateurFromLdap() {
        //$this->authUser('_NouvelUtilisateur_username', 'NOUVEL', 'UTILISATEUR');
        $this->authUser('_fake_ldap_user_', 'FAKE_LDAP', 'UTILISATEUR');
    }
    /*
     $user = [
            'Auth' => [
                'User' => [
                    'sn' => [
                        0 => 'test0'
                    ],
                    'mail' => [
                        0 => 'testt@test.fr'
                    ],
                    'givenname' => [
                        0 => 'test9'
                    ],
                    'cn' => [
                        //0 => 'testt'
                        0 => 'user5_USER'
                    ],
                    'userpassword' => [
                        0 => 'test'
                    ]
                ]
            ]
        ];
        $this->session($user);

        $authType = [
            'authType' => 'cn'
        ];
        $this->session($authType);
    }
    */
    
    //protected function assertResponseContainsIf($role, $condition, $content, $messageIfNot=null) {
    //protected function assertResponseContainsIf(string $role, bool $condition, array $contents, $testOpposite=true) {
    protected function assertResponseContainsIf($role, $condition, array $contents, $testOpposite=true) {
        if ($condition) {
            $messageOnFail = 'devrait avoir accès';
            $assertMethod = 'assertResponseContains'; 
        }
        else {
            if (!$testOpposite) return;
            $messageOnFail = 'ne devrait PAS avoir accès';
            $assertMethod = 'assertResponseNotContains';
        }
        foreach ($contents as $content=>$endmsg)
            $this->$assertMethod($content, "Le profil (rôle) $role $messageOnFail $endmsg");
        /*
        $this->assertResponseContains($content, 'Le profil (rôle) '.$role.' devrait avoir accès à '. $messageIfNot);
        }
        else {
            $this->assertResponseNotContains($content, 'Le profil (rôle) '.$role.' ne devrait PAS avoir accès à '. $messageIfNot);
        }
        */
    }
    

    // Test qu'on a bien le nb attendu d'entités dans la vue index
    protected function _testNbEntitiesInIndexView($entities_name, $nb_entities, $error_msg=null) {
        //$this->get('/emprunts/index');
        $this->get("/$entities_name/index");
        if (!$error_msg) $error_msg = "Le nombre des $entities_name dans la BD (tel qu'affiché par la vue index) devrait être de $nb_entities !";
        $this->assertResponseContains("Liste des $entities_name ($nb_entities)", $error_msg);
    }
        
    // Ajout d'entités liées à un matériel, telles que suivis, emprunts, ou documents...
    protected function _testAddEntitiesRelatedToMateriel($entity_name, $nb_entities_in_fixture, $data) {
        $matos_created_id = 1;
        $matos_validated_id = 3;
        $entities_name = $entity_name.'s';
        
        // Test qu'on a bien le nb d'entités de la fixture en BD
        $nb = $nb_entities_in_fixture;
        $this->_testNbEntitiesInIndexView($entities_name, $nb);
        /*
        //$this->get('/emprunts/index');
        $this->get("/$entities_name/index");
        $this->assertResponseContains("Liste des $entities_name ($nb)", "Le nombre initial (fixture) des $entities_name dans la BD devrait être de $nb !");
        */
        
        // Ajout d'une 1ère entité (matériel VALIDATED) => OK
        $data['materiel_id'] = $matos_validated_id;
        $this->assertTextEquals($data['materiel_id'], $matos_validated_id);
        //$this->post("/emprunts/add/$matos_validated_id", $data);
        $this->post("/$entities_name/add/$matos_validated_id", $data);
        $nb++;
        $this->_testNbEntitiesInIndexView($entities_name, $nb, "L'ajout d'un $entity_name (1) pour un materiel VALIDATED ne se fait pas correctement alors que ça devrait !");
        return;
        /*
        $this->get("/$entities_name/index");
        $this->assertResponseContains("Liste des $entities_name ($nb)", "L'ajout d'un $entity_name (1) pour un materiel VALIDATED ne se fait pas correctement alors que ça devrait !");
        */
        
        // Ajout d'une 2ème entité (matériel VALIDATED) => OK
        //$data['materiel_id'] = $matos_validated_id;
        //$this->assertTextEquals($data['materiel_id'], $matos_validated_id);
        $this->post("/$entities_name/add/$matos_validated_id", $data);
        $nb++;
        $this->_testNbEntitiesInIndexView($entities_name, $nb, "L'ajout d'un $entity_name (2) pour un materiel VALIDATED ne se fait pas correctement alors que ça devrait !");
        /*
        $this->get("/$entities_name/index");
        $this->assertResponseContains("Liste des $entities_name ($nb)", "L'ajout d'un $entity_name (2) pour un materiel VALIDATED ne se fait pas correctement alors que ça devrait !");
        */
        
        // Ajout d'une 3ème entité (matériel CREATED) => KO
        $data['materiel_id'] = $matos_created_id;
        $this->assertTextEquals($data['materiel_id'], $matos_created_id);
        $this->post("/$entities_name/add/$matos_created_id", $data);
        //$nb++;
        $this->_testNbEntitiesInIndexView($entities_name, $nb, "L'ajout d'un $entity_name (3) pour un materiel CREATED ne se fait pas correctement alors que ça ne devrait pas !");
        /*
        $this->get("/$entities_name/index");
        $this->assertResponseContains("Liste des $entities_name ($nb)", "L'ajout d'un $entity_name (3) pour un materiel CREATED se fait correctement alors que ça ne devrait pas !");
        */
        
        // Ajout d'une 4ème entité (matériel VALIDATED) => OK
        $data['materiel_id'] = $matos_validated_id;
        $this->assertTextEquals($data['materiel_id'], $matos_validated_id);
        $this->post("/$entities_name/add/$matos_validated_id", $data);
        $nb++;
        $this->_testNbEntitiesInIndexView($entities_name, $nb, "L'ajout d'un $entity_name (4) pour un materiel VALIDATED ne se fait pas correctement alors que ça devrait !");
        /*
        $this->get("/$entities_name/index");
        $this->assertResponseContains("Liste des $entities_name ($nb)", "L'ajout d'un $entity_name (4) pour un materiel VALIDATED ne se fait pas correctement alors que ça devrait !");
        */
    } // testAddEntitiesRelatedToMateriel
    
    
    protected function inflectEntityName($entities_name) {
        //$entities_name = strtolower($entities_name);
        switch ($entities_name) {
            case 'Materiel': $entities_name = 'Matériel'; break;
            case 'Materiels': $entities_name = 'Matériels'; break;
            
            case 'User': $entities_name = 'Utilisateur'; break;
            case 'Users': $entities_name = 'Utilisateurs'; break;
            
            case 'SurCategorie': $entities_name = 'Domaine'; break;
            case 'SurCategories': $entities_name = 'Domaines'; break;
            
            /*
            case 'materiel': $entities_name = 'matériel'; break;
            case 'materiels': $entities_name = 'matériels'; break;

            case 'user': $entities_name = 'utilisateur'; break;
            case 'users': $entities_name = 'utilisateurs'; break;
            
            case 'sur-categorie': $entities_name = 'domaine'; break;
            case 'sur-categories': $entities_name = 'domaines'; break;
            */
        }
        // Par défaut, aucun changement (ex: 'suivis' => 'suivis')
        //return $entities_name;
        return lcfirst($entities_name);
    }
        
    
    protected function _checkNbEntitiesInIndexViewIsAsExpected($entities_name,$nbentities_expected) {
        //$this->get('/materiels/index');
        //$entities_name = Inflector::dasherize($entities_name);
        //debug("entities1 $entities_name");
        $this->get("/$entities_name/index");
        //debug($_SESSION);return;
        $this->assertResponseOk();
        $entities_name = $this->inflectEntityName($entities_name);
        //debug("entities2 $entities_name");
        $this->assertResponseContains("Liste des $entities_name ($nbentities_expected)", "Le nombre d'entités dans la vue 'index' est incorrect");
    }
    
    
    /**
     * 
     * (EP 20200620)
     * 
     * Test (automatique) de (presque) TOUTES les actions d'un controleur
     * 
     * Appelé par chaque test de controleur en passant en paramètre $entities_name (et le reste par DataProvider)
     *
     * @return void
     *
     */
    protected function _testAuthorizationsForAllControllerActions($action, $role_short, $role_long) {
    //protected function _testAuthorizationsForAllActionsOfController($entities_name, $action, $role_short, $role_long) {
        
        // Force le rechargement des entités materiel en BD (car on les modifie en cours de route)
        //AppController::forceReload();
        $entities_name = $this->getEntitiesName();
        //debug("entities0 $entities_name");
        
        $IS_MATERIEL = ($entities_name=='Materiels');
        //debug("entities_name is $entities_name");
        
        /*
        //$controller_name = 'Materiels';
        $controller_name = 'App\Controller\\'.$entities_name.'Controller';
        //$mc = new MaterielsController();
        $c = new $controller_name();
        //$entity_name =$c->name;
        */
        $c = $this->_getController();

        // POUR CHAQUE $action et $role (fournis par le dataProvider) :
        
        $this->d("*******************");
        //$this->d("    CONTROLEUR $c->name, ACTION $action, ROLE $role_short");
        $this->d("    CONTROLEUR {$c->getName()}, ACTION $action, ROLE $role_short");
        $this->d("*******************");
        //$this->setUp();
        
        //$this->authSuperAdmin();
        $this->authAs($role_long);
        // NULL why ?
        //debug($_SESSION);return;
        
        $nbentities = $this->getNbEntitiesExpectedInIndexView();
        // NULL why ?
        //debug($_SESSION);return;
        $this->_checkNbEntitiesInIndexViewIsAsExpected($entities_name, $nbentities);
        // pas NULL why ?
        //debug($_SESSION);return;
        
        // On recupère une entité quelconque de la BD (fixture), peu importe ce que c'est car on va la modifier
        $id = $this->_getEntityIdOkForTesting();
        if (in_array($action, ['add','index'])) $id=null; // OU $id=0 ?
        
        $getpost = in_array($action, ['view', 'index', 'find', 'printLabelRuban']) ? 'get':'post';
        $posted_data = [];
        if ($action=='add') $posted_data = $this->getNewEntityWithAllMandatoryFields();
        // Si 'edit', on marque le materiel comme ayant été modifié (on change le nom)
        if ($action=='edit') {
            $e = $c->getEntity($id);
            $name_field = $c->getNameFieldLabel();
            // On ajoute '(CHANGED)' à la fin du nom de l'entité pour vérifier ensuite qu'elle a bien été modifiée
            $posted_data = [ 
                $name_field => $e->$name_field.' (CHANGED)'
            ];
        }
        
        // Quel id pour le matos (associé) ?
        // Par défaut (pour MaterielsController) $matos_id et $id sont identiques (confondus)
        $matos_id = $id;
        
        $entities_related_to_materiel = ['Documents', 'Emprunts', 'Suivis'];
        // Entité associée à un matériel ? (Suivis, Emprunts, Documents...)
        //if (!$IS_MATERIEL && $action!='index') {
        if ( in_array($entities_name, $entities_related_to_materiel) && $action!='index' ) {
            //if ($action=='add') $related_matos_id = $this->getNewEntityWithAllMandatoryFields()->materiel_id;
            //$related_matos_id = $this->getMaterielsController()->_getEntityIdOkForTesting();
            $actions_with_related_matos_id = ['add'];
            //if ($c->name == 'Documents') $actions_with_related_matos_id[] = ['admission','sortie'];
            if ($c->getName() == 'Documents') $actions_with_related_matos_id[] = ['admission','sortie'];
            if (in_array($action, $actions_with_related_matos_id)) {
                // id = celui du matos associé
                $id = MaterielsControllerTest::_getEntityIdOkForTesting();
                if ($action == 'add') $posted_data['materiel_id'] = $id;
                $matos_id = $id;
            }
            else {
                // id = celui de l'entité (suivi, emprunt, document....)
                // matos_id = celui associé à l'entité
                $e = $this->_getController()->getEntity($id);
                if ($e->has('materiel_id')) $matos_id = $e->materiel_id;
            }
        }
        
        $access_condition = $c->getAccessConditionForActionAndRole($action,$role_short);
        //debug("Condition d'accès pour l'action $action et le rôle $role_short"); debug($access_condition);
        
        /*
         *
         * Test de l'accès à l'action : 3 GRANDS CAS POSSIBLES
         *
         */
        
        // Force le rechargement des materiels car ils vont être modifiés par les tests
        AppController::forceReload();
        
        /*
         if ($access_condition==='default') {
         debug("is default");
         }
         */
        // 1) ACCÈS AUTORISÉ
        if ($access_condition===0) {
            $this->d("is 0");
            //if ($this->DEBUG) {debug($action); debug($id)};
            $this->_testExecActionForId($action,$id,$getpost,$posted_data,true,$nbentities);
        }
        // 2) ACCÈS REFUSÉ
        elseif ($access_condition===-1) {
            $this->d("is -1");
            $this->_testExecActionForId($action,$id,$getpost,$posted_data,false,$nbentities);
        }
        // 3) ACCÈS AUTORISÉ SOUS CONDITION
        // ATTENTION : toujours tester le cas où ca marche (AUTORISÉ) en dernier
        // Utile pour des actions telles que 'delete' !!!
        elseif (is_array($access_condition)) {
            $this->d("is array");
            $access_condition_on_status = $access_condition[0];
            $access_condition_on_belonging = $access_condition[1];
            // 4 cas possibles :
            /*
             * a) [0,0] => idem cas 1) ci-dessus => accès autorisé (avec matos quelconque)
             */
            //if ($access_condition_on_status===0 && $access_condition_on_belonging===0) {
            if ([$access_condition_on_status,$access_condition_on_belonging]===[0,0]) {
                $this->d("- is [0,0] => equivaut à 0");
                $this->_testExecActionForId($action,$id,$getpost,$posted_data,true,$nbentities);
            }
            /*
             * b) [0,1] => on ne teste que l'appartenance, soit 2 tests :
             *      - (1) on crée un materiel (T,F) => on teste que l'accès être bien AUTORISÉ
             *      - (2) on crée un materiel (F,T) => on teste que l'accès être bien REFUSÉ, SAUF si le profil est RESP (AUTORISÉ)
             */
            elseif ([$access_condition_on_status,$access_condition_on_belonging]===[0,1]) {
                $this->d("- is [0,1]");
                //$m = $this->Materiels->get($id);
                // - (1) (T,F)
                $this->_updateMatosBelongingToCurrentUserAndInSameGroup($matos_id, true, false);
                $this->_testExecActionForId($action,$id,$getpost,$posted_data,true,$nbentities);
                
                // - (2) (F,T)
                $this->_updateMatosBelongingToCurrentUserAndInSameGroup($matos_id, false, true);
                // ok seulement pour RESP
                $this->_testExecActionForId($action,$id,$getpost,$posted_data,$this->USER_IS_RESP(),$nbentities);
            }
            /*
             * c) [*,0] => on ne teste que le statut, soit 2 tests :
             *      - (1) on crée un matériel qui A le statut demandé => on teste que l'accès être bien AUTORISÉ
             *      - (2) on crée un matériel qui n'A PAS le statut demandé => on teste que l'accès être bien REFUSÉ
             */
            elseif (is_string($access_condition_on_status) && $access_condition_on_belonging===0) {
                $this->d("- is [*,0]");
                //$m = $this->Materiels->get($id);
                // - (1) statut KO
                /*
                 $this->_updateMatosWithStatusAs($id, $access_condition_on_status, false);
                 $this->_testExecActionForId($action,$id,false);
                 */
                // - (2) statut OK
                $this->_updateMatosWithStatusAs($matos_id, $access_condition_on_status, true);
                $this->_testExecActionForId($action,$id,$getpost,$posted_data,true,$nbentities);
            }
            /*
             * d) [*,1] => Cas général, on teste les 2 conditions, soit 2x2=4 tests
             * Les 2 conditions (A-statut et B-appartenance) doivent être vérifiées : A && B (c'est un AND)
             * On va donc tester le couple (A,B), ce qui nous donne 3 cas :
             */
            elseif (is_string($access_condition_on_status) && $access_condition_on_belonging===1) {
                $this->d("- is [*,1]");
                //$m = $this->Materiels->get($id);

                // (1) (T,F) => accès REFUSÉ
                $this->d("(1. TF) should be KO");
                $this->_updateMatosWithStatusAs($matos_id, $access_condition_on_status, true);
                $this->_updateMatosBelongingToCurrentUserAndInSameGroup($matos_id, false, false);
                $m = $this->Materiels->get($matos_id);
                $this->d("(1) id is $id, materiel (id, statut, nom_responsable, nom_createur) is : $m->id, $m->status, $m->nom_responsable, $m->nom_createur");
                $this->_testExecActionForId($action,$id,$getpost,$posted_data,false,$nbentities);

                // (2) (F,T) => accès REFUSÉ
                $this->d("(2. FT) should be KO");
                //debug($access_condition_on_status);
                $this->_updateMatosWithStatusAs($matos_id, $access_condition_on_status, false);
                $this->_updateMatosBelongingToCurrentUserAndInSameGroup($matos_id, true, true);
                $m = $this->Materiels->get($matos_id);
                $this->d("(2) id is $id, materiel (id, statut, nom_responsable, nom_createur) is : $m->id, $m->status, $m->nom_responsable, $m->nom_createur");
                $this->_testExecActionForId($action,$id,$getpost,$posted_data,false,$nbentities);
                
                // (3) (F,F) => accès REFUSÉ => inutile de tester ce cas
                // (4) (T,T) => accès AUTORISÉ
                $this->d("(3. TT) should be OK");
                $this->_updateMatosWithStatusAs($matos_id, $access_condition_on_status, true);
                $this->_updateMatosBelongingToCurrentUserAndInSameGroup($matos_id, true, $this->USER_IS_RESP());
                $m = $this->Materiels->get($matos_id);
                $this->d("(3) id is $id, materiel (id, statut, nom_responsable, nom_createur) is : $m->id, $m->status, $m->nom_responsable, $m->nom_createur");
                $this->_testExecActionForId($action,$id,$getpost,$posted_data,true,$nbentities);
            }
            else throw new \ErrorException("La conditions d'accès $access_condition est incorrecte (1)");
        }
        else throw new \ErrorException("La conditions d'accès $access_condition est incorrecte (2)");
    } // testAuthorizationsForAllActionsOfController()
    

    protected function _updateMatosStatusAndBelongingWith($id, $status, $BELONGS=true, $SAMEGROUP=true) {
        $this->_updateMatosWithStatusAs($id, $status);
        $this->_updateMatosBelongingToCurrentUserAndInSameGroup($id, $BELONGS, $SAMEGROUP);
    }
        
    private function _updateMatosWithStatusAs($id, $status, $WITH_SAME_STATUS=true, $DO_SAVE=true) {
        //$mc = new MaterielsController();
        $mc = $this->getMaterielsController();
        $u = $this->getCurrentUser();
        $m = $this->getMaterielsTable()->get($id);
        //$m = $this->getMateriel($id);
        //debug("statut:"); debug($status);
        $NOT = FALSE;
        
        // Statut commence par 'NOT' ?
        if (strpos($status,'NOT ') === 0) {
            $NOT = TRUE;
            // on prend le statut après le "NOT " :
            $status = substr($status,4);
        }
        
        // Condition de statut complexe ? (normalement de la forme "STATUS &&_ou_|| autre_condition") 
        // => pour simplifier, on ne tient compte que du STATUS (donc le 1er argument)
        if ( (strpos($status,'&&')!==FALSE) || (strpos($status,'||')!==FALSE) ) {
            //debug("statut complexe '$status'");
            $status = explode(' ', $status)[0];
            //$status = $status[0];
            //debug("=> simplifié en '$status'");
        }
        
        //if ($mc->isStatus($status)) {
        if (! $mc->isStatus($status)) 
            // On a un cas spécial, du style 'conf.hasPrinter' (sans statut avant, alors que ça pourrait être "VALIDATED && conf.hasPrinter")
            // Pour le moment, on ne le traite pas...
            //throw new \Exception("Le statut du matériel est incorrect !!!");
            ;
        else {
            /*
             $m->status = $WITH_SAME_STATUS ? $status : $mc->getNextStatusFrom($status);
             if ($NOT) $m->status = $mc->getNextStatusFrom($status);
             */
            // MEME statut (par défaut) ?
            $m->status = $status;
            //debug("bef: $m->status");
            // ou STATUT différent ?
            if ( ($NOT && $WITH_SAME_STATUS) || (!$NOT && !$WITH_SAME_STATUS) ) $m->status = $mc->getNextStatusFrom($status);
            //debug("after: $m->status");
            
            // On sauvegarde le matos modifié
            //if (! $this->Materiels->save($m))
            //if ($DO_SAVE && !$this->getMaterielsTable()->save($m))
            if ($DO_SAVE)
                if (! $this->getMaterielsTable()->save($m) )
                    throw new \Exception("La sauvegarde du materiel modifié ne se fait pas !!!");
            //return $m;
        }
        //else throw new \Exception("Le statut du matériel est incorrect !!!");
        
        return $m;
    }
    
    
    private function _updateMatosBelongingToCurrentUserAndInSameGroup($id, $BELONGS=true, $SAMEGROUP=true, $DO_SAVE=true) {
        $u = $this->getCurrentUser();
        $m = $this->Materiels->get($id);

        // BELONGS ?
        $user_name = $BELONGS ? $u->nom : 'inconnu'; // 'user5 USER'
        $m->nom_responsable = $user_name;
        $m->nom_createur = $user_name;
        
        // SAME GROUP ?
        $WRONG_GROUP_ID = 2;
        $groupe_thematique_id = $SAMEGROUP ? $u->groupes_thematique_id : $WRONG_GROUP_ID;
        $groupe_metier_id = $SAMEGROUP ? $u->groupes_metier_id : $WRONG_GROUP_ID;
        $m->groupes_thematique_id = $groupe_thematique_id;
        $m->groupes_metier_id = $groupe_metier_id;
        //debug($matos);
        
        // On sauvegarde le matos modifié
        if ( $DO_SAVE && !$this->Materiels->save($m) )
            throw new \Exception("La sauvegarde du materiel modifié ne se fait pas !!!");
        
        return $m;
    }
    
    
    //private function _testExecActionForId($action,$id,$SHOULD_BE_SUCCESS) {
    private function _testExecActionForId($action,$id,$getpost,$posted_data,$SHOULD_BE_SUCCESS,$nbentities) {
        //debug($this->_flashMessages);
        
        //$entities = 'materiels';
        //$entities_name = strtolower($this->getEntitiesName());
        //$entities_name = Inflector::dasherize($this->getEntitiesName());
        $entities_name = $this->getEntitiesName();
        //$entities_name = $this->getEntitiesName();
        //$IS_MATERIEL = ($entities_name=='materiels');
        $IS_MATERIEL = ($entities_name=='Materiels');
        
        //$getpost = in_array($action, ['add','add_by_copy','edit','delete', 'statusCreated']) ? 'post' : 'get';
    //$getpost = in_array($action, ['view', 'index', 'find', 'printLabelRuban']) ? 'get':'post';
        //$getpost = in_array($action, ['view', 'index', 'find']) ? 'get':'post';
        //$this->post("/materiels/$action/$matos_id");
        // Si 'add' ou 'index' => pas d'id
        //debug("0"); debug($_SESSION);
        
        //$data = ($action=='add') ? $this->newMaterielWithAllMandatoryFields : [];
        //$data = ($action=='add') ? $this->getNewEntityWithAllMandatoryFields() : [];
    /*
        $data = [];
        if ($action=='add') {
            $data = $this->getNewEntityWithAllMandatoryFields();
            if ($entities_name != 'materiels') $data['materiel_id'] = $id;
        }
    */
        $exec_action = $action;
        if ($action=='add_by_copy') $exec_action='add';
        
        $full_action = "/$entities_name/$exec_action";
        //if (! in_array($action, ['add', 'index'])) $full_action .= "/$id";
        if (! ($action=='index' || ($action=='add' && $IS_MATERIEL)) ) $full_action .= "/$id";
        $this->d("Execution action : \$this->$getpost($full_action, data); (avec data=)"); $this->d($posted_data);
        // GET
        if ($getpost=='get')
            $this->$getpost($full_action);
        // POST
        else
            $this->$getpost($full_action, $posted_data);
            
        /*
         if ($SUCCESS) $this->assertNoRedirect();
         else $this->assertRedirect();
         */
        //$this->get('/materiels/view/2');
        //$res = $this->_getBodyAsString();
        //$res = $this->_getBodyAsString(); debug($res);
        //debug($this->_flashMessages);
        //debug($_SESSION);
        //debug($flash_message);
        
        // Normalement, l'action devrait avoir été autorisée
        if ($SHOULD_BE_SUCCESS) {
            $flash_message = null;
            $this->assertResponseSuccess();
            //if ($entities_name=='materiels' && $action=='printLabelRuban') {
            if ($entities_name=='Materiels' && $action=='printLabelRuban') {
                /*
                 $this->assertFileResponse('inventirap_label.label', 'pas de fichier généré');
                 $this->assertHeader('Content-type', 'application/xml');
                 $this->assertHeader('Content-Type', 'application/xml');
                 */
                //var_dump($this->_getBodyAsString());
                $filename = 'inventirap_label.label';
                $this->assertHeader('Content-Disposition', 'attachment; filename="'.$filename.'"');
                //$this->assertHeader('Content-Disposition', "attachment; filename='$filename'");
                $msg_error = "L'action $action n'est pas autorisée (pour le role {$this->getUserRole()}) alors qu'elle devrait !!!";
                // - DLS :
                /*
                $this->assertResponseContains("ContinuousLabel", $msg_error);
                $this->assertResponseContains("<PaperOrientation>Landscape</PaperOrientation>", $msg_error);
                */
                // - DCD :
                $this->assertResponseContains("DYMOLabel", $msg_error);
                $this->assertResponseContains("19X7-TAPE BLACK/WHITE", $msg_error); // format 19mm
                $this->assertResponseContains("<Orientation>Landscape</Orientation>", $msg_error);
            }
            // GET
            if ($getpost == 'get') {
                $this->assertResponseOk();
                $this->assertResponseNotEmpty();
                $this->assertNoRedirect();
                if (isset($_SESSION['Flash'])) {
                    $flash_message = $_SESSION['Flash'];
                    //$this->assertEquals([], $flash_message);
                }
            }
            // POST
            else {
                // Nom de l'entité au singulier et en minuscule
                $entity_name = substr($entities_name,0,-1);
                $entity_name = $this->inflectEntityName($entity_name);
                //if ($entity_name=='materiel') $entity_name='matériel';
                $c = $this->_getController();
                $article = $c->getArticle();
                if ($entity_name=='domaine') $article='Le ';
                // "Le materiel/suivi/emprunt a bien été"
                $expected_flash_message = $article.$entity_name.' a bien été ';
                switch($action) {
                    case 'add':
                    case 'add_by_copy': 
                        $expected_flash_message .= 'ajouté';
                        // Vérifier la conséquence de l'action : 1 entité de plus
                        //debug("entities_name4 is $entities_name");
                        $this->_checkNbEntitiesInIndexViewIsAsExpected($entities_name, $nbentities+1);
                        break;
                    
                    case 'edit': 
                        $expected_flash_message .= 'modifié';
                        // Vérifier la conséquence de l'action : 1 entité de plus
                        $e = $c->getEntity($id);
                        $this->d("entity is"); $this->d($e);
                        $name_field = $c->getNameFieldLabel();
                        $this->assertTrue(strpos($e->$name_field,'(CHANGED)')!==false, "L'entité n'a pas été modifiée (action 'edit')");
                        break;
                        
                    case 'delete': 
                        $expected_flash_message .= 'supprimé';
                        // Vérifier la conséquence de l'action : 1 entité de moins
                        $this->_checkNbEntitiesInIndexViewIsAsExpected($entities_name, $nbentities-1);
                        break;
                    
                    case 'statusCreated': $expected_flash_message .= 'rétrogradé au statut CREATED'; break;
                    case 'statusValidated': $expected_flash_message .= 'validé'; break;
                    case 'statusTobearchived': $expected_flash_message = "La sortie d'inventaire a bien été demandée"; break;
                    case 'statusArchived': $expected_flash_message .= "archivé (sorti de l'inventaire)"; break;
                    
                    case 'setLabelIsPlaced':
                    case 'setLabelIsNotPlaced':
                        $verb = ($action=='setLabelIsPlaced') ? 'a' : "n'a pas";
                        $expected_flash_message = "Je prends note que l'étiquette $verb été collée sur le matériel"; break;
                }
                $flash_message = isset($_SESSION['Flash']) ? $_SESSION['Flash']['flash'][0]['message']:[];
                //if ($flash_message==null) $flash_message = [];
            }
            $this->d("(2) action $action, flash is:");$this->d($flash_message);
            /*
             * Test flash message
             * (https://book.cakephp.org/3/en/development/testing.html#testing-flash-messages)
             *
             * If you want to assert the presence of flash messages in the session and not the rendered HTML,
             * you can use enableRetainFlashMessages() in your tests
             * to retain flash messages in the session so you can write assertions :
             *
             * $this->enableRetainFlashMessages();
             */
            if ($flash_message) {
                // Assert a flash message in the 'flash' key.
                // Test message partiel
                $this->assertTrue(strpos($flash_message, $expected_flash_message) === 0, "Le message flash est incorrect : ($flash_message) au lieu de ($expected_flash_message)");
                // Test message complet
                $this->assertFlashMessage($expected_flash_message, 'flash');
                //$this->assertEquals($expected_flash_message, $flash_message);
            }
            
            //$this->assertResponseContains("Matos Test 2 (C)", "L'action $action n'est pas autorisée (pour le role {$this->getUserRole()}) !!!");
            // BUG !!!! pourquoi ça marche avec l'action 'view' ???
            //$this->assertResponseContains("Désolé, vous n&#039;êtes pas autorisé à accéder à cette zone", "(2)L'action $action n'est pas autorisée (pour le role {$this->getUserRole()}) !!!");
            //$this->assertResponseNotContains("Désolé, vous n", "L'action $action n'est pas autorisée (pour le role {$this->getUserRole()}) alors qu'elle le devrait !");
        }
        
        // Normalement, l'action devrait avoir été refusée
        else {
            // 1) Assert redirect
            //$this->assertRedirect(['controller' => 'Users', 'action' => 'login']);
            //$this->assertRedirect(['controller' => 'Pages', 'action' => 'home']);
            // OK
            //$this->assertResponseSuccess();
            $this->assertResponseEmpty();
            //if (!in_array($action,['add_by_copy'])) $this->assertResponseEmpty();
            //if ($getpost == 'get') $this->assertRedirect('/');
            // MARCHE PAS !!!
            //$this->disableErrorHandlerMiddleware();
            // OK
            //$this->assertResponseCode(302);

            // VOIR LE CONTENU DE LA REPONSE HTML
            //debug($this->_response);  // materiels/view/2
            
            if (!in_array($action,['edit','delete'])) $this->assertRedirect('/');
            //if (!in_array($action,['edit','delete'])) $this->assertRedirect('/materiels/view/2');
            //if (!in_array($action,['edit','delete'])) $this->assertRedirect('/pages/home');
            //if (!in_array($action,['edit','delete'])) $this->assertRedirect('/pages/display');
            //var_dump($this->headers['Location']); exit;
            
            // 2) Assert Flash error message (flash message in the 'flash' key)
            // Assert a flash messages uses the error element
            $this->assertFlashElement('Flash/error');
            $expected_flash_message = "Désolé, vous n'êtes pas autorisé à accéder à cette zone.";
            //var_dump($flash_message);
            // Test message complet
            //$this->assertFlashMessage($expected_flash_message);
            $this->assertFlashMessage($expected_flash_message, 'flash');
            $this->assertSession($expected_flash_message, 'Flash.flash.0.message');
            $flash_message = $_SESSION['Flash']['flash'][0]['message'];
            $this->assertEquals($expected_flash_message, $flash_message);
            //$this->assertTrue(strpos($flash_message, "Désolé, vous n'êtes pas") !== false);
            $this->assertTrue(strpos($flash_message, "Désolé, vous n'êtes pas") === 0);
            // BUG !!! why ???
            //$this->assertResponseContains("Désolé", "L'action $action est autorisée (pour le role {$this->getUserRole()}) alors qu'elle ne le devrait pas !");
        }
    } // _testExecActionForId()
    
    
    /*
     * Test générique de l'action delete (appelé par chaque controleur de test)
     */
    protected function _testActionDeleteEntity($role) {
        $action = 'delete';
        
        //$entities_name = strtolower($this->getEntitiesName());
        $entities_name = $this->getEntitiesName();
        
        $this->d("*******************");
        $this->d("    CONTROLEUR $entities_name, ACTION $action, ROLE $role");
        $this->d("*******************");
        
        $this->authAs($role);
        
        $nbentities = $this->getNbEntitiesExpectedInIndexView();
        $this->_checkNbEntitiesInIndexViewIsAsExpected($entities_name, $nbentities);
        
        $id = $this->_getEntityIdOkForTesting();
        $this->post("$entities_name/delete/$id");
        
        $nbentities--;
        $this->_checkNbEntitiesInIndexViewIsAsExpected($entities_name, $nbentities);
    }
    

}