diff --git a/config-dist.php b/config-dist.php
deleted file mode 100644
index b87bff0c2..000000000
--- a/config-dist.php
+++ /dev/null
@@ -1,37 +0,0 @@
- 'localhost',
- 'db_user' => 'itop',
- 'db_pwd' => '1T0p',
- 'db_name' => 'itopv06',
- 'db_subname' => '', // use it to differentiate two applications instances running on the same DB
-);
-
-// Modules: file names should be specified as a absolute paths
-
-$MyModules = array(
- 'application' => array (
- '../application/menunode.class.inc.php',
- '../application/audit.rule.class.inc.php',
- // to be continued...
- ),
- 'business' => array (
- // to be continued...
- ),
- 'addons' => array (
- 'user rights' => '../addons/userrights/userrightsprofile.class.inc.php',
- // other modules to come later
- )
-);
-
-
-?>
diff --git a/config-test-farm.php b/test/config-test-farm.php
similarity index 100%
rename from config-test-farm.php
rename to test/config-test-farm.php
diff --git a/test/exclude.txt b/test/exclude.txt
new file mode 100644
index 000000000..1e327f0fd
--- /dev/null
+++ b/test/exclude.txt
@@ -0,0 +1,8 @@
+#
+# The following source files are not re-distributed with the "build" of the application
+# since they are used solely for constructing other files during the build process
+#
+config-test-farm.php
+test.class.inc.php
+test.php
+testlist.inc.php
diff --git a/test/test.class.inc.php b/test/test.class.inc.php
new file mode 100644
index 000000000..5ac7741c7
--- /dev/null
+++ b/test/test.class.inc.php
@@ -0,0 +1,557 @@
+
+ * @author Romain Quetiez
+ * @author Denis Flaven
+ * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
+ */
+
+
+require_once('../core/coreexception.class.inc.php');
+require_once('../core/attributedef.class.inc.php');
+require_once('../core/filterdef.class.inc.php');
+require_once('../core/stimulus.class.inc.php');
+require_once('../core/MyHelpers.class.inc.php');
+
+require_once('../core/expression.class.inc.php');
+require_once('../core/cmdbsource.class.inc.php');
+require_once('../core/sqlquery.class.inc.php');
+
+require_once('../core/log.class.inc.php');
+require_once('../core/kpi.class.inc.php');
+
+require_once('../core/dbobject.class.php');
+require_once('../core/dbobjectsearch.class.php');
+require_once('../core/dbobjectset.class.php');
+
+require_once('../application/cmdbabstract.class.inc.php');
+
+require_once('../core/userrights.class.inc.php');
+
+require_once('../webservices/webservices.class.inc.php');
+
+
+// Just to differentiate programmatically triggered exceptions and other kind of errors (usefull?)
+class UnitTestException extends Exception
+{}
+
+
+/**
+ * Improved display of the backtrace
+ *
+ * @package iTopORM
+ */
+class ExceptionFromError extends Exception
+{
+ public function getTraceAsHtml()
+ {
+ $aBackTrace = $this->getTrace();
+ return MyHelpers::get_callstack_html(0, $this->getTrace());
+ // return "\n".$this->getTraceAsString()."
\n";
+ }
+}
+
+
+/**
+ * Test handler API and basic helpers
+ *
+ * @package iTopORM
+ */
+abstract class TestHandler
+{
+ protected $m_aSuccesses;
+ protected $m_aWarnings;
+ protected $m_aErrors;
+ protected $m_sOutput;
+
+ public function __construct()
+ {
+ $this->m_aSuccesses = array();
+ $this->m_aWarnings = array();
+ $this->m_aErrors = array();
+ }
+
+ static public function GetName() {return "fooname";}
+ static public function GetDescription(){return "foodesc";}
+
+ protected function DoPrepare() {return true;}
+ abstract protected function DoExecute();
+ protected function DoCleanup() {return true;}
+
+ protected static function DumpVariable($var)
+ {
+ echo "\n";
+ print_r($var);
+ echo "
\n";
+ }
+
+ protected function ReportSuccess($sMessage, $sSubtestId = '')
+ {
+ $this->m_aSuccesses[] = $sMessage;
+ }
+
+ protected function ReportWarning($sMessage, $sSubtestId = '')
+ {
+ $this->m_aWarnings[] = $sMessage;
+ }
+
+ protected function ReportError($sMessage, $sSubtestId = '')
+ {
+ $this->m_aErrors[] = $sMessage;
+ }
+
+ public function GetResults()
+ {
+ return $this->m_aSuccesses;
+ }
+
+ public function GetWarnings()
+ {
+ return $this->m_aWarnings;
+ }
+
+ public function GetErrors()
+ {
+ return $this->m_aErrors;
+ }
+
+ public function GetOutput()
+ {
+ return $this->m_sOutput;
+ }
+
+ public function error_handler($errno, $errstr, $errfile, $errline)
+ {
+ // Note: return false to call the default handler (stop the program if an error)
+
+ switch ($errno)
+ {
+ case E_USER_ERROR:
+ $this->ReportError($errstr);
+ //throw new ExceptionFromError("Fatal error in line $errline of file $errfile: $errstr");
+ break;
+ case E_USER_WARNING:
+ $this->ReportWarning($errstr);
+ break;
+ case E_USER_NOTICE:
+ $this->ReportWarning($errstr);
+ break;
+ default:
+ $this->ReportWarning("Unknown error type: [$errno] $errstr in $errfile at $errline");
+ echo "Unknown error type: [$errno] $errstr in $errfile at $errline
\n";
+ break;
+ }
+ return true; // do not call the default handler
+ }
+
+ public function Execute()
+ {
+ ob_start();
+ set_error_handler(array($this, 'error_handler'));
+ try
+ {
+ $this->DoPrepare();
+ $this->DoExecute();
+ }
+ catch (ExceptionFromError $e)
+ {
+ $this->ReportError($e->getMessage().' - '.$e->getTraceAsHtml());
+ }
+ catch (CoreException $e)
+ {
+ //$this->ReportError($e->getMessage());
+ //$this->ReportError($e->__tostring());
+ $this->ReportError($e->getMessage().' - '.$e->getTraceAsHtml());
+ }
+ catch (Exception $e)
+ {
+ //$this->ReportError($e->getMessage());
+ //$this->ReportError($e->__tostring());
+ $this->ReportError('class '.get_class($e).' --- '.$e->getMessage().' - '.$e->getTraceAsString());
+ }
+ restore_error_handler();
+ $this->m_sOutput = ob_get_clean();
+ return (count($this->GetErrors()) == 0);
+ }
+}
+
+
+
+
+/**
+ * Test to execute a piece of code (checks if an error occurs)
+ *
+ * @package iTopORM
+ */
+abstract class TestFunction extends TestHandler
+{
+ // simply overload DoExecute (temporary)
+}
+
+
+/**
+ * Test to execute a piece of code (checks if an error occurs)
+ *
+ * @package iTopORM
+ */
+abstract class TestWebServices extends TestHandler
+{
+ // simply overload DoExecute (temporary)
+
+ static protected function DoPostRequestAuth($sRelativeUrl, $aData, $sLogin = 'admin', $sPassword = 'admin', $sOptionnalHeaders = null)
+ {
+ $aDataAndAuth = $aData;
+// To be changed to use basic authentication
+ $aDataAndAuth['operation'] = 'login';
+ $aDataAndAuth['auth_user'] = $sLogin;
+ $aDataAndAuth['auth_pwd'] = $sPassword;
+ $sHost = $_SERVER['HTTP_HOST'];
+ $sRawPath = $_SERVER['SCRIPT_NAME'];
+ $sPath = dirname($sRawPath);
+ $sUrl = "http://$sHost/$sPath/$sRelativeUrl";
+
+ return self::DoPostRequest($sUrl, $aDataAndAuth, $sOptionnalHeaders);
+ }
+
+ // Source: http://netevil.org/blog/2006/nov/http-post-from-php-without-curl
+ // originaly named after do_post_request
+ // Partially adapted to our coding conventions
+ static protected function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null)
+ {
+ // $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request.
+
+ $sData = http_build_query($aData);
+
+ $aParams = array('http' => array(
+ 'method' => 'POST',
+ 'content' => $sData,
+ 'header'=> "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n",
+ ));
+ if ($sOptionnalHeaders !== null)
+ {
+ $aParams['http']['header'] .= $sOptionnalHeaders;
+ }
+ $ctx = stream_context_create($aParams);
+
+ $fp = @fopen($sUrl, 'rb', false, $ctx);
+ if (!$fp)
+ {
+ global $php_errormsg;
+ if (isset($php_errormsg))
+ {
+ throw new Exception("Problem with $sUrl, $php_errormsg");
+ }
+ else
+ {
+ throw new Exception("Problem with $sUrl");
+ }
+ }
+ $response = @stream_get_contents($fp);
+ if ($response === false)
+ {
+ throw new Exception("Problem reading data from $sUrl, $php_errormsg");
+ }
+ return $response;
+ }
+}
+
+/**
+ * Test to execute a piece of code (checks if an error occurs)
+ *
+ * @package iTopORM
+ */
+abstract class TestSoapWebService extends TestHandler
+{
+ // simply overload DoExecute (temporary)
+
+ function __construct()
+ {
+ parent::__construct();
+ }
+}
+
+/**
+ * Test to check that a function outputs some values depending on its input
+ *
+ * @package iTopORM
+ */
+abstract class TestFunctionInOut extends TestFunction
+{
+// abstract static public function GetCallSpec(); // parameters to call_user_func
+// abstract static public function GetInOut(); // array of input => output
+
+ protected function DoExecute()
+ {
+ $aTests = $this->GetInOut();
+ if (is_array($aTests))
+ {
+ foreach ($aTests as $iTestId => $aTest)
+ {
+ $ret = call_user_func_array($this->GetCallSpec(), $aTest['args']);
+ if ($ret != $aTest['output'])
+ {
+ // Note: to be improved to cope with non string parameters
+ $this->ReportError("Found '$ret' while expecting '".$aTest['output']."'", $iTestId);
+ }
+ else
+ {
+ $this->ReportSuccess("Found the expected output '$ret'", $iTestId);
+ }
+ }
+ }
+ else
+ {
+ $ret = call_user_func($this->GetCallSpec());
+ $this->ReportSuccess('Finished successfully');
+ }
+ }
+}
+
+
+/**
+ * Test to check an URL (Searches for Error/Warning/Etc keywords)
+ *
+ * @package iTopORM
+ */
+abstract class TestUrl extends TestHandler
+{
+// abstract static public function GetUrl();
+// abstract static public function GetErrorKeywords();
+// abstract static public function GetWarningKeywords();
+
+ protected function DoExecute()
+ {
+ return true;
+ }
+}
+
+
+/**
+ * Test to check a user management module
+ *
+ * @package iTopORM
+ */
+abstract class TestUserRights extends TestHandler
+{
+ protected function DoExecute()
+ {
+ return true;
+ }
+}
+
+
+/**
+ * Test to execute a scenario on a given DB
+ *
+ * @package iTopORM
+ */
+abstract class TestScenarioOnDB extends TestHandler
+{
+// abstract static public function GetDBHost();
+// abstract static public function GetDBUser();
+// abstract static public function GetDBPwd();
+// abstract static public function GetDBName();
+
+ protected function DoPrepare()
+ {
+ $sDBHost = $this->GetDBHost();
+ $sDBUser = $this->GetDBUser();
+ $sDBPwd = $this->GetDBPwd();
+ $sDBName = $this->GetDBName();
+
+ CMDBSource::Init($sDBHost, $sDBUser, $sDBPwd);
+ CMDBSource::SetCharacterSet();
+ if (CMDBSource::IsDB($sDBName))
+ {
+ CMDBSource::DropDB($sDBName);
+ }
+ CMDBSource::CreateDB($sDBName);
+ }
+
+ protected function DoCleanup()
+ {
+ // CMDBSource::DropDB($this->GetDBName());
+ }
+}
+
+
+/**
+ * Test to use a business model on a given DB
+ *
+ * @package iTopORM
+ */
+abstract class TestBizModel extends TestHandler
+{
+// abstract static public function GetDBSubName();
+// abstract static public function GetBusinessModelFile();
+// abstract static public function GetConfigFile();
+
+ protected function DoPrepare()
+ {
+ MetaModel::Startup($this->GetConfigFile());
+// #@# Temporary disabled by Romain
+// MetaModel::CheckDefinitions();
+
+ // something here to create records... but that's another story
+ }
+
+ protected $m_oChange;
+ protected function ObjectToDB($oNew, $bReload = false)
+ {
+ list($bRes, $aIssues) = $oNew->CheckToWrite();
+ if (!$bRes)
+ {
+ throw new CoreException('Could not create object, unexpected values', array('issues' => $aIssues));
+ }
+ if ($oNew instanceof CMDBObject)
+ {
+ if (!isset($this->m_oChange))
+ {
+ new CMDBChange();
+ $oMyChange = MetaModel::NewObject("CMDBChange");
+ $oMyChange->Set("date", time());
+ $oMyChange->Set("userinfo", "Someone doing some tests");
+ $iChangeId = $oMyChange->DBInsertNoReload();
+ $this->m_oChange = $oMyChange;
+ }
+ if ($bReload)
+ {
+ $iId = $oNew->DBInsertTracked($this->m_oChange);
+ }
+ else
+ {
+ $iId = $oNew->DBInsertTrackedNoReload($this->m_oChange);
+ }
+ }
+ else
+ {
+ if ($bReload)
+ {
+ $iId = $oNew->DBInsert();
+ }
+ else
+ {
+ $iId = $oNew->DBInsertNoReload();
+ }
+ }
+ return $iId;
+ }
+
+ protected function ResetDB()
+ {
+ if (MetaModel::DBExists(false))
+ {
+ MetaModel::DBDrop();
+ }
+ MetaModel::DBCreate();
+ }
+
+ static protected function show_list($oObjectSet)
+ {
+ $oObjectSet->Rewind();
+ $aData = array();
+ while ($oItem = $oObjectSet->Fetch())
+ {
+ $aValues = array();
+ foreach(MetaModel::GetAttributesList(get_class($oItem)) as $sAttCode)
+ {
+ $aValues[$sAttCode] = $oItem->GetAsHTML($sAttCode);
+ }
+ //echo $oItem->GetKey()." => ".implode(", ", $aValues)."\n";
+ $aData[] = $aValues;
+ }
+ echo MyHelpers::make_table_from_assoc_array($aData);
+ }
+
+ static protected function search_and_show_list(DBObjectSearch $oMyFilter)
+ {
+ $oObjSet = new CMDBObjectSet($oMyFilter);
+ echo $oMyFilter->__DescribeHTML()."' - Found ".$oObjSet->Count()." items.\n";
+ self::show_list($oObjSet);
+ }
+
+ static protected function search_and_show_list_from_oql($sOQL)
+ {
+ echo $sOQL."...
\n";
+ $oNewFilter = DBObjectSearch::FromOQL($sOQL);
+ self::search_and_show_list($oNewFilter);
+ }
+}
+
+
+/**
+ * Test to execute a scenario common to any business model (tries to build all the possible queries, etc.)
+ *
+ * @package iTopORM
+ */
+abstract class TestBizModelGeneric extends TestBizModel
+{
+ static public function GetName()
+ {
+ return 'Full test on a given business model';
+ }
+
+ static public function GetDescription()
+ {
+ return 'Systematic tests: gets each and every existing class and tries every attribute, search filters, etc.';
+ }
+
+ protected function DoPrepare()
+ {
+ parent::DoPrepare();
+
+ if (!MetaModel::DBExists(false))
+ {
+ MetaModel::DBCreate();
+ }
+ // something here to create records... but that's another story
+ }
+
+ protected function DoExecute()
+ {
+ foreach(MetaModel::GetClasses() as $sClassName)
+ {
+ if (MetaModel::HasTable($sClassName)) continue;
+
+ $oNobody = MetaModel::GetObject($sClassName, 123);
+ $oBaby = new $sClassName;
+ $oFilter = new DBObjectSearch($sClassName);
+
+ // Challenge reversibility of OQL / filter object
+ //
+ $sExpr1 = $oFilter->ToOQL();
+ $oNewFilter = DBObjectSearch::FromOQL($sExpr1);
+ $sExpr2 = $oNewFilter->ToOQL();
+ if ($sExpr1 != $sExpr2)
+ {
+ $this->ReportError("Found two different OQL expression out of the (same?) filter: $sExpr1 != $sExpr2");
+ }
+
+ // Use the filter (perform the query)
+ //
+ $oSet = new CMDBObjectSet($oFilter);
+ $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName");
+ }
+ return true;
+ }
+}
+
+
+?>
diff --git a/test/test.php b/test/test.php
new file mode 100644
index 000000000..0664aac9b
--- /dev/null
+++ b/test/test.php
@@ -0,0 +1,157 @@
+
+ * @author Romain Quetiez
+ * @author Denis Flaven
+ * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
+ */
+
+?>
+
+
+
+///////////////////////////////////////////////////////////////////////////////
+// Helpers
+///////////////////////////////////////////////////////////////////////////////
+
+function ReadMandatoryParam($sName)
+{
+ $value = utils::ReadParam($sName, null);
+ if (is_null($value))
+ {
+ echo "Missing mandatory argument $sName
";
+ exit;
+ }
+ return $value;
+}
+
+function IsAValidTestClass($sClassName)
+{
+ // Must be a child of TestHandler
+ //
+ if (!is_subclass_of($sClassName, 'TestHandler')) return false;
+
+ // Must not be abstract
+ //
+ $oReflectionClass = new ReflectionClass($sClassName);
+ if (!$oReflectionClass->isInstantiable()) return false;
+
+ return true;
+}
+
+function DisplayEvents($aEvents, $sTitle)
+{
+ echo "$sTitle
\n";
+ if (count($aEvents) > 0)
+ {
+ echo "\n";
+ foreach ($aEvents as $sEvent)
+ {
+ echo "- $sEvent
\n";
+ }
+ echo "
\n";
+ }
+ else
+ {
+ echo "none
\n";
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////////
+// Main
+///////////////////////////////////////////////////////////////////////////////
+
+
+require_once('../application/utils.inc.php');
+require_once('../core/test.class.inc.php');
+require_once('./testlist.inc.php');
+
+require_once('../core/cmdbobject.class.inc.php');
+
+$sTodo = utils::ReadParam("todo", "");
+if ($sTodo == '')
+{
+ // Show the list of tests
+ //
+ echo "Existing tests
\n";
+ echo "\n";
+ foreach (get_declared_classes() as $sClassName)
+ {
+ if (!IsAValidTestClass($sClassName)) continue;
+
+ $sName = call_user_func(array($sClassName, 'GetName'));
+ $sDescription = call_user_func(array($sClassName, 'GetDescription'));
+ echo "- $sName ($sDescription)
\n";
+}
+else if ($sTodo == 'exec')
+{
+ // Execute a test
+ //
+ $sTestClass = ReadMandatoryParam("testid");
+
+ if (!IsAValidTestClass($sTestClass))
+ {
+ echo "Wrong value for testid, expecting a valid class name
\n";
+ }
+ else
+ {
+ $oTest = new $sTestClass();
+ echo "Testing: ".$oTest->GetName()."
\n";
+ $bRes = $oTest->Execute();
+ }
+
+/*
+MyHelpers::var_dump_html($oTest->GetResults());
+MyHelpers::var_dump_html($oTest->GetWarnings());
+MyHelpers::var_dump_html($oTest->GetErrors());
+*/
+
+ if ($bRes)
+ {
+ echo "Success :-)
\n";
+ DisplayEvents($oTest->GetResults(), 'Results');
+ }
+ else
+ {
+ echo "Failure :-(
\n";
+ }
+ DisplayEvents($oTest->GetErrors(), 'Errors');
+ DisplayEvents($oTest->GetWarnings(), 'Warnings');
+
+ // Render the output
+ //
+ echo "Actual output
\n";
+ echo "\n";
+ echo $oTest->GetOutput();
+ echo "
\n";
+}
+else
+{
+}
+
+
+?>
diff --git a/test/testlist.inc.php b/test/testlist.inc.php
new file mode 100644
index 000000000..55c8872d5
--- /dev/null
+++ b/test/testlist.inc.php
@@ -0,0 +1,2104 @@
+
+ * @author Romain Quetiez
+ * @author Denis Flaven
+ * @license http://www.opensource.org/licenses/gpl-3.0.html LGPL
+ */
+
+
+class TestSQLQuery extends TestScenarioOnDB
+{
+ static public function GetName() {return 'SQLQuery';}
+ static public function GetDescription() {return 'SQLQuery does not depend on the rest of the framework, therefore it makes sense to have a separate test framework for it';}
+
+ static public function GetDBHost() {return 'localhost';}
+ static public function GetDBUser() {return 'root';}
+ static public function GetDBPwd() {return '';}
+ static public function GetDBName() {return 'TestSQLQuery';}
+ static public function GetDBSubName() {return 'taratata';}
+
+
+ protected function DoPrepare()
+ {
+ parent::DoPrepare();
+ cmdbSource::CreateTable('CREATE TABLE `myTable` (myKey INT(11) NOT NULL auto_increment, column1 VARCHAR(255), column2 VARCHAR(255), PRIMARY KEY (`myKey`)) ENGINE = '.MYSQL_ENGINE);
+ cmdbSource::CreateTable('CREATE TABLE `myTable1` (myKey1 INT(11) NOT NULL auto_increment, column1_1 VARCHAR(255), column1_2 VARCHAR(255), PRIMARY KEY (`myKey1`)) ENGINE = '.MYSQL_ENGINE);
+ cmdbSource::CreateTable('CREATE TABLE `myTable2` (myKey2 INT(11) NOT NULL auto_increment, column2_1 VARCHAR(255), column2_2 VARCHAR(255), PRIMARY KEY (`myKey2`)) ENGINE = '.MYSQL_ENGINE);
+ }
+
+ protected function DoExecute()
+ {
+ $oQuery = new SQLQuery(
+ $sTable = 'myTable',
+ $sTableAlias = 'myTableAlias',
+ $aFields = array('column1'=>new FieldExpression('column1', 'myTableAlias'), 'column2'=>new FieldExpression('column2', 'myTableAlias')),
+ $oCondition = new BinaryExpression(new FieldExpression('column1', 'myTableAlias'), 'LIKE', new ScalarExpression('trash')),
+ $aFullTextNeedles = array('column1'),
+ $bToDelete = false,
+ $aValues = array()
+ );
+ $oQuery->AddCondition(Expression::FromOQL('DATE(NOW() - 1200 * 2) > \'2008-07-31\''));
+
+ $oSubQuery1 = new SQLQuery(
+ $sTable = 'myTable1',
+ $sTableAlias = 'myTable1Alias',
+ $aFields = array('column1_1'=>new FieldExpression('column1', 'myTableAlias'), 'column1_2'=>new FieldExpression('column1', 'myTableAlias')),
+ $oCondition = new TrueSQLExpression,
+ $aFullTextNeedles = array(),
+ $bToDelete = false,
+ $aValues = array()
+ );
+
+ $oSubQuery2 = new SQLQuery(
+ $sTable = 'myTable2',
+ $sTableAlias = 'myTable2Alias',
+ $aFields = array('column2_1'=>new FieldExpression('column2', 'myTableAlias'), 'column2_2'=>new FieldExpression('column2', 'myTableAlias')),
+ $oCondition = new TrueSQLExpression,
+ $aFullTextNeedles = array(),
+ $bToDelete = false,
+ $aValues = array()
+ );
+
+ $oQuery->AddInnerJoin($oSubQuery1, 'column1', 'column1_1');
+ $oQuery->AddLeftJoin($oSubQuery2, 'column2', 'column2_2');
+
+ $oQuery->DisplayHtml();
+ $oQuery->RenderDelete();
+ $oQuery->RenderUpdate();
+ echo ''.$oQuery->RenderSelect().'
';
+ $oQuery->RenderSelect(array('column1'));
+ $oQuery->RenderSelect(array('column1', 'column2'));
+ }
+}
+
+class TestOQLParser extends TestFunction
+{
+ static public function GetName() {return 'Check OQL parsing';}
+ static public function GetDescription() {return 'Attempts a series of queries, and in particular those with a bad syntax';}
+
+ protected function CheckQuery($sQuery, $bIsCorrectQuery)
+ {
+ $oOql = new OqlInterpreter($sQuery);
+ try
+ {
+ $oTrash = $oOql->Parse(); // Not expecting a given format, otherwise use ParseExpression/ParseObjectQuery/ParseValueSetQuery
+ self::DumpVariable($oTrash);
+ }
+ catch (OQLException $OqlException)
+ {
+ if ($bIsCorrectQuery)
+ {
+ echo "More info on this unexpected failure:
".$OqlException->getHtmlDesc()."
\n";
+ throw $OqlException;
+ return false;
+ }
+ else
+ {
+ // Everything is fine :-)
+ echo "More info on this expected failure:
".$OqlException->getHtmlDesc()."
\n";
+ return true;
+ }
+ }
+ // The query was correctly parsed, was it expected to be correct ?
+ if ($bIsCorrectQuery)
+ {
+ return true;
+ }
+ else
+ {
+ throw new UnitTestException("The query '$sQuery' was parsed with success, while it shouldn't (?)");
+ return false;
+ }
+ }
+
+ protected function TestQuery($sQuery, $bIsCorrectQuery)
+ {
+ if (!$this->CheckQuery($sQuery, $bIsCorrectQuery))
+ {
+ return false;
+ }
+ return true;
+ }
+
+ public function DoExecute()
+ {
+ $aQueries = array(
+ 'SELECT toto' => true,
+ 'SELECT toto WHERE toto.a = 1' => true,
+ 'SELECT toto WHERE toto.a=1' => true,
+ 'SELECT toto WHERE toto.a = "1"' => true,
+ 'SELECT toto WHHHERE toto.a = "1"' => false,
+ 'SELECT toto WHERE toto.a == "1"' => false,
+ 'SELECT toto WHERE toto.a % 1' => false,
+ //'SELECT toto WHERE toto.a LIKE 1' => false,
+ 'SELECT toto WHERE toto.a like \'arg\'' => false,
+ 'SELECT toto WHERE toto.a NOT LIKE "That\'s it"' => true,
+ 'SELECT toto WHERE toto.a NOT LIKE "That\'s "it""' => false,
+ 'SELECT toto WHERE toto.a NOT LIKE "That\'s \\"it\\""' => true,
+ 'SELECT toto WHERE toto.a NOT LIKE \'That"s it\'' => true,
+ 'SELECT toto WHERE toto.a NOT LIKE \'That\'s it\'' => false,
+ 'SELECT toto WHERE toto.a NOT LIKE \'That\\\'s it\'' => true,
+ 'SELECT toto WHERE toto.a NOT LIKE "blah \\ truc"' => false,
+ 'SELECT toto WHERE toto.a NOT LIKE "blah \\\\ truc"' => true,
+ 'SELECT toto WHERE toto.a NOT LIKE \'blah \\ truc\'' => false,
+ 'SELECT toto WHERE toto.a NOT LIKE \'blah \\\\ truc\'' => true,
+
+ 'SELECT toto WHERE toto.a NOT LIKE "\\\\"' => true,
+ 'SELECT toto WHERE toto.a NOT LIKE "\\""' => true,
+ 'SELECT toto WHERE toto.a NOT LIKE "\\"\\\\"' => true,
+ 'SELECT toto WHERE toto.a NOT LIKE "\\\\\\""' => true,
+ 'SELECT toto WHERE toto.a NOT LIKE ""' => true,
+ 'SELECT toto WHERE toto.a NOT LIKE "\\\\"' => true,
+ "SELECT UserRightsMatrixClassGrant WHERE UserRightsMatrixClassGrant.class = 'lnkContactRealObject' AND UserRightsMatrixClassGrant.action = 'modify' AND UserRightsMatrixClassGrant.login = 'Denis'" => true,
+ "SELECT A WHERE A.col1 = 'lit1' AND A.col2 = 'lit2' AND A.col3 = 'lit3'" => true,
+
+ 'SELECT toto WHERE toto.a NOT LIKE "blah" AND toto.b LIKE "foo"' => true,
+
+ //'SELECT toto WHERE toto.a > \'asd\'' => false,
+ 'SELECT toto WHERE toto.a = 1 AND toto.b LIKE "x" AND toto.f >= 12345' => true,
+ 'SELECT Device JOIN Site ON Device.site = Site.id' => true,
+ 'SELECT Device JOIN Site ON Device.site = Site.id JOIN Country ON Site.location = Country.id' => true,
+
+ "SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 = 123 AND B.col1 = 'aa') OR (A.col3 = 'zzz' AND B.col4 > 100)" => true,
+ "SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 = B.col2 AND B.col1 = A.col2) OR (A.col3 = '' AND B.col4 > 100)" => true,
+ "SELECT A JOIN B ON A.myB = B.id WHERE A.col1 + B.col2 * B.col1 = A.col2" => true,
+ "SELECT A JOIN B ON A.myB = B.id WHERE A.col1 + (B.col2 * B.col1) = A.col2" => true,
+ "SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 + B.col2) * B.col1 = A.col2" => true,
+
+ 'SELECT Device AS D_ JOIN Site AS S_ ON D_.site = S_.id WHERE S_.country = "Francia"' => true,
+
+ // Several objects in a row...
+ //
+ 'SELECT A FROM A' => true,
+ 'SELECT A JOIN B ON A.myB = B.id WHERE A.col1 = 2' => true,
+ 'SELECT A FROM A JOIN B ON A.myB = B.id WHERE A.col1 = 2' => true,
+ 'SELECT B FROM A JOIN B ON A.myB = B.id WHERE A.col1 = 2' => true,
+ 'SELECT A,B FROM A JOIN B ON A.myB = B.id WHERE A.col1 = 2' => true,
+ 'SELECT A, B FROM A JOIN B ON A.myB = B.id WHERE A.col1 = 2' => true,
+ 'SELECT B,A FROM A JOIN B ON A.myB = B.id WHERE A.col1 = 2' => true,
+ 'SELECT A, B,C FROM A JOIN B ON A.myB = B.id' => false,
+ 'SELECT C FROM A JOIN B ON A.myB = B.id WHERE A.col1 = 2' => false,
+ );
+
+ $iErrors = 0;
+
+ foreach($aQueries as $sQuery => $bIsCorrectQuery)
+ {
+ $sIsOk = $bIsCorrectQuery ? 'good' : 'bad';
+ echo "Testing query: $sQuery ($sIsOk)
\n";
+ $bRet = $this->TestQuery($sQuery, $bIsCorrectQuery);
+ if (!$bRet) $iErrors++;
+ }
+
+ return ($iErrors == 0);
+ }
+}
+
+
+class TestCSVParser extends TestFunction
+{
+ static public function GetName() {return 'Check CSV parsing';}
+ static public function GetDescription() {return 'Loads a set of CSV data';}
+
+ public function DoExecute()
+ {
+ $sDataFile = '?field1?;?field2?;?field3?
+?a?;?b?;?c?
+a;b;c
+ ? a ? ; ? b ? ; ? c ?
+ a ; b ; c
+??;??;??
+;;
+?a"?;?b?;?c?
+?a1
+a2?;?b?;?c?
+?a1,a2?;?b?;?c?
+?a?;?b?;?c1,",c2
+,c3?
+?a?;?b?;?ouf !?
+ Espace sur la fin ; 1234; e@taloc.com ';
+
+ self::DumpVariable($sDataFile);
+
+ $aExpectedResult = array(
+ //array('field1', 'field2', 'field3'),
+ array('a', 'b', 'c'),
+ array('a', 'b', 'c'),
+ array(' a ', ' b ', ' c '),
+ array('a', 'b', 'c'),
+ array('', '', ''),
+ array('', '', ''),
+ array('a"', 'b', 'c'),
+ array("a1\na2", 'b', 'c'),
+ array('a1,a2', 'b', 'c'),
+ array('a', 'b', "c1,\",c2\n,c3"),
+ array('a', 'b', 'ouf !'),
+ array('Espace sur la fin', '1234', 'e@taloc.com'),
+ );
+
+ $oCSVParser = new CSVParser($sDataFile, ';', '?');
+ $aData = $oCSVParser->ToArray(1, null, 0);
+
+ $iIssues = 0;
+
+ echo "\n";
+ foreach ($aData as $iRow => $aRow)
+ {
+ echo "\n";
+ foreach ($aRow as $iCol => $sCell)
+ {
+ if (empty($sCell))
+ {
+ $sCellValue = ' ';
+ }
+ else
+ {
+ $sCellValue = htmlentities($sCell);
+ }
+
+ if (!isset($aExpectedResult[$iRow][$iCol]))
+ {
+ $iIssues++;
+ $sCellValue = "$sCellValue";
+ }
+ elseif ($aExpectedResult[$iRow][$iCol] != $sCell)
+ {
+ $iIssues++;
+ $sCellValue = "$sCellValue, expecting '".$aExpectedResult[$iRow][$iCol]."'";
+ }
+
+ echo "$sCellValue | ";
+ }
+ echo "
\n";
+ }
+ echo "
\n";
+ return ($iIssues > 0);
+ }
+}
+
+class TestGenericItoMyModel extends TestBizModelGeneric
+{
+ static public function GetName()
+ {
+ return 'Generic RO test on '.self::GetConfigFile();
+ }
+
+ static public function GetConfigFile() {return '../config-test-mymodel.php';}
+}
+
+class TestGenericItopBigModel extends TestBizModelGeneric
+{
+ static public function GetName()
+ {
+ return 'Generic RO test on '.self::GetConfigFile();
+ }
+
+ static public function GetConfigFile() {return '../config-test-itopv06.php';}
+}
+
+class TestUserRightsMatrixItop extends TestUserRights
+{
+ static public function GetName()
+ {
+ return 'User rights test on user rights matrix';
+ }
+
+ static public function GetDescription()
+ {
+ return 'blah blah blah';
+ }
+
+ public function DoPrepare()
+ {
+ parent::DoPrepare();
+ MetaModel::Startup('../config-test-itopv06.php');
+ }
+
+ protected function DoExecute()
+ {
+ $sUser = 'Romain';
+ echo "Totor: ".(UserRights::CheckCredentials('Totor', 'toto') ? 'ok' : 'NO')."
\n";
+ echo "Romain: ".(UserRights::CheckCredentials('Romain', 'toto') ? 'ok' : 'NO')."
\n";
+ echo "User: ".UserRights::GetUser()."
\n";
+ echo "On behalf of...".UserRights::GetRealUser()."
\n";
+
+ echo "Denis (impersonate) : ".(UserRights::Impersonate('Denis', 'tutu') ? 'ok' : 'NO')."
\n";
+ echo "User: ".UserRights::GetUser()."
\n";
+ echo "On behalf of...".UserRights::GetRealUser()."
\n";
+
+ $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT bizOrganization"));
+ echo "IsActionAllowed...".(UserRights::IsActionAllowed('bizOrganization', UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES ? 'ok' : 'NO')."
\n";
+ echo "IsStimulusAllowed...".(UserRights::IsStimulusAllowed('bizOrganization', 'myStimulus', $oSet) == UR_ALLOWED_YES ? 'ok' : 'NO')."
\n";
+ echo "IsActionAllowedOnAttribute...".(UserRights::IsActionAllowedOnAttribute('bizOrganization', 'myattribute', UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES ? 'ok' : 'NO')."
\n";
+ return true;
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Test a complex biz model on the fly
+///////////////////////////////////////////////////////////////////////////
+
+class TestMyBizModel extends TestBizModel
+{
+ static public function GetName()
+ {
+ return 'A series of tests on a weird business model';
+ }
+
+ static public function GetDescription()
+ {
+ return 'Attempts various operations and build complex queries';
+ }
+
+ static public function GetConfigFile() {return '../config-test-mymodel.php';}
+
+ function test_linksinfo()
+ {
+ echo "Enum links
";
+ self::DumpVariable(MetaModel::EnumReferencedClasses("cmdbTeam"));
+ self::DumpVariable(MetaModel::EnumReferencingClasses("Organization"));
+
+ self::DumpVariable(MetaModel::EnumLinkingClasses());
+ self::DumpVariable(MetaModel::EnumLinkingClasses("cmdbContact"));
+ self::DumpVariable(MetaModel::EnumLinkingClasses("cmdWorkshop"));
+ self::DumpVariable(MetaModel::GetLinkLabel("Liens_entre_contacts_et_workshop", "toworkshop"));
+ }
+
+ function test_list_attributes()
+ {
+ echo "List attributes
";
+ foreach(MetaModel::ListAttributeDefs("cmdbTeam") as $sAttCode=>$oAttDef)
+ {
+ echo $oAttDef->GetLabel()." / ".$oAttDef->GetDescription()." / ".$oAttDef->GetType()."\n";
+ }
+ }
+
+ function test_search()
+ {
+ echo "Two searches
";
+ $oFilterAllDevs = new DBObjectSearch("cmdbTeam");
+ $oAllDevs = new DBObjectSet($oFilterAllDevs);
+
+ echo "Found ".$oAllDevs->Count()." items.\n";
+ while ($oDev = $oAllDevs->Fetch())
+ {
+ $aValues = array();
+ foreach(MetaModel::GetAttributesList($oAllDevs->GetClass()) as $sAttCode)
+ {
+ $aValues[] = MetaModel::GetLabel(get_class($oDev), $sAttCode)." (".MetaModel::GetDescription(get_class($oDev), $sAttCode).") = ".$oDev->GetAsHTML($sAttCode);
+ }
+ echo $oDev->GetKey()." => ".implode(", ", $aValues)."\n";
+ }
+
+ // a second one
+ $oMyFilter = new DBObjectSearch("cmdbContact");
+ //$oMyFilter->AddCondition("name", "aii", "Finishes with");
+ $oMyFilter->AddCondition("name", "aii");
+ $this->search_and_show_list($oMyFilter);
+
+ }
+
+ function test_reload()
+ {
+ echo "Reload
";
+ $team = MetaModel::GetObject("cmdbContact", "2");
+ echo "Chargement de l'attribut headcount: {$team->Get("headcount")}\n";
+ self::DumpVariable($team);
+ }
+
+ function test_setattribute()
+ {
+ echo "Set attribute and update
";
+ $team = MetaModel::GetObject("cmdbTeam", "2");
+ $team->Set("headcount", rand(1,1000));
+ $team->Set("email", "Luis ".rand(9,250));
+ self::DumpVariable($team->ListChanges());
+ echo "New headcount = {$team->Get("headcount")}\n";
+ echo "Computed name = {$team->Get("name")}\n";
+
+ $oMyChange = MetaModel::NewObject("CMDBChange");
+ $oMyChange->Set("date", time());
+ $oMyChange->Set("userinfo", "test_setattribute / Made by robot #".rand(1,100));
+ $iChangeId = $oMyChange->DBInsert();
+
+ //MetaModel::StartDebugQuery();
+ $team->DBUpdateTracked($oMyChange);
+ //MetaModel::StopDebugQuery();
+
+ echo "Check the modified team
";
+ $oTeam = MetaModel::GetObject("cmdbTeam", "2");
+ self::DumpVariable($oTeam);
+ }
+ function test_newobject()
+ {
+ $oMyChange = MetaModel::NewObject("CMDBChange");
+ $oMyChange->Set("date", time());
+ $oMyChange->Set("userinfo", "test_newobject / Made by robot #".rand(1,100));
+ $iChangeId = $oMyChange->DBInsert();
+
+ echo "Create a new object (team)
";
+ $oNewTeam = MetaModel::NewObject("cmdbTeam");
+ $oNewTeam->Set("name", "ekip2choc #".rand(1000, 2000));
+ $oNewTeam->Set("email", "machin".rand(1,100)."@tnut.com");
+ $oNewTeam->Set("email", null);
+ $oNewTeam->Set("owner", "ITOP");
+ $oNewTeam->Set("headcount", "0".rand(38000, 38999)); // should be reset to an int value
+ $iId = $oNewTeam->DBInsertTracked($oMyChange);
+ echo "Created new team: $iId";
+ echo "Delete team #$iId
";
+ $oTeam = MetaModel::GetObject("cmdbTeam", $iId);
+ $oTeam->DBDeleteTracked($oMyChange);
+ echo "Deleted team: $iId";
+ self::DumpVariable($oTeam);
+ }
+
+
+ function test_updatecolumn()
+ {
+ $oMyChange = MetaModel::NewObject("CMDBChange");
+ $oMyChange->Set("date", time());
+ $oMyChange->Set("userinfo", "test_updatecolumn / Made by robot #".rand(1,100));
+ $iChangeId = $oMyChange->DBInsert();
+
+ $sNewEmail = "updatecol".rand(9,250)."@quedlaballe.com";
+ echo "Update a the email: set to '$sNewEmail'
";
+ $oMyFilter = new DBObjectSearch("cmdbContact");
+ $oMyFilter->AddCondition("name", "o", "Contains");
+
+ echo "Candidates before:";
+ $this->search_and_show_list($oMyFilter);
+
+ MetaModel::BulkUpdateTracked($oMyChange, $oMyFilter, array("email" => $sNewEmail));
+
+ echo "Candidates after:";
+ $this->search_and_show_list($oMyFilter);
+ }
+
+ function test_error()
+ {
+ trigger_error("Stop requested", E_USER_ERROR);
+ }
+
+ function test_changetracking()
+ {
+ echo "Create a change
";
+ $oMyChange = MetaModel::NewObject("CMDBChange");
+ $oMyChange->Set("date", time());
+ $oMyChange->Set("userinfo", "Made by robot #".rand(1,100));
+ $iChangeId = $oMyChange->DBInsert();
+ echo "Created new change: $iChangeId";
+ self::DumpVariable($oMyChange);
+
+ echo "Create a new object (team)
";
+ $oNewTeam = MetaModel::NewObject("cmdbTeam");
+ $oNewTeam->Set("name", "ekip2choc #".rand(1000, 2000));
+ $oNewTeam->Set("email", "machin".rand(1,100)."@tnut.com");
+ $oNewTeam->Set("email", null);
+ $oNewTeam->Set("owner", "ITOP");
+ $oNewTeam->Set("headcount", "0".rand(38000, 38999)); // should be reset to an int value
+ $iId = $oNewTeam->DBInsertTracked($oMyChange);
+ echo "Created new team: $iId";
+ echo "Delete team #$iId
";
+ $oTeam = MetaModel::GetObject("cmdbTeam", $iId);
+ $oTeam->DBDeleteTracked($oMyChange);
+ echo "Deleted team: $iId";
+ self::DumpVariable($oTeam);
+ }
+
+ function test_zlist()
+ {
+ echo "Test ZLists
";
+ $aZLists = MetaModel::EnumZLists();
+ foreach ($aZLists as $sListCode)
+ {
+ $aListInfos = MetaModel::GetZListInfo($sListCode);
+ echo "List '".$sListCode."' (".$aListInfos["description"].") of type '".$aListInfos["type"]."'\n";
+
+ foreach (MetaModel::GetSubclasses("cmdbObjectHomeMade") as $sKlass)
+ {
+ $aItems = MetaModel::GetZListItems($sKlass, $sListCode);
+ if (count($aItems) == 0) continue;
+
+ echo "$sKlass - $sListCode : {".implode(", ", $aItems)."}\n";
+ }
+ }
+
+ echo "IsAttributeInZList()...
";
+ echo "Liens_entre_contacts_et_workshop::ws_info in list1 ? ".(MetaModel::IsAttributeInZList("Liens_entre_contacts_et_workshop", "list1", "ws_info") ? "yes" : "no")."\n";
+ echo "Liens_entre_contacts_et_workshop::toworkshop in list1 ? ".(MetaModel::IsAttributeInZList("Liens_entre_contacts_et_workshop", "list1", "toworkshop") ? "yes" : "no")."\n";
+
+ }
+
+ function test_pkey()
+ {
+ echo "Test search on pkey
";
+ $sExpr1 = "SELECT cmdbContact WHERE id IN (40, 42)";
+ $sExpr2 = "SELECT cmdbContact WHERE IN NOT IN (40, 42)";
+ $this->search_and_show_list_from_oql($sExpr1);
+ $this->search_and_show_list_from_oql($sExpr2);
+
+ echo "Et maintenant, on fusionne....\n";
+ $oSet1 = new CMDBObjectSet(DBObjectSearch::FromOQL($sExpr1));
+ $oSet2 = new CMDBObjectSet(DBObjectSearch::FromOQL($sExpr2));
+ $oIntersect = $oSet1->CreateIntersect($oSet2);
+ $oDelta = $oSet1->CreateDelta($oSet2);
+
+ $oMerge = clone $oSet1;
+ $oMerge->Merge($oSet2);
+ $oMerge->Merge($oSet2);
+
+ echo "Set1 - Found ".$oSet1->Count()." items.\n";
+ echo "Set2 - Found ".$oSet2->Count()." items.\n";
+ echo "Intersect - Found ".$oIntersect->Count()." items.\n";
+ echo "Delta - Found ".$oDelta->Count()." items.\n";
+ echo "Merge - Found ".$oMerge->Count()." items.\n";
+ //$this->show_list($oObjSet);
+ }
+
+ function test_relations()
+ {
+ echo "Test relations
";
+
+ //self::DumpVariable(MetaModel::EnumRelationQueries("cmdbObjectHomeMade", "Potes"));
+ self::DumpVariable(MetaModel::EnumRelationQueries("cmdbContact", "Potes"));
+
+ $iMaxDepth = 9;
+ echo "Max depth = $iMaxDepth\n";
+
+ $oObj = MetaModel::GetObject("cmdbContact", 18);
+ $aRels = $oObj->GetRelatedObjects("Potes", $iMaxDepth);
+ echo $oObj->Get('name')." has some 'Potes'...\n";
+ foreach ($aRels as $sClass => $aObjs)
+ {
+ echo "$sClass, count = ".count($aObjs)." => ".implode(', ', array_keys($aObjs))."\n";
+ $oObjectSet = CMDBObjectSet::FromArray($sClass, $aObjs);
+ $this->show_list($oObjectSet);
+ }
+
+ echo "Test relations - same results, by the mean of a OQL
";
+ $this->search_and_show_list_from_oql("cmdbContact: RELATED (Potes, $iMaxDepth) TO (cmdbContact: pkey = 18)");
+
+ }
+
+ function test_linkedset()
+ {
+ echo "Linked set attributes
\n";
+ $oObj = MetaModel::GetObject("cmdbContact", 18);
+
+ echo "Current workshops
\n";
+ $oSetWorkshopsCurr = $oObj->Get("myworkshops");
+ $this->show_list($oSetWorkshopsCurr);
+
+ echo "Setting workshops
\n";
+ $oNewLink = new cmdbLiens();
+ $oNewLink->Set('toworkshop', 2);
+ $oNewLink->Set('function', 'mafonctioooon');
+ $oNewLink->Set('a1', 'tralala1');
+ $oNewLink->Set('a2', 'F7M');
+ $oSetWorkshops = CMDBObjectSet::FromArray("cmdbLiens", array($oNewLink));
+ $oObj->Set("myworkshops", $oSetWorkshops);
+ $this->show_list($oSetWorkshops);
+
+ echo "New workshops
\n";
+ $oSetWorkshopsCurr = $oObj->Get("myworkshops");
+ $this->show_list($oSetWorkshopsCurr);
+
+ $oMyChange = MetaModel::NewObject("CMDBChange");
+ $oMyChange->Set("date", time());
+ $oMyChange->Set("userinfo", "test_linkedset / Made by robot #".rand(1,100));
+ $iChangeId = $oMyChange->DBInsert();
+ $oObj->DBUpdateTracked($oMyChange);
+ $oObj = MetaModel::GetObject("cmdbContact", 18);
+
+ echo "After the write
\n";
+ $oSetWorkshopsCurr = $oObj->Get("myworkshops");
+ $this->show_list($oSetWorkshopsCurr);
+ }
+
+ function test_object_lifecycle()
+ {
+ echo "Test object lifecycle
";
+
+
+ self::DumpVariable(MetaModel::GetStateAttributeCode("cmdbContact"));
+ self::DumpVariable(MetaModel::EnumStates("cmdbContact"));
+ self::DumpVariable(MetaModel::EnumStimuli("cmdbContact"));
+ foreach(MetaModel::EnumStates("cmdbContact") as $sStateCode => $aStateDef)
+ {
+ echo "
Transition from $sStateCode
\n";
+ self::DumpVariable(MetaModel::EnumTransitions("cmdbContact", $sStateCode));
+ }
+
+ $oObj = MetaModel::GetObject("cmdbContact", 18);
+ echo "Current state: ".$oObj->GetState()."... let's go to school...";
+ self::DumpVariable($oObj->EnumTransitions());
+ $oObj->ApplyStimulus("toschool");
+ echo "New state: ".$oObj->GetState()."... let's get older...";
+ self::DumpVariable($oObj->EnumTransitions());
+ $oObj->ApplyStimulus("raise");
+ echo "New state: ".$oObj->GetState()."... let's try to go further... (should give an error)";
+ self::DumpVariable($oObj->EnumTransitions());
+ $oObj->ApplyStimulus("raise"); // should give an error
+ }
+
+
+ protected function DoExecute()
+ {
+// $this->ReportError("Found two different OQL expression out of the (same?) filter: $sExpr1 != $sExpr2");
+// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName");
+ //$this->test_linksinfo();
+ //$this->test_list_attributes();
+ //$this->test_search();
+ //$this->test_reload();
+ //$this->test_newobject();
+ $this->test_setattribute();
+ //$this->test_updatecolumn();
+ //$this->test_error();
+ //$this->test_changetracking();
+ $this->test_zlist();
+ $this->test_OQL();
+ //$this->test_pkey();
+ $this->test_relations();
+ $this->test_linkedset();
+ $this->test_object_lifecycle();
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// Test a complex biz model on the fly
+///////////////////////////////////////////////////////////////////////////
+
+abstract class MyFarm extends TestBizModel
+{
+ static public function GetConfigFile() {return '../config-test-farm.php';}
+
+ protected function DoPrepare()
+ {
+ parent::DoPrepare();
+ $this->ResetDB();
+ MetaModel::DBCheckIntegrity();
+ }
+
+ protected function InsertMammal($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $sName, $iHeight, $sBirth)
+ {
+ $oNew = MetaModel::NewObject('Mammal');
+ $oNew->Set('species', $sSpecies);
+ $oNew->Set('sex', $sSex);
+ $oNew->Set('speed', $iSpeed);
+ $oNew->Set('mother', $iMotherid);
+ $oNew->Set('father', $iFatherId);
+ $oNew->Set('name', $sName);
+ $oNew->Set('height', $iHeight);
+ $oNew->Set('birth', $sBirth);
+ return $this->ObjectToDB($oNew);
+ }
+
+ protected function InsertBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId)
+ {
+ $oNew = MetaModel::NewObject('Bird');
+ $oNew->Set('species', $sSpecies);
+ $oNew->Set('sex', $sSex);
+ $oNew->Set('speed', $iSpeed);
+ $oNew->Set('mother', $iMotherid);
+ $oNew->Set('father', $iFatherId);
+ return $this->ObjectToDB($oNew);
+ }
+
+ protected function InsertFlyingBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $iFlyingSpeed)
+ {
+ $oNew = MetaModel::NewObject('FlyingBird');
+ $oNew->Set('species', $sSpecies);
+ $oNew->Set('sex', $sSex);
+ $oNew->Set('speed', $iSpeed);
+ $oNew->Set('mother', $iMotherid);
+ $oNew->Set('father', $iFatherId);
+ $oNew->Set('flyingspeed', $iFlyingSpeed);
+ return $this->ObjectToDB($oNew);
+ }
+
+ private function InsertGroup($sName, $iLeaderId)
+ {
+ $oNew = MetaModel::NewObject('Group');
+ $oNew->Set('name', $sName);
+ $oNew->Set('leader', $iLeaderId);
+ $iId = $oNew->DBInsertNoReload();
+ return $iId;
+ }
+}
+
+
+class TestQueriesOnFarm extends MyFarm
+{
+ static public function GetName()
+ {
+ return 'Farm test';
+ }
+
+ static public function GetDescription()
+ {
+ return 'A series of tests on the farm business model (SQL generation)';
+ }
+
+ protected function CheckQuery($sQuery, $bIsCorrectQuery)
+ {
+ if ($bIsCorrectQuery)
+ {
+ echo "$sQuery
\n";
+ }
+ else
+ {
+ echo "$sQuery\n";
+ }
+ try
+ {
+ //$oOql = new OqlInterpreter($sQuery);
+ //$oTrash = $oOql->ParseObjectQuery();
+ //self::DumpVariable($oTrash, true);
+ $oMyFilter = DBObjectSearch::FromOQL($sQuery);
+ }
+ catch (OQLException $oOqlException)
+ {
+ if ($bIsCorrectQuery)
+ {
+ echo "
More info on this unexpected failure:
".$oOqlException->getHtmlDesc()."
\n";
+ throw $oOqlException;
+ return false;
+ }
+ else
+ {
+ // Everything is fine :-)
+ echo "More info on this expected failure:\n";
+ echo "
\n";
+ echo "- ".get_class($oOqlException)."
\n";
+ echo "- ".$oOqlException->getMessage()."
\n";
+ echo "- ".$oOqlException->getHtmlDesc()."
\n";
+ echo "
\n";
+ echo "
\n";
+ return true;
+ }
+ }
+ // The query was correctly parsed, was it expected to be correct ?
+ if (!$bIsCorrectQuery)
+ {
+ throw new UnitTestException("The query '$sQuery' was parsed with success, while it shouldn't (?)");
+ return false;
+ }
+ echo "To OQL: ".$oMyFilter->ToOQL()."
";
+
+ $this->search_and_show_list($oMyFilter);
+
+ //echo "first pass
\n";
+ //self::DumpVariable($oMyFilter, true);
+ $sQuery1 = MetaModel::MakeSelectQuery($oMyFilter);
+ //echo "
second pass
\n";
+ //self::DumpVariable($oMyFilter, true);
+ //$sQuery1 = MetaModel::MakeSelectQuery($oMyFilter);
+
+ $sSerialize = $oMyFilter->serialize();
+ echo "
Serialized:$sSerialize
\n";
+ $oFilter2 = DBObjectSearch::unserialize($sSerialize);
+ try
+ {
+ $sQuery2 = MetaModel::MakeSelectQuery($oFilter2);
+ }
+ catch (Exception $e)
+ {
+ echo "Could not compute the query after unserialize
\n";
+ echo "Query 1: $sQuery1
\n";
+ MyHelpers::var_cmp_html($oMyFilter, $oFilter2);
+ throw $e;
+ }
+ //if ($oFilter2 != $oMyFilter) no, they may differ while the resulting query is the same!
+ if ($sQuery1 != $sQuery2)
+ {
+ echo "serialize/unserialize mismatch :-(
\n";
+ MyHelpers::var_cmp_html($sQuery1, $sQuery2);
+ MyHelpers::var_cmp_html($oMyFilter, $oFilter2);
+ return false;
+ }
+ return true;
+ }
+
+ protected function DoExecute()
+ {
+// $this->ReportError("Found two different OQL expression out of the (same?) filter: $sExpr1 != $sExpr2");
+// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName");
+ echo "Create protagonists...
";
+
+ $iId1 = $this->InsertMammal('human', 'male', 10, 0, 0, 'romanoff', 192, '1971-07-19');
+ $iId2 = $this->InsertMammal('human', 'female', 9, 0, 0, 'rouanita', 165, '1983-01-23');
+ $this->InsertMammal('human', 'female', 3, $iId2, $iId1, 'pomme', 169, '2008-02-23');
+ $this->InsertMammal('pig', 'female', 3, 0, 0, 'grouinkette', 85, '2006-06-01');
+ $this->InsertMammal('donkey', 'female', 3, 0, 0, 'muleta', 124, '2003-11-11');
+
+ $this->InsertBird('rooster', 'male', 12, 0, 0);
+ $this->InsertFlyingBird('pie', 'female', 11, 0, 0, 35);
+
+ // Benchmarking
+ //
+ if (false)
+ {
+ define ('COUNT_BENCHMARK', 10);
+ echo "Parsing a long query, ".COUNT_BENCHMARK." times
";
+ $sQuery = "SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.birth < DATE_SUB(CURRENT_DATE(), INTERVAL 10 YEAR) AND Dad.height * 2 <= ROUND(TO_DAYS(Dad.birth) / (3 + 1) * 5 - 3)";
+
+ $fStart = MyHelpers::getmicrotime();
+ for($i=0 ; $i < COUNT_BENCHMARK ; $i++)
+ {
+ $oMyFilter = DBObjectSearch::FromOQL($sQuery);
+ }
+ $fDuration = MyHelpers::getmicrotime() - $fStart;
+ $fParsingDuration = $fDuration / COUNT_BENCHMARK;
+ echo "Mean time by op: $fParsingDuration
";
+ }
+
+ echo "Test queries...
";
+
+ $aQueries = array(
+ 'SELECT Animal' => true,
+ 'SELECT Animal WHERE Animal.pkey = 1' => false,
+ 'SELECT Animal WHERE Animal.id = 1' => true,
+ 'SELECT Aniiimal' => false,
+ 'SELECTe Animal' => false,
+ 'SELECT * FROM Animal' => false,
+ 'SELECT Animal AS zoo WHERE zoo.species = \'human\'' => true,
+ 'SELECT Animal AS zoo WHERE species = \'human\'' => true,
+ 'SELECT Animal AS zoo WHERE espece = \'human\'' => false,
+ 'SELECT Animal AS zoo WHERE zoo.species IN (\'human\', "pig")' => true,
+ 'SELECT Animal AS zoo WHERE CONCATENATION(zoo.species, zoo.sex) LIKE "hum%male"' => false,
+ 'SELECT Animal AS zoo WHERE CONCAT(zoo.species, zoo.sex) LIKE "hum%male"' => true,
+ 'SELECT Animal AS zoo WHERE zoo.species NOT IN (\'human\', "pig")' => true,
+ 'SELECT Animal AS zoo WHERE zoo.kind = \'human\'' => false,
+ 'SELECT Animal WHERE Animal.species = \'human\' AND Animal.sex = \'female\'' => true,
+ 'SELECT Mammal AS x WHERE (x.species = \'human\' AND x.name LIKE \'ro%\') OR (x.species = \'donkey\' AND x.name LIKE \'po%\')' => true,
+ 'SELECT Mammal AS x WHERE x.species = \'human\' AND x.name LIKE \'ro%\' OR x.species = \'donkey\' AND x.name LIKE \'po%\'' => true,
+ 'SELECT Mammal AS m WHERE MONTH(m.birth) = 7' => true,
+ 'SELECT Mammal AS m WHERE DAY(m.birth) = 19' => true,
+ 'SELECT Mammal AS m WHERE YEAR(m.birth) = 1971' => true,
+ 'SELECT Mammal AS m WHERE m.birth < DATE_SUB(CURRENT_DATE(), INTERVAL 10 YEAR)' => true,
+ 'SELECT Mammal AS m WHERE m.birth > DATE_SUB(NOW(), INTERVAL 2000 DAY)' => true,
+ 'SELECT Mammal AS m WHERE (TO_DAYS(NOW()) - TO_DAYS(m.birth)) > 2000' => true,
+ 'SELECT Mammal AS m WHERE m.name = IF(FLOOR(ROUND(m.height)) > 2, "pomme", "romain")' => true,
+ 'SELECT Mammal AS m WHERE (1 + 2' => false,
+ 'SELECT Mammal AS m WHERE (1 + 2 * 4 / 23) > 0' => true,
+ 'SELECT Mammal AS m WHERE (4 / 23 * 2 + 1) > 0' => true,
+ 'SELECT Mammal AS m WHERE 1/0' => true,
+ 'SELECT Mammal AS m WHERE MONTH(m.birth) = 7' => true,
+ 'SELECT Animal JOIN Group ON Group.leader = Animal.id' => true,
+ 'SELECT Group JOIN Animal ON Group.leader = Animal.id' => true,
+ 'SELECT Animal AS A JOIN Group AS G1 ON G1.leader = A.id' => true,
+ 'SELECT Animal AS A JOIN Group AS G ON FooClass.leader = A.id' => false,
+ 'SELECT Animal AS A JOIN Group AS G ON G.leader = FooClass.id' => false,
+ 'SELECT Animal AS A JOIN Group AS G ON G.masterchief = A.id' => false,
+ 'SELECT Animal AS A JOIN Group AS G ON A.id = G.leader' => false,
+ 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.sex=\'male\' OR G.qwerty = 123' => false,
+ 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.sex=\'male\' OR G.name LIKE "a%"' => true,
+ 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.id = 1' => true,
+ 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE id = 1' => false,
+ 'SELECT Animal AS A JOIN Group AS G ON A.member = G.id' => false,
+ 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id' => true,
+ 'SELECT Mammal AS M JOIN Group AS G ON A.member = G.id' => false,
+ 'SELECT Mammal AS myAlias JOIN Group AS myAlias ON myAlias.member = myAlias.id' => false,
+ 'SELECT Mammal AS Mammal JOIN Group AS Mammal ON Mammal.member = Mammal.id' => false,
+ 'SELECT Group AS G WHERE G.leader_name LIKE "%"' => true,
+ 'SELECT Group AS G WHERE G.leader_speed < 100000' => true,
+ 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_name LIKE "%"' => true,
+ 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_speed < 100000' => true,
+ 'SELECT Mammal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true,
+ 'SELECT Mammal AS Child JOIN Animal AS Dad ON Child.father = Dad.id' => true,
+ 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true,
+ 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id' => true,
+ 'SELECT Animal AS Dad JOIN Animal AS Child ON Child.father = Dad.id' => true,
+ 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id' => true,
+ 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.id = 1' => true,
+ 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\'' => false,
+ 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true,
+ 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\' OR Mum.speed = 0' => true,
+ 'SELECT Animal AS Dad JOIN Animal AS Child ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id' => true,
+ 'SELECT Mammal AS Dad JOIN Mammal AS Child ON Child.father = Dad.id' => true,
+ 'SELECT Mammal AS Dad JOIN Mammal AS Child ON Child.father = Dad.id JOIN Mammal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\' OR Mum.name=\'chloe\' OR Child.name=\'bizounours\'' => true,
+ // Specifying multiple objects
+ 'SELECT Animal FROM Animal' => true,
+ 'SELECT yelele FROM Animal' => false,
+ 'SELECT Animal FROM Animal AS A' => false,
+ 'SELECT A FROM Animal AS A' => true,
+ );
+ //$aQueries = array(
+ // 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_name LIKE "%"' => true,
+ //);
+ foreach($aQueries as $sQuery => $bIsCorrect)
+ {
+ $this->CheckQuery($sQuery, $bIsCorrect);
+ }
+ return true;
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// Test data load
+///////////////////////////////////////////////////////////////////////////
+
+class TestBulkChangeOnFarm extends TestBizModel
+{
+ static public function GetName()
+ {
+ return 'Farm test - data load';
+ }
+
+ static public function GetDescription()
+ {
+ return 'Bulk load';
+ }
+
+ static public function GetConfigFile() {return '../config-test-farm.php';}
+
+ protected function DoPrepare()
+ {
+ parent::DoPrepare();
+ $this->ResetDB();
+ MetaModel::DBCheckIntegrity();
+ }
+
+ protected function DoExecute()
+ {
+// $this->ReportError("Found two different OQL expression out of the (same?) filter: $sExpr1 != $sExpr2");
+// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName");
+
+ $oParser = new CSVParser("denomination,hauteur,age
+ suzy,123,2009-01-01
+ chita,456,
+ ");
+ $aData = $oParser->ToArray(array('_name', '_height', '_birth'), ',');
+ self::DumpVariable($aData);
+
+ $oBulk = new BulkChange(
+ 'Mammal',
+ $aData,
+ // attributes
+ array('name' => '_name', 'height' => '_height', 'birth' => '_birth'),
+ // ext keys
+ array(),
+ // reconciliation
+ array('name')
+ );
+
+ $oMyChange = MetaModel::NewObject("CMDBChange");
+ $oMyChange->Set("date", time());
+ $oMyChange->Set("userinfo", "Testor");
+ $iChangeId = $oMyChange->DBInsert();
+// echo "Created new change: $iChangeId";
+
+ echo "Planned for loading...
";
+ $aRes = $oBulk->Process();
+ self::DumpVariable($aRes);
+ echo "Go for loading...
";
+ $aRes = $oBulk->Process($oMyChange);
+ self::DumpVariable($aRes);
+
+ return;
+
+ $oRawData = array(
+ 'Mammal',
+ array('species', 'sex', 'speed', 'mother', 'father', 'name', 'height', 'birth'),
+ "human,male,23,0,0,romulus,192,1971
+ human,male,23,0,0,remus,154,-50
+ human,male,23,0,0,julius,160,-49
+ human,female,23,0,0,cleopatra,142,-50
+ pig,female,23,0,0,confucius,50,2003"
+ );
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// Test data load
+///////////////////////////////////////////////////////////////////////////
+
+class TestFullTextSearchOnFarm extends MyFarm
+{
+ static public function GetName()
+ {
+ return 'Farm test - full text search';
+ }
+
+ static public function GetDescription()
+ {
+ return 'Focus on the full text search feature';
+ }
+
+ protected function DoExecute()
+ {
+ echo "Create protagonists...
";
+
+ $iId1 = $this->InsertMammal('human', 'male', 10, 0, 0, 'romanoff', 192, '1971-07-19');
+ $iId2 = $this->InsertMammal('human', 'female', 9, 0, 0, 'rouanita', 165, '1983-01-23');
+ $this->InsertMammal('human', 'female', 3, $iId2, $iId1, 'pomme', 169, '2008-02-23');
+ $this->InsertMammal('pig', 'female', 3, 0, 0, 'grouinkette', 85, '2006-06-01');
+ $this->InsertMammal('donkey', 'female', 3, 0, 0, 'muleta', 124, '2003-11-11');
+
+ $this->InsertBird('rooster', 'male', 12, 0, 0);
+ $this->InsertFlyingBird('pie', 'female', 11, 0, 0, 35);
+
+ echo "Search...
";
+ $oSearch = new DBObjectSearch('Mammal');
+ $oSearch->AddCondition_FullText('manof');
+ //$oResultSet = new DBObjectSet($oSearch);
+ $this->search_and_show_list($oSearch);
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// Benchmark queries
+///////////////////////////////////////////////////////////////////////////
+
+class TestItopEfficiency extends TestBizModel
+{
+ static public function GetName()
+ {
+ return 'Itop - benchmark';
+ }
+
+ static public function GetDescription()
+ {
+ return 'Measure time to perform the queries';
+ }
+
+ static public function GetConfigFile() {return '../config-itop.php';}
+
+ protected function DoBenchmark($sOqlQuery)
+ {
+ echo "Testing query: $sOqlQuery
";
+
+ $fStart = MyHelpers::getmicrotime();
+ for($i=0 ; $i < COUNT_BENCHMARK ; $i++)
+ {
+ $oFilter = DBObjectSearch::FromOQL($sOqlQuery);
+ }
+ $fDuration = MyHelpers::getmicrotime() - $fStart;
+ $fParsingDuration = $fDuration / COUNT_BENCHMARK;
+
+ $fStart = MyHelpers::getmicrotime();
+ for($i=0 ; $i < COUNT_BENCHMARK ; $i++)
+ {
+ $sSQL = MetaModel::MakeSelectQuery($oFilter);
+ }
+ $fDuration = MyHelpers::getmicrotime() - $fStart;
+ $fBuildDuration = $fDuration / COUNT_BENCHMARK;
+
+ $fStart = MyHelpers::getmicrotime();
+ for($i=0 ; $i < COUNT_BENCHMARK ; $i++)
+ {
+ $res = CMDBSource::Query($sSQL);
+ }
+ $fDuration = MyHelpers::getmicrotime() - $fStart;
+ $fQueryDuration = $fDuration / COUNT_BENCHMARK;
+
+ // The fetch could not be repeated with the same results
+ // But we've seen so far that is was very very quick to exec
+ // So it makes sense to benchmark it a single time
+ $fStart = MyHelpers::getmicrotime();
+ $aRow = CMDBSource::FetchArray($res);
+ $fDuration = MyHelpers::getmicrotime() - $fStart;
+ $fFetchDuration = $fDuration;
+
+ $fStart = MyHelpers::getmicrotime();
+ for($i=0 ; $i < COUNT_BENCHMARK ; $i++)
+ {
+ $sOql = $oFilter->ToOQL();
+ }
+ $fDuration = MyHelpers::getmicrotime() - $fStart;
+ $fToOqlDuration = $fDuration / COUNT_BENCHMARK;
+
+ echo "\n";
+ echo "- Parsing: $fParsingDuration
\n";
+ echo "- Build: $fBuildDuration
\n";
+ echo "- Query: $fQueryDuration
\n";
+ echo "- Fetch: $fFetchDuration
\n";
+ echo "- ToOql: $fToOqlDuration
\n";
+ echo "
\n";
+
+ // Everything but the ToOQL (wich is interesting, anyhow)
+ $fTotal = $fParsingDuration + $fBuildDuration + $fQueryDuration + $fFetchDuration;
+
+ return array(
+ 'rows' => CMDBSource::NbRows($res),
+ 'duration (s)' => round($fTotal, 4),
+ 'parsing (%)' => round(100 * $fParsingDuration / $fTotal, 1),
+ 'build SQL (%)' => round(100 * $fBuildDuration / $fTotal, 1),
+ 'query exec (%)' => round(100 * $fQueryDuration / $fTotal, 1),
+ 'fetch (%)' => round(100 * $fFetchDuration / $fTotal, 1),
+ 'to OQL (%)' => round(100 * $fToOqlDuration / $fTotal, 1),
+ 'parsing+build (%)' => round(100 * ($fParsingDuration + $fBuildDuration) / $fTotal, 1),
+ );
+ }
+
+ protected function DoExecute()
+ {
+ define ('COUNT_BENCHMARK', 3);
+ echo "The test will be repeated ".COUNT_BENCHMARK." times
";
+
+ $aQueries = array(
+ 'SELECT CMDBChangeOpSetAttribute',
+ 'SELECT CMDBChangeOpSetAttribute WHERE id=10',
+ 'SELECT CMDBChangeOpSetAttribute WHERE id=123456789',
+ 'SELECT CMDBChangeOpSetAttribute WHERE CMDBChangeOpSetAttribute.id=10',
+ 'SELECT Ticket',
+ 'SELECT Ticket WHERE id=1',
+ 'SELECT Person',
+ 'SELECT Person WHERE id=1',
+ 'SELECT Server',
+ 'SELECT Server WHERE id=1',
+ 'SELECT Incident JOIN Person ON Incident.agent_id = Person.id WHERE Person.id = 5',
+ );
+ $aStats = array();
+ foreach ($aQueries as $sOQL)
+ {
+ $aStats[$sOQL] = $this->DoBenchmark($sOQL);
+ }
+
+ $aData = array();
+ foreach ($aStats as $sOQL => $aResults)
+ {
+ $aValues = array();
+ $aValues['OQL'] = htmlentities($sOQL);
+
+ foreach($aResults as $sDesc => $sInfo)
+ {
+ $aValues[$sDesc] = htmlentities($sInfo);
+ }
+ $aData[] = $aValues;
+ }
+ echo MyHelpers::make_table_from_assoc_array($aData);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Test data load
+///////////////////////////////////////////////////////////////////////////
+
+class TestImportREST extends TestWebServices
+{
+ static public function GetName()
+ {
+ return 'CSV import (REST)';
+ }
+
+ static public function GetDescription()
+ {
+ return 'Test various options and fonctionality of import.php';
+ }
+
+ protected function DoExecSingleLoad($aLoadSpec)
+ {
+ $sCsvData = $aLoadSpec['csvdata'];
+
+ echo "\n";
+ echo "
{$aLoadSpec['desc']}
\n";
+
+ $aPostData = array('csvdata' => $sCsvData);
+
+ $aGetParams = array();
+ $aGetParamReport = array();
+ foreach($aLoadSpec['args'] as $sArg => $sValue)
+ {
+ $aGetParams[] = $sArg.'='.urlencode($sValue);
+ $aGetParamReport[] = $sArg.'='.$sValue;
+ }
+ $sGetParams = implode('&', $aGetParams);
+ $sLogin = isset($aLoadSpec['login']) ? $aLoadSpec['login'] : 'admin';
+ $sPassword = isset($aLoadSpec['password']) ? $aLoadSpec['password'] : 'admin';
+
+ $sRes = self::DoPostRequestAuth('../webservices/import.php?'.$sGetParams, $aPostData, $sLogin, $sPassword);
+
+ $sArguments = implode('
', $aGetParamReport);
+
+ if (strlen($sCsvData) > 5000)
+ {
+ $sCsvDataViewable = 'INPUT TOO LONG TO BE DISPLAYED ('.strlen($sCsvData).")\n".substr($sCsvData, 0, 500)."\n... TO BE CONTINUED";
+ }
+ else
+ {
+ $sCsvDataViewable = $sCsvData;
+ }
+
+ echo "
\n";
+ echo "
\n";
+ echo " $sArguments\n";
+ echo "
\n";
+ echo "
\n";
+ echo "
$sCsvDataViewable
\n";
+ echo "
\n";
+ echo "
\n";
+
+ echo "
$sRes
\n";
+
+ echo "
\n";
+ }
+
+ protected function DoExecute()
+ {
+
+ $aLoads = array(
+ array(
+ 'desc' => 'Missing class',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ ),
+ 'csvdata' => "xxx",
+ ),
+ array(
+ 'desc' => 'Wrong class',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'toto',
+ ),
+ 'csvdata' => "xxx",
+ ),
+ array(
+ 'desc' => 'Wrong output type',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'NetworkDevice',
+ 'output' => 'onthefly',
+ ),
+ 'csvdata' => "xxx",
+ ),
+ array(
+ 'desc' => 'Wrong report level',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'NetworkDevice',
+ 'reportlevel' => 'errors|ouarnings|changed',
+ ),
+ 'csvdata' => "xxx",
+ ),
+ array(
+ 'desc' => 'Weird format, working anyhow...',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Server',
+ 'output' => 'details',
+ 'separator' => '*',
+ 'qualifier' => '@',
+ 'reconciliationkeys' => 'org_id,name',
+ ),
+ 'csvdata' => 'name*org_id
+ server01*2
+ @server02@@combodo@* 2
+ server45*99',
+ ),
+ array(
+ 'desc' => 'Load an organization',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Organization',
+ 'output' => 'details',
+ 'reconciliationkeys' => '',
+ ),
+ 'csvdata' => "name;code\nWorldCompany;WCY",
+ ),
+ array(
+ 'desc' => 'Load a location',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Location',
+ 'output' => 'details',
+ 'reconciliationkeys' => '',
+ ),
+ 'csvdata' => "name;org_id;address\nParis;1;Centre de la Franca",
+ ),
+ array(
+ 'desc' => 'Load a person',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Person',
+ 'output' => 'details',
+ 'reconciliationkeys' => '',
+ ),
+ 'csvdata' => "email;name;first_name;org_id;phone\njohn.foo@starac.com;Foo;John;1;+33(1)23456789",
+ ),
+ array(
+ 'desc' => 'Load a person - wrong email format',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Person',
+ 'output' => 'details',
+ 'reconciliationkeys' => '',
+ ),
+ 'csvdata' => "email;name;first_name;org_id\nemailPASbon;Foo;John;1",
+ ),
+ array(
+ 'desc' => 'Load a team',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Team',
+ 'output' => 'details',
+ 'reconciliationkeys' => '',
+ ),
+ 'csvdata' => "name;org_id;location_name\nSquadra Azzura2;1;Paris",
+ ),
+ array(
+ 'desc' => 'Load server',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Server',
+ 'output' => 'details',
+ 'reconciliationkeys' => '',
+ ),
+ 'csvdata' => "name;status;owner_name;location_name;location_id->org_name;os_family;os_version;management_ip;cpu;ram;brand;model;serial_number\nlocalhost.;production;Demo;Grenoble;Demo;Ubuntu 9.10;2.6.31-19-generic-#56-Ubuntu SMP Thu Jan 28 01:26:53 UTC 2010;16.16.230.232;Intel(R) Core(TM)2 Duo CPU T7100 @ 1.80GHz;2005;Hewlett-Packard;HP Compaq 6510b (GM108UC#ABF);CNU7370BNP",
+ ),
+ array(
+ 'desc' => 'Load NW if',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'NetworkInterface',
+ 'output' => 'details',
+ 'reconciliationkeys' => '',
+ ),
+ 'csvdata' => "name;status;org_id;device_name;physical_type;ip_address;ip_mask;mac_address;speed\neth0;implementation;2;localhost.;ethernet;16.16.230.232;255.255.240.0;00:1a:4b:68:e3:97;\nlo;implementation;2;localhost.;ethernet;127.0.0.1;255.0.0.0;;",
+ ),
+ // Data Bruno
+ array(
+ 'desc' => 'Load NW devices from real life',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'NetworkDevice',
+ 'output' => 'details',
+ 'reconciliationkeys' => 'org_id->name,name',
+ ),
+ 'csvdata' => 'name;management_ip;importance;org_id->name;type
+ truc-machin-bidule;172.15.255.150;high;My Company/Department;switch
+ 10.15.255.222;10.15.255.222;high;My Company/Department;switch',
+ ),
+ array(
+ 'desc' => 'Load NW ifs',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'NetworkInterface',
+ 'output' => 'details',
+ 'reconciliationkeys' => 'device_id->name,name',
+ ),
+ 'csvdata' => 'device_id->name;org_id->name;name;ip_address;ip_mask;speed;link_type;mac_address;physical_type
+ truc-machin-bidule;My Company/Department;"GigabitEthernet44";;;0;downlink;00 12 F2 CB C4 EB ;ethernet
+ truc-machin-bidule;My Company/Department;"GigabitEthernet38";;;0;downlink;00 12 F2 CB C4 E5 ;ethernet
+ un-autre;My Company/Department;"GigabitEthernet2/3";;;1000000000;uplink;00 12 F2 20 0F 1A ;ethernet',
+ ),
+ array(
+ 'desc' => 'The simplest data load',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Location',
+ 'output' => 'details',
+ ),
+ 'csvdata' => "name\nParis",
+ ),
+ array(
+ 'desc' => 'The simplest data load + org',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Location',
+ 'output' => 'details',
+ ),
+ 'csvdata' => "name;org_id\nParis;2",
+ ),
+ array(
+ 'desc' => 'The simplest data load + org (name)',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Location',
+ 'output' => 'details',
+ ),
+ 'csvdata' => "name;org_name\nParis;Demo",
+ ),
+ array(
+ 'desc' => 'The simplest data load + org (code)',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Location',
+ 'output' => 'details',
+ ),
+ 'csvdata' => "name;org_id->code\nParis;DEMO",
+ ),
+ array(
+ 'desc' => 'Ouput: summary',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Location',
+ 'output' => 'summary',
+ ),
+ 'csvdata' => "name;org_id->code\nParis;DEMO",
+ ),
+ array(
+ 'desc' => 'Ouput: retcode',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Location',
+ 'output' => 'retcode',
+ ),
+ 'csvdata' => "name;org_id->code\nParis;DEMO",
+ ),
+ array(
+ 'desc' => 'Error in reconciliation list',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Location',
+ 'output' => 'details',
+ 'reconciliationkeys' => 'org_id',
+ ),
+ 'csvdata' => "org_name;name\nDemo;Paris",
+ ),
+ array(
+ 'desc' => 'Error in attribute list that does not allow to compute reconciliation scheme',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Location',
+ 'output' => 'details',
+ ),
+ 'csvdata' => "org_name;country\nDemo;France",
+ ),
+ array(
+ 'desc' => 'Error in attribute list - case A',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Location',
+ 'output' => 'details',
+ ),
+ 'csvdata' => "name;org\nParis;2",
+ ),
+ array(
+ 'desc' => 'Error in attribute list - case B1 (key->attcode)',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Location',
+ 'output' => 'details',
+ ),
+ 'csvdata' => "name;org->code\nParis;DEMO",
+ ),
+ array(
+ 'desc' => 'Error in attribute list - case B2 (key->attcode)',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Location',
+ 'output' => 'details',
+ ),
+ 'csvdata' => "name;org_id->duns\nParis;DEMO",
+ ),
+ array(
+ 'desc' => 'Always changing... special comment in change tracking',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Location',
+ 'output' => 'details',
+ 'comment' => 'automated testing'
+ ),
+ 'csvdata' => "org_name;name;address\nDemo;Le pantheon;Addresse bidon:".((string)microtime(true)),
+ ),
+ array(
+ 'desc' => 'Always changing... but "simulate"',
+ 'login' => 'admin',
+ 'password' => 'admin',
+ 'args' => array(
+ 'class' => 'Location',
+ 'output' => 'details',
+ 'simulate' => '1',
+ 'comment' => 'SHOULD NEVER APPEAR IN THE HISTORY'
+ ),
+ 'csvdata' => "org_name;name;address\nDemo;Le pantheon;restore address?",
+ ),
+ );
+
+ foreach ($aLoads as $aLoadSpec)
+ {
+ $this->DoExecSingleLoad($aLoadSpec);
+ }
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Test massive data load
+///////////////////////////////////////////////////////////////////////////
+define('IMPORT_COUNT', 1000);
+
+class TestImportRESTMassive extends TestImportREST
+{
+ static public function GetName()
+ {
+ return 'CSV import (REST) - HUGE data set ('.IMPORT_COUNT.' PCs)';
+ }
+
+ static public function GetDescription()
+ {
+ return 'Stress import.php';
+ }
+
+ protected function DoExecute()
+ {
+ $aLoadSpec = array(
+ 'desc' => 'Missing class',
+ 'args' => array(
+ 'class' => 'PC',
+ 'output' => 'summary',
+ ),
+ 'csvdata' => "name;org_id;brand\n",
+ );
+ for($i = 0 ; $i <= IMPORT_COUNT ; $i++)
+ {
+ $aLoadSpec['csvdata'] .= "pc.import.$i;2;Combodo\n";
+ }
+ $this->DoExecSingleLoad($aLoadSpec);
+ }
+}
+
+
+///////////////////////////////////////////////////////////////////////////
+// Test SOAP services
+///////////////////////////////////////////////////////////////////////////
+
+$aWebServices = array(
+ array(
+ 'verb' => 'GetVersion',
+ 'expected result' => WebServices::GetVersion(),
+ 'explain result' => 'no comment!',
+ 'args' => array(),
+ ),
+ array(
+ 'verb' => 'CreateIncidentTicket',
+ 'expected result' => true,
+ 'explain result' => 'link attribute unknown + a CI not found',
+ 'args' => array(
+ 'admin', /* sLogin */
+ 'admin', /* sPassword */
+ 'desc of ticket', /* sDescription */
+ 'initial situation blah blah blah', /* sInitialSituation */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Demo'))), /* aCustomerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'HW Management'))), /* aServiceDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aServiceSubcategoryDesc */
+ 'sub product of the service', /* sProduct */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Hardware support'))), /* aWorkgroupDesc */
+ array(
+ new SOAPLinkCreationSpec(
+ 'InfrastructureCI',
+ array(new SOAPSearchCondition('name', 'dbserver1.demo.com')),
+ array(new SOAPAttributeValue('impacting', 'very critical'))
+ ),
+ new SOAPLinkCreationSpec(
+ 'NetworkDevice',
+ array(new SOAPSearchCondition('name', 'switch01')),
+ array(new SOAPAttributeValue('impact', 'who cares'))
+ ),
+ new SOAPLinkCreationSpec(
+ 'Server',
+ array(new SOAPSearchCondition('name', 'thisone')),
+ array(new SOAPAttributeValue('impact', 'our lives'))
+ ),
+ ), /* aImpact */
+ '1', /* sImpact */
+ '1', /* sUrgency */
+ ),
+ ),
+ array(
+ 'verb' => 'CreateIncidentTicket',
+ 'expected result' => true,
+ 'explain result' => 'caller not specified',
+ 'args' => array(
+ 'admin', /* sLogin */
+ 'admin', /* sPassword */
+ 'PC burning', /* sDescription */
+ 'The power supply suddenly started to warm up', /* sInitialSituation */
+ new SOAPExternalKeySearch(), /* aCallerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Demo'))), /* aCustomerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'HW Management'))), /* aServiceDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aServiceSubcategoryDesc */
+ 'sub product of the service', /* sProduct */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Hardware support'))), /* aWorkgroupDesc */
+ array(
+ new SOAPLinkCreationSpec(
+ 'InfrastructureCI',
+ array(new SOAPSearchCondition('name', 'dbserver1.demo.com')),
+ array()
+ ), /* aImpact */
+ ),
+ '1', /* sImpact */
+ '1', /* sUrgency */
+ ),
+ ),
+ array(
+ 'verb' => 'CreateIncidentTicket',
+ 'expected result' => false,
+ 'explain result' => 'wrong class on CI to attach',
+ 'args' => array(
+ 'admin', /* sLogin */
+ 'admin', /* sPassword */
+ 'PC burning', /* sDescription */
+ 'The power supply suddenly started to warm up', /* sInitialSituation */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Demo'))), /* aCustomerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'HW Management'))), /* aServiceDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aServiceSubcategoryDesc */
+ 'sub product of the service', /* sProduct */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Hardware support'))), /* aWorkgroupDesc */
+ array(
+ new SOAPLinkCreationSpec(
+ 'logInfra',
+ array(new SOAPSearchCondition('dummyfiltercode', 2)),
+ array(new SOAPAttributeValue('impact', 'very critical'))
+ ),
+ ), /* aImpact */
+ '1', /* sImpact */
+ '1', /* sUrgency */
+ ),
+ ),
+ array(
+ 'verb' => 'CreateIncidentTicket',
+ 'expected result' => false,
+ 'explain result' => 'wrong search condition on CI to attach',
+ 'args' => array(
+ 'admin', /* sLogin */
+ 'admin', /* sPassword */
+ 'PC burning', /* sDescription */
+ 'The power supply suddenly started to warm up', /* sInitialSituation */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Demo'))), /* aCustomerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'HW Management'))), /* aServiceDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aServiceSubcategoryDesc */
+ 'sub product of the service', /* sProduct */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Hardware support'))), /* aWorkgroupDesc */
+ array(
+ new SOAPLinkCreationSpec(
+ 'InfrastructureCI',
+ array(new SOAPSearchCondition('dummyfiltercode', 2)),
+ array(new SOAPAttributeValue('impact', 'very critical'))
+ ),
+ ), /* aImpact */
+ '1', /* sImpact */
+ '1', /* sUrgency */
+ ),
+ ),
+ array(
+ 'verb' => 'CreateIncidentTicket',
+ 'expected result' => true,
+ 'explain result' => 'no CI to attach (empty array)',
+ 'args' => array(
+ 'admin', /* sLogin */
+ 'admin', /* sPassword */
+ 'Houston not reachable', /* sDescription */
+ 'Tried to join the shuttle', /* sInitialSituation */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Demo'))), /* aCustomerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'HW Management'))), /* aServiceDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aServiceSubcategoryDesc */
+ 'sub product of the service', /* sProduct */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Hardware support'))), /* aWorkgroupDesc */
+ array(
+ ), /* aImpact */
+ '1', /* sImpact */
+ '1', /* sUrgency */
+ ),
+ ),
+ array(
+ 'verb' => 'CreateIncidentTicket',
+ 'expected result' => true,
+ 'explain result' => 'no CI to attach (null)',
+ 'args' => array(
+ 'admin', /* sLogin */
+ 'admin', /* sPassword */
+ 'Houston not reachable', /* sDescription */
+ 'Tried to join the shuttle', /* sInitialSituation */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Demo'))), /* aCustomerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'HW Management'))), /* aServiceDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aServiceSubcategoryDesc */
+ 'sub product of the service', /* sProduct */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Hardware support'))), /* aWorkgroupDesc */
+ null, /* aImpact */
+ '1', /* sImpact */
+ '1', /* sUrgency */
+ ),
+ ),
+ array(
+ 'verb' => 'CreateIncidentTicket',
+ 'expected result' => true,
+ 'explain result' => 'caller unknown',
+ 'args' => array(
+ 'admin', /* sLogin */
+ 'admin', /* sPassword */
+ 'Houston not reachable', /* sDescription */
+ 'Tried to join the shuttle', /* sInitialSituation */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1000))), /* aCallerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Demo'))), /* aCustomerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'HW Management'))), /* aServiceDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aServiceSubcategoryDesc */
+ 'sub product of the service', /* sProduct */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Hardware support'))), /* aWorkgroupDesc */
+ array(
+ ), /* aImpact */
+ '1', /* sImpact */
+ '1', /* sUrgency */
+ ),
+ ),
+ array(
+ 'verb' => 'CreateIncidentTicket',
+ 'expected result' => false,
+ 'explain result' => 'wrong values for impact and urgency',
+ 'args' => array(
+ 'admin', /* sLogin */
+ 'admin', /* sPassword */
+ 'Houston not reachable', /* sDescription */
+ 'Tried to join the shuttle', /* sInitialSituation */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Demo'))), /* aCustomerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'HW Management'))), /* aServiceDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aServiceSubcategoryDesc */
+ 'sub product of the service', /* sProduct */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Hardware support'))), /* aWorkgroupDesc */
+ array(
+ ), /* aImpact */
+ '6', /* sImpact */
+ '7', /* sUrgency */
+ ),
+ ),
+ array(
+ 'verb' => 'CreateIncidentTicket',
+ 'expected result' => false,
+ 'explain result' => 'wrong password',
+ 'args' => array(
+ 'admin', /* sLogin */
+ 'xxxxx', /* sPassword */
+ 'Houston not reachable', /* sDescription */
+ 'Tried to join the shuttle', /* sInitialSituation */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Demo'))), /* aCustomerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'HW Management'))), /* aServiceDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aServiceSubcategoryDesc */
+ 'sub product of the service', /* sProduct */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Hardware support'))), /* aWorkgroupDesc */
+ array(
+ ), /* aImpact */
+ '1', /* sImpact */
+ '1', /* sUrgency */
+ ),
+ ),
+ array(
+ 'verb' => 'CreateIncidentTicket',
+ 'expected result' => false,
+ 'explain result' => 'wrong login',
+ 'args' => array(
+ 'xxxxx', /* sLogin */
+ 'yyyyy', /* sPassword */
+ 'Houston not reachable', /* sDescription */
+ 'Tried to join the shuttle', /* sInitialSituation */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Demo'))), /* aCustomerDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'HW Management'))), /* aServiceDesc */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aServiceSubcategoryDesc */
+ 'sub product of the service', /* sProduct */
+ new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Hardware support'))), /* aWorkgroupDesc */
+ array(
+ ), /* aImpact */
+ '1', /* sImpact */
+ '1', /* sUrgency */
+ ),
+ ),
+);
+
+
+class TestSoap extends TestSoapWebService
+{
+ static public function GetName() {return 'Test SOAP';}
+ static public function GetDescription() {return 'Do basic stuff to test the SOAP capability';}
+
+ protected function DoExecute()
+ {
+ echo "Note: You may also want to try the sample SOAP client itopsoap.examples.php
\n";
+
+ global $aSOAPMapping;
+
+ // this file is generated dynamically with location = here
+ $sWsdlUri = 'http'.(empty($_SERVER['HTTPS']) ? '' : 's').'://'.$_SERVER['SERVER_NAME'].':'.$_SERVER['SERVER_PORT'].dirname($_SERVER['SCRIPT_NAME']).'/../webservices/itop.wsdl.php';
+
+ ini_set("soap.wsdl_cache_enabled","0");
+ $this->m_SoapClient = new SoapClient
+ (
+ $sWsdlUri,
+ array(
+ 'classmap' => $aSOAPMapping,
+ 'trace' => 1,
+ )
+ );
+
+ if (false)
+ {
+ self::DumpVariable($this->m_SoapClient->__getTypes());
+ }
+
+ global $aWebServices;
+ foreach ($aWebServices as $iPos => $aWebService)
+ {
+ echo "SOAP call #$iPos - {$aWebService['verb']}
\n";
+ echo "{$aWebService['explain result']}
\n";
+
+ try
+ {
+ $oRes = call_user_func_array(array($this->m_SoapClient, $aWebService['verb']), $aWebService['args']);
+ }
+ catch(SoapFault $e)
+ {
+ print "\n";
+ print "Request: \n".htmlspecialchars($this->m_SoapClient->__getLastRequest()) ."\n";
+ print "Response: \n".htmlspecialchars($this->m_SoapClient->__getLastResponse())."\n";
+ print "
";
+ print "Response in HTML: ".$this->m_SoapClient->__getLastResponse()."
";
+ throw $e;
+ }
+
+ self::DumpVariable($oRes);
+
+ print "\n";
+ print "Request: \n".htmlspecialchars($this->m_SoapClient->__getLastRequest()) ."\n";
+ print "Response: \n".htmlspecialchars($this->m_SoapClient->__getLastResponse())."\n";
+ print "
";
+
+ if ($oRes instanceof SOAPResult)
+ {
+ $res = $oRes->status;
+ }
+ else
+ {
+ $res = $oRes;
+ }
+ if ($res != $aWebService['expected result'])
+ {
+ echo "Expecting:
\n";
+ var_dump($aWebService['expected result']);
+ echo "Obtained:
\n";
+ var_dump($res);
+ throw new UnitTestException("Expecting result '{$aWebService['expected result']}', but got '$res'");
+ }
+ }
+ }
+}
+
+class TestWebServicesDirect extends TestBizModel
+{
+ static public function GetName() {return 'Test web services locally';}
+ static public function GetDescription() {return 'Invoke the service directly (troubleshooting)';}
+
+ static public function GetConfigFile() {return '../config-itop.php';}
+
+ protected function DoExecute()
+ {
+ $oWebServices = new WebServices();
+
+ global $aWebServices;
+ foreach ($aWebServices as $iPos => $aWebService)
+ {
+ echo "SOAP call #$iPos - {$aWebService['verb']}
\n";
+ echo "{$aWebService['explain result']}
\n";
+ $oRes = call_user_func_array(array($oWebServices, $aWebService['verb']), $aWebService['args']);
+ self::DumpVariable($oRes);
+
+ if ($oRes instanceof SOAPResult)
+ {
+ $res = $oRes->status;
+ }
+ else
+ {
+ $res = $oRes;
+ }
+ if ($res != $aWebService['expected result'])
+ {
+ echo "Expecting:
\n";
+ var_dump($aWebService['expected result']);
+ echo "Obtained:
\n";
+ var_dump($res);
+ throw new UnitTestException("Expecting result '{$aWebService['expected result']}', but got '$res'");
+ }
+ }
+ return true;
+ }
+}
+
+class TestTriggerAndEmail extends TestBizModel
+{
+ static public function GetName() {return 'Test trigger and email';}
+ static public function GetDescription() {return 'Create a trigger and an email, then activates the trigger';}
+
+ static public function GetConfigFile() {return '../config-itop.php';}
+
+ protected function CreateEmailSpec($oTrigger, $sStatus, $sTo, $sCC, $sTesterEmail)
+ {
+ $oAction = MetaModel::NewObject("ActionEmail");
+ $oAction->Set("status", $sStatus);
+ $oAction->Set("name", "New server");
+ $oAction->Set("test_recipient", $sTesterEmail);
+ $oAction->Set("from", $sTesterEmail);
+ $oAction->Set("reply_to", $sTesterEmail);
+ $oAction->Set("to", $sTo);
+ $oAction->Set("cc", $sCC);
+ $oAction->Set("bcc", "");
+ $oAction->Set("subject", "New server: '\$this->name()$'");
+ $oAction->Set("body", "Dear customer,
We have created the server \$this->hyperlink()$ in the IT infrastructure database.
You will be further notified when it is in Production.
The IT infrastructure management team.
Here are some accentuated characters for french people: 'ééà'
");
+ $oAction->Set("importance", "low");
+ $iActionId = $this->ObjectToDB($oAction, true);
+
+ $oLink = MetaModel::NewObject("lnkTriggerAction");
+ $oLink->Set("trigger_id", $oTrigger->GetKey());
+ $oLink->Set("action_id", $iActionId);
+ $oLink->Set("order", "1");
+ $iLink = $this->ObjectToDB($oLink, true);
+ }
+
+ protected function DoExecute()
+ {
+ $oMyPerson = MetaModel::NewObject("bizPerson");
+ $oMyPerson->Set("name", "testemail1");
+ $oMyPerson->Set("org_id", "1");
+ $oMyPerson->Set("email", "romain.quetiez@hp.com");
+ $iPersonId = $this->ObjectToDB($oMyPerson, true);
+
+ $oMyPerson = MetaModel::NewObject("bizPerson");
+ $oMyPerson->Set("name", "testemail2");
+ $oMyPerson->Set("org_id", "1");
+ $oMyPerson->Set("email", "denis.flaven@hp.com");
+ $iPersonId = $this->ObjectToDB($oMyPerson, true);
+
+ $oMyPerson = MetaModel::NewObject("bizPerson");
+ $oMyPerson->Set("name", "testemail3");
+ $oMyPerson->Set("org_id", "1");
+ $oMyPerson->Set("email", "erwan.taloc@hp.com");
+ $iPersonId = $this->ObjectToDB($oMyPerson, true);
+
+ $oMyServer = MetaModel::NewObject("bizServer");
+ $oMyServer->Set("name", "wfr.terminator.com");
+ $oMyServer->Set("severity", "low");
+ $oMyServer->Set("status", "production");
+ $oMyServer->Set("org_id", 2);
+ $oMyServer->Set("location_id", 2);
+ $iServerId = $this->ObjectToDB($oMyServer, true);
+
+ $oMyTrigger = MetaModel::NewObject("TriggerOnStateEnter");
+ $oMyTrigger->Set("description", "Testor");
+ $oMyTrigger->Set("target_class", "bizServer");
+ $oMyTrigger->Set("state", "Shipped");
+ $iTriggerId = $this->ObjectToDB($oMyTrigger, true);
+
+ // Error in OQL field(s)
+ //
+ $this->CreateEmailSpec
+ (
+ $oMyTrigger,
+ 'test',
+ "SELECT bizPerson WHERE naime = 'Dali'",
+ "SELECT bizServer",
+ 'romain.quetiez@hp.com'
+ );
+
+ // Error: no recipient
+ //
+ $this->CreateEmailSpec
+ (
+ $oMyTrigger,
+ 'test',
+ "",
+ "",
+ 'romain.quetiez@hp.com'
+ );
+
+ // Test
+ //
+ $this->CreateEmailSpec
+ (
+ $oMyTrigger,
+ 'test',
+ "SELECT bizPerson WHERE name LIKE 'testemail%'",
+ "SELECT bizPerson",
+ 'romain.quetiez@hp.com'
+ );
+
+ // Test failing because of a wrong test recipient address
+ //
+ $this->CreateEmailSpec
+ (
+ $oMyTrigger,
+ 'test',
+ "SELECT bizPerson WHERE name LIKE 'testemail%'",
+ "",
+ 'toto@walibi.bg'
+ );
+
+ // Normal behavior
+ //
+ $this->CreateEmailSpec
+ (
+ $oMyTrigger,
+ 'enabled',
+ "SELECT bizPerson WHERE name LIKE 'testemail%'",
+ "",
+ 'romain.quetiez@hp.com'
+ );
+
+ // Does nothing, because it is disabled
+ //
+ $this->CreateEmailSpec
+ (
+ $oMyTrigger,
+ 'disabled',
+ "SELECT bizPerson WHERE name = 'testemail%'",
+ "",
+ 'romain.quetiez@hp.com'
+ );
+
+ $oMyTrigger->DoActivate($oMyServer->ToArgs('this'));
+
+ return true;
+ }
+}
+?>