3 * Tine 2.0 - http://www.tine20.org
7 * @license http://www.gnu.org/licenses/agpl.html AGPL Version 3
8 * @copyright Copyright (c) 2008-2012 Metaways Infosystems GmbH (http://www.metaways.de)
9 * @author Cornelius Weiss <c.weiss@metaways.de>
15 require_once dirname(dirname(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
18 * Test class for Tinebase_Group
20 class Tinebase_Timemachine_ModificationLogTest extends PHPUnit_Framework_TestCase
23 * @var Tinebase_Timemachine_ModificationLog
25 protected $_modLogClass;
28 * @var Tinebase_Record_RecordSet
30 protected $_logEntries;
33 * @var Tinebase_Record_RecordSet
34 * Persistant Records we need to cleanup at tearDown()
36 protected $_persistantLogEntries;
39 * @var array holds recordId's we create log entries for
41 protected $_recordIds = array();
44 * Runs the test methods of this class.
49 public static function main()
51 $suite = new PHPUnit_Framework_TestSuite('Tinebase_Timemachine_ModificationLogTest');
52 PHPUnit_TextUI_TestRunner::run($suite);
56 * Lets update a record tree times
60 protected function setUp()
62 Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
64 $now = new Tinebase_DateTime();
65 $this->_modLogClass = Tinebase_Timemachine_ModificationLog::getInstance();
66 $this->_persistantLogEntries = new Tinebase_Record_RecordSet('Tinebase_Model_ModificationLog');
67 $this->_recordIds = array('5dea69be9c72ea3d263613277c3b02d529fbd8bc');
69 $tinebaseApp = Tinebase_Application::getInstance()->getApplicationByName('Tinebase');
71 $this->_logEntries = new Tinebase_Record_RecordSet('Tinebase_Model_ModificationLog', array(
73 'application_id' => $tinebaseApp,
74 'record_id' => $this->_recordIds[0],
75 'record_type' => 'TestType',
76 'record_backend' => 'TestBackend',
77 'modification_time' => $this->_cloner($now)->addDay(-2),
78 'modification_account' => 7,
79 'modified_attribute' => 'FirstTestAttribute',
80 'old_value' => 'Hamburg',
81 'new_value' => 'Bremen'
84 'application_id' => $tinebaseApp,
85 'record_id' => $this->_recordIds[0],
86 'record_type' => 'TestType',
87 'record_backend' => 'TestBackend',
88 'modification_time' => $this->_cloner($now)->addDay(-1),
89 'modification_account' => 7,
90 'modified_attribute' => 'FirstTestAttribute',
91 'old_value' => 'Bremen',
92 'new_value' => 'Frankfurt'
95 'application_id' => $tinebaseApp,
96 'record_id' => $this->_recordIds[0],
97 'record_type' => 'TestType',
98 'record_backend' => 'TestBackend',
99 'modification_time' => $this->_cloner($now),
100 'modification_account' => 7,
101 'modified_attribute' => 'FirstTestAttribute',
102 'old_value' => 'Frankfurt',
103 'new_value' => 'Stuttgart'
106 'application_id' => $tinebaseApp,
107 'record_id' => $this->_recordIds[0],
108 'record_type' => 'TestType',
109 'record_backend' => 'TestBackend',
110 'modification_time' => $this->_cloner($now)->addDay(-2),
111 'modification_account' => 7,
112 'modified_attribute' => 'SecondTestAttribute',
113 'old_value' => 'Deutschland',
114 'new_value' => 'Östereich'
117 'application_id' => $tinebaseApp,
118 'record_id' => $this->_recordIds[0],
119 'record_type' => 'TestType',
120 'record_backend' => 'TestBackend',
121 'modification_time' => $this->_cloner($now)->addDay(-1)->addSecond(1),
122 'modification_account' => 7,
123 'modified_attribute' => 'SecondTestAttribute',
124 'old_value' => 'Östereich',
125 'new_value' => 'Schweitz'
128 'application_id' => $tinebaseApp->getId(),
129 'record_id' => $this->_recordIds[0],
130 'record_type' => 'TestType',
131 'record_backend' => 'TestBackend',
132 'modification_time' => $this->_cloner($now),
133 'modification_account' => 7,
134 'modified_attribute' => 'SecondTestAttribute',
135 'old_value' => 'Schweitz',
136 'new_value' => 'Italien'
140 foreach ($this->_logEntries as $logEntry) {
141 $id = $this->_modLogClass->setModification($logEntry);
142 $this->_persistantLogEntries->addRecord($this->_modLogClass->getModification($id));
150 protected function tearDown()
152 Tinebase_TransactionManager::getInstance()->rollBack();
156 * tests that the returned mod logs equal the initial ones we defined
157 * in this test setup.
158 * If this works, also the setting of logs works!
161 public function testGetModification()
163 foreach ($this->_logEntries as $num => $logEntry) {
164 $rawLogEntry = $logEntry->toArray();
165 $rawPersistantLogEntry = $this->_persistantLogEntries[$num]->toArray();
167 foreach ($rawLogEntry as $field => $value) {
168 $persistantValue = $rawPersistantLogEntry[$field];
169 if ($value != $persistantValue) {
170 $this->fail("Failed asserting that contents of saved LogEntry #$num in field $field equals initial datas. \n" .
171 "Expected '$value', got '$persistantValue'");
175 $this->assertTrue(true);
179 * tests computation of a records differences described by a set of modification logs
181 public function testComputeDiff()
183 $diffs = $this->_modLogClass->computeDiff($this->_persistantLogEntries)->toArray();
184 $this->assertEquals(2, count($diffs)); // we changed two attributes
185 foreach ($diffs as $diff) {
186 switch ($diff['modified_attribute']) {
187 case 'FirstTestAttribute':
188 $this->assertEquals('Hamburg', $diff['old_value']);
189 $this->assertEquals('Stuttgart', $diff['new_value']);
191 case 'SecondTestAttribute':
192 $this->assertEquals('Deutschland', $diff['old_value']);
193 $this->assertEquals('Italien', $diff['new_value']);
199 * get modifications test
201 public function testGetModifications()
204 'record_id' => '5dea69be9c72ea3d263613277c3b02d529fbd8bc',
205 'type' => 'TestType',
206 'backend' => 'TestBackend'
208 $firstModificationTime = $this->_persistantLogEntries[0]->modification_time;
209 $lastModificationTime = $this->_persistantLogEntries[count($this->_persistantLogEntries)-1]->modification_time;
211 $toTest[] = $testBase + array(
212 'from_add' => 'addDay,-3',
213 'until_add' => 'addDay,1',
216 $toTest[] = $testBase + array(
219 $toTest[] = $testBase + array(
220 'account' => Tinebase_Record_Abstract::generateUID(),
224 foreach ($toTest as $params) {
225 $from = clone $firstModificationTime;
226 $until = clone $lastModificationTime;
228 if (isset($params['from_add'])) {
229 list($fn,$p) = explode(',', $params['from_add']);
232 if (isset($params['until_add'])) {
233 list($fn,$p) = explode(',', $params['until_add']);
237 $account = isset($params['account']) ? $params['account'] : NULL;
238 $diffs = $this->_modLogClass->getModifications('Tinebase', $params['record_id'], $params['type'], $params['backend'], $from, $until, $account);
240 foreach ($diffs as $diff) {
241 if ($diff->record_id == $params['record_id']) {
245 $this->assertEquals($params['nums'], $count);
252 * @see 0006252: allow to undo history items (modlog)
253 * @see 0000554: modlog: records can't be updated in less than 1 second intervals
255 public function testUndo()
258 $contact = Addressbook_Controller_Contact::getInstance()->create(new Addressbook_Model_Contact(array(
259 'n_family' => 'tester',
260 'tel_cell' => '+491234',
262 // change something using the record controller
263 $contact->tel_cell = NULL;
264 $contact = Addressbook_Controller_Contact::getInstance()->update($contact);
266 // fetch modlog and test seq
267 $modlog = $this->_modLogClass->getModifications('Addressbook', $contact->getId(), NULL, 'Sql',
268 Tinebase_DateTime::now()->subSecond(5), Tinebase_DateTime::now())->getFirstRecord();
269 $this->assertTrue($modlog !== NULL);
270 $this->assertEquals(2, $modlog->seq);
271 $this->assertContains('1234', $modlog->old_value);
273 $filter = new Tinebase_Model_ModificationLogFilter(array(
274 array('field' => 'record_type', 'operator' => 'equals', 'value' => 'Addressbook_Model_Contact'),
275 array('field' => 'record_id', 'operator' => 'equals', 'value' => $contact->getId()),
276 array('field' => 'modification_time', 'operator' => 'within', 'value' => 'weekThis'),
278 $result = $this->_modLogClass->undo($filter, true);
279 $this->assertEquals(2, $result['totalcount'], 'did not get 2 undone modlog: ' . print_r($result, TRUE));
280 $this->assertContains('1234', $result['undoneModlogs']->getFirstRecord()->old_value);
282 // check record after undo
283 $contact = Addressbook_Controller_Contact::getInstance()->get($contact);
284 $this->assertEquals('+491234', $contact->tel_cell);
288 * purges mod log entries of given recordIds
290 * @param mixed [string|array|Tinebase_Record_RecordSet] $_recordIds
292 * @todo should be removed when other tests do not need this anymore
294 public static function purgeLogs($_recordIds)
296 $table = new Tinebase_Db_Table(array('name' => SQL_TABLE_PREFIX . 'timemachine_modlog'));
298 foreach ((array) $_recordIds as $recordId) {
299 $table->delete($table->getAdapter()->quoteInto('record_id = ?', $recordId));
304 * Workaround as the php clone operator does not return cloned
305 * objects right hand sided
307 * @param object $_object
310 protected function _cloner($_object)
312 return clone $_object;
318 * @see 0000996: add changes in relations/linked objects to modlog/history
320 public function testDateTimeModlog()
322 $task = Tasks_Controller_Task::getInstance()->create(new Tasks_Model_Task(array(
323 'summary' => 'test task',
326 $task->due = Tinebase_DateTime::now();
327 $updatedTask = Tasks_Controller_Task::getInstance()->update($task);
330 $modlog = $this->_modLogClass->getModificationsBySeq(
331 Tinebase_Application::getInstance()->getApplicationByName('Tasks')->getId(),
334 $this->assertEquals(1, count($modlog));
335 $this->assertEquals((string) $task->due, (string) $modlog->getFirstRecord()->new_value, 'new value mismatch: ' . print_r($modlog->toArray(), TRUE));