Commit acabf49b authored by Jordan Breton's avatar Jordan Breton

Query side WIP + PHPMailer & htmlPurifier update

parent 44560f22
-- MySQL dump 10.13 Distrib 5.7.22, for Linux (x86_64)
--
-- Host: localhost Database: event_store
-- ------------------------------------------------------
-- Server version 5.7.22-0ubuntu0.16.04.1
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Current Database: `event_store`
--
CREATE DATABASE /*!32312 IF NOT EXISTS*/ `event_store_test` /*!40100 DEFAULT CHARACTER SET utf8 */;
USE `event_store_test`;
--
-- Table structure for table `aggregates`
--
DROP TABLE IF EXISTS `aggregates`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `aggregates` (
`id` binary(16) NOT NULL COMMENT 'UUID',
`type` varchar(255) DEFAULT NULL COMMENT 'Nom complet de l''agrégat',
`version` int(10) unsigned DEFAULT NULL COMMENT 'Version du dernier événement associé à cet aggrégat',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Aggrégat concerné par des événements';
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `commands`
--
DROP TABLE IF EXISTS `commands`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `commands` (
`id` binary(16) NOT NULL COMMENT 'UUID',
`type` varchar(255) DEFAULT NULL COMMENT 'Nom complet de la commande',
`generation_date` timestamp(6) NULL DEFAULT NULL COMMENT 'Date de génération de la commande',
`processing_date` timestamp(6) NULL DEFAULT NULL COMMENT 'Date d''éxécution de la commande',
`writing_date` timestamp(6) NULL DEFAULT NULL COMMENT 'Date d''écriture de la commande en base de données',
`data` blob COMMENT 'Données transportées par la commande (langage agnostique)',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `events`
--
DROP TABLE IF EXISTS `events`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `events` (
`id` binary(16) NOT NULL COMMENT 'UUID',
`type` varchar(255) DEFAULT NULL COMMENT 'Nom de l''événement',
`data` blob COMMENT 'Données de l''événement (langage agnostique)',
`version` int(10) unsigned DEFAULT NULL COMMENT 'Version de l''événement (défini au niveau de l''aggrégat) ',
`generation_date` timestamp(6) NULL DEFAULT NULL COMMENT 'Date de génération de l''événement',
`writing_date` timestamp(6) NULL DEFAULT NULL COMMENT 'Date d''insertion en base de données',
`aggregates_id` binary(16) DEFAULT NULL COMMENT 'Aggrégat relatif à l''événement courant',
`commands_id` binary(16) DEFAULT NULL COMMENT 'Commande ayant entraîné la génération de l''événement',
PRIMARY KEY (`id`),
KEY `fk_events_aggregates_idx` (`aggregates_id`),
KEY `fk_events_commands1_idx` (`commands_id`),
CONSTRAINT `fk_events_aggregates` FOREIGN KEY (`aggregates_id`) REFERENCES `aggregates` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION,
CONSTRAINT `fk_events_commands1` FOREIGN KEY (`commands_id`) REFERENCES `commands` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Evenements';
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Table structure for table `snapshots`
--
DROP TABLE IF EXISTS `snapshots`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `snapshots` (
`id` binary(16) NOT NULL COMMENT 'UUID',
`aggregates_id` binary(16) DEFAULT NULL COMMENT 'UUID de l''aggrégat',
`data` blob COMMENT 'donnée sérialisée de l''état de l''aggrégat (langage agnostique)',
`version` int(10) unsigned DEFAULT NULL COMMENT 'Version du snapshot',
`generation_date` timestamp(6) NULL DEFAULT NULL COMMENT 'Date de génération du snapshot',
`writing_date` timestamp(6) NULL DEFAULT NULL COMMENT 'Date d''écriture du snapshot en base de données',
PRIMARY KEY (`id`),
KEY `fk_snapshots_aggregates1_idx` (`aggregates_id`),
CONSTRAINT `fk_snapshots_aggregates1` FOREIGN KEY (`aggregates_id`) REFERENCES `aggregates` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='Représente un snapshot de de l''état d''un aggrégat à une version déterminée';
/*!40101 SET character_set_client = @saved_cs_client */;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
-- Dump completed on 2018-06-07 17:24:24
{
//Contient la liste des environements de test disponibles
"environments" : {
"name" : "full\\class\\Name"
},
//Contient la liste des launchers de tests disponibles
"launchers" : {
"name" : "full\\class\\Name"
},
//Liste des sequences de tests disponibles.
//Chaque séquence est définie dans un launcher particulier.
//Chaque sequence est lancée dans un environnement séparé des autres séquences (nouveau processus)
//Si des environnements de test sont définis dans une séquence, ils seront utilisés tour à tour dans des séquences
//de tests séparées.
"sequences" : {
//Nom du launcher utilisé pour les sequences
"launcherName" : {
//Type de test (integration,unit,...)
"type" : {
//Nom de la séquence
"name" : {
//(optionnel) Description de la séquence
"description" : "Test sequence description",
//(optionnel) Liste des environnements à utiliser pour la séquence de tests (surtout pous les tests d'intégration)
"environments" : ["environmentName1","environmentName2"],
//Chemins vers les dossiers contenant les tests.
//Les chemins relatifs sont résolus suivant ROOT."/tests/{launcherName}/{testType}/{relativePath}
"paths" : [
"path/to/test/folder",
"path/to/another/test/folder2"
]
}
}
}
}
}
\ No newline at end of file
{
"environments" : {
"Default" : "wfw\\cli\\tester\\contexts\\DefaultTestsEnvironment"
},
"launchers" : {
"PHPUnit" : "wfw\\cli\\tester\\launchers\\PHPUnit\\PHPUnitTestsLauncher"
},
"launcherCommands" : {
"PHPUnit" : {
"--coverage-html" : "/var/www/phpunit"
}
},
"sequences" : {
"PHPUnit" : {
"unit" : {
"modules" : {
"users" : {
"description" : "Users module (wfw\\engine\\package\\users\\*)",
"codePath" : "engine/package/users"
},
"news" : {
"description" : "News module (wfw\\engine\\package\\news\\*)",
"codePath" : "engine/package/news"
},
"contact" : {
"description" : "Contact module (wfw\\engine\\package\\management\\*)",
"codePath" : "engine/package/contact"
}
}
},
"integration" : {
"modules" : {
"contact" : {
"description" : "Integration contact module (wfw\\engine\\package\\contact\\*)",
"environments" : [
"Default"
],
"codePath" : "engine/package/contact"
},
"news" : {
"description" : "Integration news module (wfw\\engine\\package\\news\\*)",
"environments" : [
"Default"
],
"codePath" : "engine/package/news"
},
"users" : {
"description" : "Integration users module (wfw\\engine\\package\\users\\*)",
"environments" : [
"Default"
],
"codePath" : "engine/package/users"
}
}
}
}
}
}
\ No newline at end of file
{
"server" : {
"databases": {
"default": {
"host": "localhost",
"database": "event_store_test",
"login": "tester",
"password": "test"
}
},
"msserver" : {
"addr" : "daemons/modelSupervisor/data/ModelSupervisor.socket",
"db" : "test",
"login" : "tester",
"password" : "test"
}
}
}
\ No newline at end of file
<?php
namespace wfw\cli\tester\contexts;
use Dice\Dice;
use wfw\engine\core\app\context\WebAppContext;
use wfw\engine\core\app\context\IWebAppContext;
use wfw\engine\core\app\WebApp;
use wfw\engine\core\security\rules\RequireAuthentification;
use wfw\engine\core\security\rules\UserTypeBasedAccess;
use wfw\engine\core\security\rules\ValidToken;
use wfw\engine\lib\PHP\errors\IllegalInvocation;
use wfw\engine\package\general\layouts\blank\BlankLayout;
use wfw\engine\package\users\domain\types\Client;
/**
* Contexte de test basé sur Dice.
*
* Ce contexte par défaut n'est compatible qu'avec les IWebAppContext dont la classe ROOT possède une
* propriété _dice dans laquelle une instance de Dice est présente avec une méthode create(string class,array $args)
*/
class DefaultTestsEnvironment implements ITestsEnvironment {
/** @var null|WebAppContext $_context */
private $_context;
/** @var Dice $_dice */
private $_dice;
/**
* DefaultTestsEnvironment constructor.
*/
public function __construct(){}
/**
* Initialise ou réinitialise l'environnement de tests avec les arguments spécifiés.
*
* @param array $args Paramètres du context à créer.
*/
public function init(array $args = []): void {
$this->_context = $this->createWebAppContext($args);
$class = get_class($this->_context);
while(get_parent_class($class)){
$class = get_parent_class($class);
}
$reflect = new \ReflectionClass($class);
$diceProperty = $reflect->getProperty("_dice");
$diceProperty->setAccessible(true);
$this->_dice = $diceProperty->getValue($this->_context);
}
/**
* Crée un IWebAppContext
*
* @param array $args
* @return WebAppContext
*/
private function createWebAppContext(array $args = []):IWebAppContext{
$class = $args["context"] ?? WebAppContext::class;
if(!is_a($class,IWebAppContext::class,true))
throw new \InvalidArgumentException("$class doesn't implements ".IWebAppContext::class);
return new $class(...$this->initContextArgs($args));
}
/**
* @param array $args Arguments
* @return array
*/
private function initContextArgs(array $args=[]):array{
$globals = $args["globals"] ?? [];
$globals["_SERVER"]=array_merge([
"HTTP_ACCEPT_LANGUAGE" => "fr-FR,fr;q=0.9,en-US;q=0.8,en;q=0.7",
"REMOTE_ADDR" => "127.0.0.1",
"PATH_INFO" => "/",
"REQUEST_URI" => "",
"REQUEST_METHOD" => "GET"
],$globals["_SERVER"]??[]);
return [
$args["defaultLayoutClass"] ?? BlankLayout::class,
$args["errorViewPath"] ?? null,
$args["ajaxViewPath"] ?? null,
$args["connections"] ?? [],
$args["langs"] ?? ["fr" => [
dirname(__DIR__,3)."/engine/config/lang/fr.lang.json",
dirname(__DIR__,3)."/site/config/lang/fr.lang.json"
]],
$args["accessRules"] ?? [
RequireAuthentification::class => [
[
//ATTENTION : avec le fonctionnement actuel, on met les régles
//les plus précises en premier, mais les régles
//suivante doivent permettre le package aussi pour que ça fonctionne !
//Heureusement, très peu de package disposent de certaines méthodes publique/privées,
//ils sont en générale purement privés, ou purement publiques. Rarement les deux.
//On autorise publiquement les actions suivantes pour le package users :
// FrogottentPassword,ResendForgottenPasswordMail
// Register,ConfirmRegistration
// ChangeMailConfirmation
// Login
"^users/(?!(L|l)ogin|(F|f)orgottenPassword|(C|c)onfirmUserRegistration|(R|r)esendForgottenPassword|(R|r)esetPassword|(R|r)egister|(C|c)angeMailConfirmation).*$",
//Tous les packages autres que web et lang sont protégés et requièrent
// une authentification.
//users est là pour permettre à la régle précédente de s'appliquer
"^(?!web|lang|users).*$"
]
],
ValidToken::class => [],
UserTypeBasedAccess::class => [
[
Client::class => [
//On autorise toutes les actions du package users sauf la partie admin
"^users/(?!admin/).*$"
]
]
]
],
$args["diceRules"] ?? [],
$globals,
$args["confFiles"] ?? [
dirname(__DIR__,3)."/engine/config/conf.json",
dirname(__DIR__,3)."/site/config/conf.json",
dirname(__DIR__,2)."/tester/config/conf.tests.json"
],
$args["baseUrl"] ?? null
];
}
/**
* Permet de créer n'importe quel objet en utilisant Dice initialisé par le context courant.
*
* Si une WebApp est demandée, $params devra correspondre aux paramètres de son context. Si aucun
* paramètre n'est passé, le contexte courant sera utilisé, sinon un nouveau contexte est créé.
*
* Si un IWebAppContext est demandé sans paramète, le context courant est retourné, sinon
* un nouveau context avec les paramètres demandés est créé.
*
* @param string $obj Classe de l'objet à instancier
* @param array $params Liste des paramètres à passer au constructeur
* @return mixed
*/
public function create(string $obj, array $params = []){
if(!$this->_context) throw new IllegalInvocation(
"Test environment havn't been initialized ! Please call init() at least once !"
);
if($obj === WebApp::class){
if(count($params) === 0) return new WebApp($this->_context);
else return new WebApp($this->createWebAppContext($params));
}else if(is_a($obj,IWebAppContext::class,true)){
if(count($params) === 0) return $this->_context;
else{
$params["context"] = $obj;
return $this->createWebAppContext($params);
}
}else return $this->_dice->create($obj,$params);
}
/**
* Retourne le webAppContext courant
*
* @return IWebAppContext
*/
public function getAppContext(): IWebAppContext{
return $this->_context;
}
}
\ No newline at end of file
<?php
namespace wfw\cli\tester\contexts;
use wfw\engine\core\app\context\IWebAppContext;
/**
* Contexte de tests
*/
interface ITestsEnvironment {
/**
* Initialise ou réinitialise l'environnement de tests avec les arguments spécifiés
* @param array $args
*/
public function init(array $args=[]):void;
/**
* @param string $obj Classe de l'objet à instancier
* @param array $params Liste des paramètres à passer au constructeur
* @return mixed
*/
public function create(string $obj, array $params=[]);
/**
* Retourne le webAppContext courant
* @return IWebAppContext
*/
public function getAppContext():IWebAppContext;
}
\ No newline at end of file
<?php
namespace wfw\cli\tester\contexts;
use wfw\cli\backup\LocalMysqlDbBackup;
use wfw\daemons\modelSupervisor\client\IMSClient;
use wfw\daemons\modelSupervisor\client\MSInstanceAddrResolver;
use wfw\daemons\modelSupervisor\client\MSClient;
use wfw\engine\core\conf\IConf;
use wfw\engine\core\errors\handlers\DefaultErrorHandler;
use wfw\engine\lib\PHP\errors\IllegalInvocation;
/**
* Singleton-like disgusting thing. Should only be used inside integration tests, never outside.
* It allow to retrieve a pre-configured context inside those tests.
* For the fool who read this : Please, don't write Singletons or singleton-like terrible things.
* Global states are almost ALWAYS bad ideas.
* If you do so, don't use them.
* Never.
* (And prey for those who don't understand why they're evil)
*
* This crappy class is used and initialized by the cli/tester to create an environment reachable
* from all integration tests.
* It allow some fantasy like manage a test DB.
*/
final class TestEnv {
/** @var null|ITestsEnvironment $instance */
private static $instance;
/**
* Pas de constructeur pour cette classe.
*/
private function __construct(){}
/**
* @param ITestsEnvironment $environment Env that we want reachable from everywhere in integration test.
*/
public static function init(ITestsEnvironment $environment):void{
$_SESSION = []; //SetUp the $_SESSION var as is doesn'nt exists in CLI mode.
if(!self::$instance) self::$instance = $environment;
else throw new IllegalInvocation("This test environment have already been initialized !");
}
/**
* @return ITestsEnvironment
*/
public static function get():ITestsEnvironment{ return self::$instance; }
/**
* Supprime la base de données de tests courante et recharge une nouvelle DB vide.
*
* @param string $sqlDbFile
* @param string $dbName
* @param null|string $host
* @param null|string $user
* @param null|string $password
*/
public static function restoreEmptyTestSqlDb(
?string $sqlDbFile=null,
string $dbName = "event_store_test",
?string $host = null,
?string $user = null,
?string $password = null
):void{
if(is_null($sqlDbFile)) $sqlDbFile = dirname(__DIR__,2)."/tester/config/clean_db.sql";
/** @var IConf $conf */
$conf = self::get()->getAppContext()->getConf();
$sqlBackup = new LocalMysqlDbBackup(
$sqlDbFile,
$host ?? $conf->getString("server/databases/default/host"),
$dbName,
$user ?? $conf->getString("server/databases/default/login"),
$password ?? $conf->getString("server/databases/default/password")
);
$sqlBackup->load();
}
/**
* Reconstruit tous les models du MSServer de Test
*
* @param string $resolverAddr Adresse de la socket du MSServerPool
* @param string $instanceName Nom de l'instance
* @param string $user Login de l'utilisateur test
* @param string $password Password de l'utilisateur de test
*/
public static function restoreModels(
?string $resolverAddr = null,
string $instanceName = "test",
string $user = "tester",
string $password = "test"
):void{
if(is_null($resolverAddr))
$resolverAddr = dirname(__DIR__,3)."daemons/modelSupervisor/data/ModelSupervisor.socket";
$client = self::createMSClient(
$resolverAddr,
$instanceName,
$user,
$password
);
$client->login();
$client->rebuildAllModels();
$client->logout();
}
/**
* Crée une connexion à un MSWriterClient de test
*
* @param string $resolverAddr Adresse de la socket du MSServerPool
* @param string $instanceName Nom de l'instance
* @param string $user Login de l'utilisateur test
* @param string $password Password de l'utilisateur de test
* @throws \wfw\daemons\modelSupervisor\client\errors\MSClientFailure
* @return IMSClient
*/
public static function createMSClient(
?string $resolverAddr = null,
string $instanceName = "test",
string $user = "tester",
string $password = "test"
):IMSClient{
if(is_null($resolverAddr))
$resolverAddr = dirname(__DIR__,3)."/daemons/modelSupervisor/data/ModelSupervisor.socket";
$instanceAddr = (new MSInstanceAddrResolver($resolverAddr))->find($instanceName);
$client = new MSClient($instanceAddr, $user, $password);
return $client;
}
/**
* Restaure le handler sans warning (permet à code coverage de fonctionner)
*/
public static function restoreErrorHandler():void{
(new DefaultErrorHandler(false))->handle();
}
}
\ No newline at end of file
<?php
namespace wfw\engine\core\session;
use wfw\engine\lib\PHP\types\UUID;
use wfw\engine\package\general\domain\Email;
use wfw\engine\package\users\data\model\DTO\User;
use wfw\engine\package\users\domain\Login;
use wfw\engine\package\users\domain\Password;
use wfw\engine\package\users\domain\settings\InMemoryUserSettings;
use wfw\engine\package\users\domain\states\EnabledUser;
use wfw\engine\package\users\domain\types\Admin;
function session_start(){
$_SESSION=[];
$_SESSION["csrfToken"] = "falseToken";
$_SESSION["user"] = new User(
new UUID(UUID::V4,"e9107d64-ecb8-46cf-a98d-332c1b1a904a"),
new Login("test"),
new Password("testtest"),
new Email("email@email.fr"),
new InMemoryUserSettings(),
new EnabledUser(),
new Admin(),
''
);
};
function session_destroy(){
$_SESSION=[];
}
\ No newline at end of file
<?php
namespace wfw\cli\tester\launchers;
/**
* Sequence de tests
*/
interface ITestSequence {
/**
* Permet de lancer la séquence de tests
*/