protected function DoExecScenario($aSingleScenario) { echo "<div style=\"padding: 10;\">\n"; echo "<h3 style=\"background-color: #ddddff; padding: 10;\">{$aSingleScenario['desc']}</h3>\n"; $sClass = $aSingleScenario['target_class']; $aTargetData = $aSingleScenario['target_data']; $aSourceData = $aSingleScenario['source_data']; $aTargetAttributes = array_shift($aTargetData); $aSourceAttributes = array_shift($aSourceData); if (count($aSourceData) + 1 != count($aTargetData)) { throw new Exception("Target data must contain exactly " . (count($aSourceData) + 1) . " items, found " . count($aTargetData)); } // Create the data source // $oDataSource = new SynchroDataSource(); $oDataSource->Set('name', 'Test data sync ' . time()); $oDataSource->Set('description', 'unit test - created automatically'); $oDataSource->Set('status', 'production'); $oDataSource->Set('user_id', 0); $oDataSource->Set('scope_class', $sClass); $oDataSource->Set('scope_restriction', ''); $oDataSource->Set('full_load_periodicity', $aSingleScenario['full_load_periodicity']); $oDataSource->Set('reconciliation_policy', $aSingleScenario['reconciliation_policy']); $oDataSource->Set('action_on_zero', $aSingleScenario['action_on_zero']); $oDataSource->Set('action_on_one', $aSingleScenario['action_on_one']); $oDataSource->Set('action_on_multiple', $aSingleScenario['action_on_multiple']); $oDataSource->Set('delete_policy', $aSingleScenario['delete_policy']); $oDataSource->Set('delete_policy_update', $aSingleScenario['delete_policy_update']); $oDataSource->Set('delete_policy_retention', $aSingleScenario['delete_policy_retention']); $iDataSourceId = $this->ObjectToDB($oDataSource, true); $oAttributeSet = $oDataSource->Get('attribute_list'); while ($oAttribute = $oAttributeSet->Fetch()) { if (array_key_exists($oAttribute->Get('attcode'), $aSingleScenario['attributes'])) { $aAttribInfo = $aSingleScenario['attributes'][$oAttribute->Get('attcode')]; if (array_key_exists('reconciliation_attcode', $aAttribInfo)) { $oAttribute->Set('reconciliation_attcode', $aAttribInfo['reconciliation_attcode']); } $oAttribute->Set('update', $aAttribInfo['do_update']); $oAttribute->Set('reconcile', $aAttribInfo['do_reconcile']); } else { $oAttribute->Set('update', false); $oAttribute->Set('reconcile', false); } $this->UpdateObjectInDB($oAttribute); } // Prepare list of prefixes -> make sure objects are unique with regard to the reconciliation scheme $aPrefixes = array(); // attcode => prefix foreach ($aSourceAttributes as $iDummy => $sAttCode) { $aPrefixes[$sAttCode] = ''; // init with something } foreach ($aSingleScenario['attributes'] as $sAttCode => $aAttribInfo) { if (isset($aAttribInfo['automatic_prefix']) && $aAttribInfo['automatic_prefix']) { $aPrefixes[$sAttCode] = 'TEST_' . $iDataSourceId . '_'; } } // List existing objects (to be ignored in the analysis // $oAllObjects = new DBObjectSet(new DBObjectSearch($sClass)); $aExisting = $oAllObjects->ToArray(true); $sExistingIds = implode(', ', array_keys($aExisting)); // Create the initial object list // $aInitialTarget = $aTargetData[0]; foreach ($aInitialTarget as $aObjFields) { $oNewTarget = MetaModel::NewObject($sClass); foreach ($aTargetAttributes as $iAtt => $sAttCode) { $oNewTarget->Set($sAttCode, $aPrefixes[$sAttCode] . $aObjFields[$iAtt]); } $this->ObjectToDB($oNewTarget); } foreach ($aTargetData as $iRow => $aExpectedObjects) { sleep(2); // Check the status (while ignoring existing objects) // if (empty($sExistingIds)) { $oObjects = new DBObjectSet(DBObjectSearch::FromOQL("SELECT {$sClass}")); } else { $oObjects = new DBObjectSet(DBObjectSearch::FromOQL("SELECT {$sClass} WHERE id NOT IN({$sExistingIds})")); } $aFound = $oObjects->ToArray(); $aErrors_Unexpected = array(); foreach ($aFound as $iObj => $oObj) { // Is this object in the expected objects list $bFoundMatch = false; foreach ($aExpectedObjects as $iExp => $aValues) { $bDoesMatch = true; foreach ($aTargetAttributes as $iCol => $sAttCode) { if ($oObj->Get($sAttCode) != $aPrefixes[$sAttCode] . $aValues[$iCol]) { $bDoesMatch = false; break; } } if ($bDoesMatch) { $bFoundMatch = true; unset($aExpectedObjects[$iExp]); break; } } if (!$bFoundMatch) { $aErrors_Unexpected[] = $oObj->GetKey(); } } // Display the current status // echo "<p>Status at step {$iRow}</p>\n"; $aCurrentDataSet = array(); foreach ($aFound as $iObj => $oObj) { $aObjDesc = array('Status' => in_array($iObj, $aErrors_Unexpected) ? 'unexpected' : 'ok', 'Object' => $oObj->GetHyperLink()); foreach ($aTargetAttributes as $iCol => $sAttCode) { $aObjDesc[$sAttCode] = $oObj->Get($sAttCode); } $aCurrentDataSet[] = $aObjDesc; } if (count($aExpectedObjects) > 0) { foreach ($aExpectedObjects as $iExp => $aValues) { $aObjDesc = array('Status' => 'missing', 'Object' => 'n/a'); foreach ($aTargetAttributes as $iCol => $sAttCode) { $aObjDesc[$sAttCode] = $aPrefixes[$sAttCode] . $aValues[$iCol]; } $aCurrentDataSet[] = $aObjDesc; } } echo MyHelpers::make_table_from_assoc_array($aCurrentDataSet); if (count($aErrors_Unexpected) > 0 || count($aExpectedObjects) > 0) { throw new UnitTestException("The current status in iTop does not match the expectations"); } // If not on the final row, run a data exchange sequence // if (array_key_exists($iRow, $aSourceData)) { $aToBeLoaded = $aSourceData[$iRow]; $sCsvData = implode(';', $aSourceAttributes) . "\n"; foreach ($aToBeLoaded as $aDataRow) { $aFinalData = array(); foreach ($aDataRow as $iCol => $value) { if (is_null($value)) { $aFinalData[] = '<NULL>'; } else { $sAttCode = $aSourceAttributes[$iCol]; $aFinalData[] = $aPrefixes[$sAttCode] . $value; } } $sCsvData .= implode(';', $aFinalData) . "\n"; } $aPostData = array('csvdata' => $sCsvData); $aImportArgs = array('data_source_id' => $iDataSourceId, 'separator' => ';', 'simulate' => 0, 'output' => 'details'); $aGetParams = array(); $aGetParamReport = array(); foreach ($aImportArgs as $sArg => $sValue) { $aGetParams[] = $sArg . '=' . urlencode($sValue); $aGetParamReport[] = $sArg . '=' . $sValue; } $sGetParams = implode('&', $aGetParams); $sLogin = isset($aSingleScenario['login']) ? $aSingleScenario['login'] : '******'; $sPassword = isset($aSingleScenario['password']) ? $aSingleScenario['password'] : '******'; $sRes = self::DoPostRequestAuth('../synchro/synchro_import.php?' . $sGetParams, $aPostData, $sLogin, $sPassword); // Report the load results // 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; } $sCsvDataViewable = htmlentities($sCsvDataViewable, ENT_QUOTES, 'UTF-8'); echo "<div style=\"\">\n"; echo " <pre class=\"vardump\">{$sCsvDataViewable}</pre>\n"; echo "</div>\n"; echo "<pre class=\"vardump\" style=\"clear: both; padding: 15; background-color: black; color: green;\">{$sRes}</pre>\n"; if (stripos($sRes, 'exception') !== false) { throw new UnitTestException('Encountered an Exception during the last import/synchro'); } } } return; echo "</div>\n"; }