diff --git a/.gitignore b/.gitignore index 52d686de27..700df3aa03 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ /client/css/violet.css /client/css/violet-vertical.css /tests/unit/testData/cache/* +/tests/integration/config.php composer.phar vendor/ /custom/Espo/Custom/* \ No newline at end of file diff --git a/tests/integration/Core/BaseTestCase.php b/tests/integration/Core/BaseTestCase.php new file mode 100644 index 0000000000..852849f32c --- /dev/null +++ b/tests/integration/Core/BaseTestCase.php @@ -0,0 +1,104 @@ +espoTester->getApp(true, $userName, $password); + } + + return $this->espoTester->getApp(true); + } + + /** + * Get Espo Application + * + * @return \Espo\Core\Application + */ + protected function getApp() + { + return $this->espoApp; + } + + /** + * Get Espo container + * + * @return \Espo\Core\Container + */ + protected function getContainer() + { + return $this->getApp()->getContainer(); + } + + protected function setUp() + { + $params = array( + 'dataFile' => $this->dataFile, + 'pathToFiles' => $this->pathToFiles, + 'className' => get_class($this), + ); + + $this->espoTester = new Tester($params); + $this->espoTester->initialize(); + $this->espoApp = $this->loadApp($this->userName, $this->password); + } + + protected function tearDown() + { + $this->espoTester->terminate(); + $this->espoTester = NULL; + $this->espoApp = NULL; + } +} diff --git a/tests/integration/Core/DataLoader.php b/tests/integration/Core/DataLoader.php new file mode 100644 index 0000000000..c83e6ba177 --- /dev/null +++ b/tests/integration/Core/DataLoader.php @@ -0,0 +1,168 @@ +app = $app; + + $config = $this->getContainer()->get('config'); + $this->passwordHash = new \Espo\Core\Utils\PasswordHash($config); + } + + protected function getApp() + { + return $this->app; + } + + protected function getContainer() + { + return $this->app->getContainer(); + } + + protected function getPasswordHash() + { + return $this->passwordHash; + } + + public function loadData($dataFile) + { + if (!file_exists($dataFile)) { + return; + } + + $fullData = include($dataFile); + + //sort $fullData ??? + + foreach ($fullData as $type => $data) { + $methodName = 'load' . ucfirst($type); + if (!method_exists($this, $methodName)) { + throw new \Exception('DataLoader: Data type is not supported in dataFile ['.$dataFile.'].'); + } + + $this->$methodName($data); + } + } + + public function loadFiles($path) + { + try { + $fileManager = $this->getContainer()->get('fileManager'); + $fileManager->copy($path, '.', true); + } catch (Exception $e) { + throw new \Exception('Error loadFiles: ' . $e->getMessage()); + } + } + + protected function loadEntities(array $data) + { + $entityManager = $this->getContainer()->get('entityManager'); + + foreach($data as $entityName => $entities) { + + foreach($entities as $entityData) { + $entity = $entityManager->getEntity($entityName, $entityData['id']); + if (empty($entity)) { + $entity = $entityManager->getEntity($entityName); + } + + foreach($entityData as $field => $value) { + if ($field == "password" && $entityName == "User") { + $value = $this->getPasswordHash()->hash($value); + } + $entity->set($field, $value); + } + + try { + $entityManager->saveEntity($entity); + } catch (\Exception $e) { + throw new \Exception('Error loadEntities: ' . $e->getMessage() . ', ' . print_r($entityData, true)); + } + } + } + } + + protected function loadConfig(array $data) + { + if (empty($data)) { + return; + } + + $config = $this->getContainer()->get('config'); + $config->set($data); + + try { + $config->save(); + } catch (\Exception $e) { + throw new \Exception('Erro loadConfig: ' . $e->getMessage()); + } + } + + protected function loadPreferences(array $data) + { + $entityManager = $this->getContainer()->get('entityManager'); + + foreach ($data as $userId => $params) { + $entityManager->getRepository('Preferences')->resetToDefaults($userId); + $preferences = $entityManager->getEntity('Preferences', $userId); + $preferences->set($params); + + try { + $entityManager->saveEntity($preferences); + } catch (\Exception $e) { + throw new \Exception('Error loadPreferences: ' . $e->getMessage()); + } + } + } + + protected function loadSql(array $data) + { + if (empty($data)) { + return; + } + + $pdo = $this->getContainer()->get('entityManager')->getPDO(); + + foreach ($data as $sql) { + try { + $pdo->query($sql); + } catch (Exception $e) { + throw new \Exception('Error loadSql: ' . $e->getMessage() . ', sql: ' . $sql); + } + } + } +} \ No newline at end of file diff --git a/tests/integration/Core/Tester.php b/tests/integration/Core/Tester.php new file mode 100644 index 0000000000..c7ecc32c01 --- /dev/null +++ b/tests/integration/Core/Tester.php @@ -0,0 +1,158 @@ +params = $this->normalizeParams($params); + } + + protected function normalizeParams(array $params) + { + $namespaceToRemove = 'tests\\integration\\Espo'; + $classPath = preg_replace('/^'.preg_quote($namespaceToRemove).'\\\\(.+)Test$/', '${1}', $params['className']); + + if (!isset($params['dataFile'])) { + $params['dataFile'] = realpath($this->testDataPath) . '/' . str_replace('\\', '/', $classPath) . '.php'; + } + + if (!isset($params['pathToFiles'])) { + $params['pathToFiles'] = realpath($this->testDataPath) . '/' . str_replace('\\', '/', $classPath); + } + + return $params; + } + + protected function getParam($name, $returns = null) + { + if (isset($this->params[$name])) { + return $this->params[$name]; + } + + return $returns; + } + + public function getApp($reload = false, $userName = null, $password = null) + { + if (!isset($this->app) || $reload) { + $this->app = new \Espo\Core\Application(); + $auth = new \Espo\Core\Utils\Auth($this->app->getContainer()); + + if (isset($userName) && isset($password)) { + $auth->login($userName, $password); + } else { + $auth->useNoAuth(); + } + } + + return $this->app; + } + + protected function getDataLoader() + { + if (!isset($this->dataLoader)) { + $this->dataLoader = new DataLoader($this->getApp()); + } + + return $this->dataLoader; + } + + public function initialize() + { + $this->install(); + $this->loadData(); + } + + public function terminate() + { + $baseDir = str_replace('/' . $this->installPath, '', getcwd()); + + chdir($baseDir); + set_include_path($baseDir); + } + + protected function install() + { + $mainApp = new \Espo\Core\Application(); + $fileManager = $mainApp->getContainer()->get('fileManager'); + + $latestEspo = Utils::getLatestBuildedPath($this->buildedPath); + + $configData = include($this->configPath); + $configData['siteUrl'] = $mainApp->getContainer()->get('config')->get('siteUrl') . '/' . $this->installPath; + + //remove and copy Espo files + Utils::dropTables($configData['database']); + $fileManager->removeInDir($this->installPath); + $fileManager->copy($latestEspo, $this->installPath, true); + + Utils::fixUndefinedVariables(); + + chdir($this->installPath); + set_include_path($this->installPath); + + require_once('install/core/Installer.php'); + + $installer = new \Installer(); + $installer->saveData(array(), 'en_US'); + $installer->saveConfig($configData); + + $installer = new \Installer(); //reload installer to get all config data + $installer->buildDatabase(); + $installer->setSuccess(); + } + + protected function loadData() + { + if (!empty($this->params['dataFile'])) { + $this->getDataLoader()->loadData($this->params['dataFile']); + } + + if (!empty($this->params['pathToFiles'])) { + $this->getDataLoader()->loadFiles($this->params['pathToFiles']); + } + } +} \ No newline at end of file diff --git a/tests/integration/Core/Utils.php b/tests/integration/Core/Utils.php new file mode 100644 index 0000000000..db17246304 --- /dev/null +++ b/tests/integration/Core/Utils.php @@ -0,0 +1,136 @@ +read()) { + if ($folderName === '.'|| $folderName === '..' || empty($folderName)) continue; + + $pattern = '/^EspoCRM-([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-((a|alpha|b|beta|pre|rc)[0-9]+)?)?$/'; + + if (preg_match($pattern, $folderName)) { + $archives[] = $folderName; + } + } + + if (count($archives) > 0) { + static::sortVersions($archives); + return $path . '/' . $archives[count($archives) - 1]; + } + } + + protected static function sortVersions(& $existVersions) + { + usort($existVersions, array("\\tests\\integration\\Core\\Utils", "versionCmp")); + } + + public static function versionCmp($a, $b) + { + $order = array('a' => 0, 'alpha' => 1, 'b' => 2, 'beta' => 3, 'pre' => 4, 'rc' => 5); + + $ma = $mb = array(); + + $pattern = '/^EspoCRM-([0-9]+)\.([0-9]+)(?:\.([0-9]+))?(?:-((a|alpha|b|beta|pre|rc)[0-9]+)?)?$/'; + + preg_match($pattern, $a, $ma); + preg_match($pattern, $b, $mb); + + if ($ma[1] != $mb[1]) { + return (int) $ma[1] < (int) $mb[1] ? -1 : 1; + } + if ($ma[2] != $mb[2]) { + return (int) $ma[2] < (int) $mb[2] ? -1 : 1; + } + if (!isset($ma[3])) { + $ma[3] = 0; + } + if (!isset($mb[3])) { + $mb[3] = 0; + } + if ($ma[3] != $mb[3]) { + return (int) $ma[3] < (int) $mb[3] ? -1 : 1; + } + if (isset($ma[4]) && !isset($mb[4])) { + return -1; + } + if (!isset($ma[4]) && isset($mb[4])) { + return 1; + } + if (@$ma[5] != @$mb[5]) { + return ($order[$ma[5]] < $order[$mb[5]]) ? -1 : 1; + } + if (@$ma[4] != @$mb[4]) { + return ($ma[4] < $mb[4]) ? -1 : 1; + } + return 0; + } + + public static function fixUndefinedVariables() + { + /*SET UNDEFINED $_SERVER VARIABLES*/ + $list = array( + 'REQUEST_METHOD', + 'REMOTE_ADDR', + 'SERVER_NAME', + 'SERVER_PORT', + 'REQUEST_URI', + 'HTTPS', + ); + + foreach ($list as $name) { + if (!array_key_exists($name, $_SERVER)) { + $_SERVER[$name] = ''; + } + } /*END: SET UNDEFINED VARIABLES*/ + } + + public static function dropTables(array $options) + { + $db = new \PDO('mysql:dbname='.$options['dbname'].';host='.$options['host'], $options['user'], $options['password']); + + $result = $db->query("show tables"); + while ($row = $result->fetch(\PDO::FETCH_NUM)) { + $db->query("DROP TABLE IF EXISTS `".$row[0]."`;"); + } + } +} \ No newline at end of file diff --git a/tests/integration/Espo/Account/ChangeFieldsTest.php b/tests/integration/Espo/Account/ChangeFieldsTest.php new file mode 100644 index 0000000000..7173df7885 --- /dev/null +++ b/tests/integration/Espo/Account/ChangeFieldsTest.php @@ -0,0 +1,53 @@ +getContainer()->get('entityManager'); + + $account = $entityManager->getEntity('Account', '53203b942850b'); + + $this->assertEquals('Espo\\Modules\\Crm\\Entities\\Account', get_class($account)); + $this->assertEquals('Besharp', $account->get('name')); + + $account->set('name', 'Changed Name'); + $entityManager->saveEntity($account); + + $account = $entityManager->getEntity('Account', '53203b942850b'); + + $this->assertEquals('Changed Name', $account->get('name')); + } +} diff --git a/tests/integration/Espo/User/LoginTest.php b/tests/integration/Espo/User/LoginTest.php new file mode 100644 index 0000000000..ec2a3bd18a --- /dev/null +++ b/tests/integration/Espo/User/LoginTest.php @@ -0,0 +1,52 @@ +getContainer()->get('user'); + + $this->assertEquals('Espo\\Entities\\User', get_class($user)); + $this->assertEquals('1', $user->get('id')); + $this->assertEquals('admin', $user->get('userName')); + } + + public function testWrongCredentials() + { + $app = $this->loadApp('admin', 'wrong-password'); + + $this->assertNull($app->getContainer()->get('user')); + } +} diff --git a/tests/integration/testData/Account/ChangeFields.php b/tests/integration/testData/Account/ChangeFields.php new file mode 100644 index 0000000000..2bba791285 --- /dev/null +++ b/tests/integration/testData/Account/ChangeFields.php @@ -0,0 +1,119 @@ + array( + 'User' => [ + array( + 'id' => '1', + 'isAdmin' => true, + 'userName' => 'admin', + 'password' => '1', + 'salutationName' => '', + 'firstName' => '', + 'lastName' => 'Admin', + 'title' => '', + 'emailAddress' => 'demo@espocrm.com', + 'phoneNumberData' => array( + (object) array( + 'phoneNumber' => '111', + 'primary' => true, + 'type' => 'Office', + ), + ), + ), + ], + 'Account' => [ + array( + 'id' => '53203b942850b', + 'name' => 'Besharp', + 'website' => 'http://www.be.sharp.ca', + 'phoneNumberData' => array( + (object) array( + 'phoneNumber' => '311-2233-11', + 'primary' => true, + 'type' => 'Office', + ), + (object) array( + 'phoneNumber' => '311-2233-12', + 'type' => 'Fax', + ), + ), + 'type' => 'Customer', + 'industry' => 'Apparel', + 'sicCode' => '', + 'billingAddressStreet' => '130 Somerset Street West', + 'billingAddressCity' => 'Ottawa', + 'billingAddressState' => 'Ontario', + 'billingAddressCountry' => 'Canada', + 'billingAddressPostalCode' => 'K3R 0F7', + 'shippingAddressStreet' => '130 Somerset Street West', + 'shippingAddressCity' => 'Ottawa', + 'shippingAddressState' => 'Ontario', + 'shippingAddressCountry' => 'Canada', + 'shippingAddressPostalCode' => 'K3R 0F7', + 'description' => '', + 'emailAddress' => 'supp@be.sharp-example.ca', + 'assignedUserId' => '1', + ), + array( + 'id' => '53203b9428546', + 'name' => 'Mein Heimathaus', + 'website' => 'http://www.meinheimathaus.de', + 'phoneNumberData' => array( + (object) array( + 'phoneNumber' => '165-681-158', + 'primary' => true, + 'type' => 'Office', + ), + (object) array( + 'phoneNumber' => '165-681-159', + 'type' => 'Other', + ), + ), + 'type' => 'Partner', + 'industry' => 'Finance', + 'sicCode' => '', + 'billingAddressStreet' => 'Goethestraße 23', + 'billingAddressCity' => 'Berlin', + 'billingAddressState' => 'Berlin', + 'billingAddressCountry' => 'Germany', + 'billingAddressPostalCode' => '10623', + 'shippingAddressStreet' => 'Goethestraße 23', + 'shippingAddressCity' => 'Berlin', + 'shippingAddressState' => 'Berlin', + 'shippingAddressCountry' => 'Germany', + 'shippingAddressPostalCode' => '10623', + 'description' => '', + 'emailAddress' => 'supp@be1.sharp-example.ca', + 'assignedUserId' => '1', + ), + ], + ), +); \ No newline at end of file diff --git a/tests/integration/testData/User/Login.php b/tests/integration/testData/User/Login.php new file mode 100644 index 0000000000..471965da37 --- /dev/null +++ b/tests/integration/testData/User/Login.php @@ -0,0 +1,53 @@ + array( + 'User' => [ + array( + 'id' => '1', + 'isAdmin' => true, + 'userName' => 'admin', + 'password' => '1', + 'salutationName' => '', + 'firstName' => '', + 'lastName' => 'Admin', + 'title' => '', + 'emailAddress' => 'demo@espocrm.com', + 'phoneNumberData' => array( + (object) array( + 'phoneNumber' => '111', + 'primary' => true, + 'type' => 'Office', + ), + ), + ), + ], + ), +); \ No newline at end of file