fix modlog test undo
[tine20] / tests / tine20 / TestCase.php
1 <?php
2 /**
3  * Tine 2.0 - http://www.tine20.org
4  * 
5  * @package     Tests
6  * @license     http://www.gnu.org/licenses/agpl.html AGPL Version 3
7  * @copyright   Copyright (c) 2013-2015 Metaways Infosystems GmbH (http://www.metaways.de)
8  * @author      Philipp Schüle <p.schuele@metaways.de>
9  */
10
11 /**
12  * Test helper
13  */
14 require_once __DIR__ . DIRECTORY_SEPARATOR . 'TestHelper.php';
15
16 /**
17  * Abstract test class
18  * 
19  * @package     Tests
20  *
21  * TODO separation of concerns: split into multiple classes/traits with cleanup / fixture / ... functionality
22  */
23 abstract class TestCase extends PHPUnit_Framework_TestCase
24 {
25     /**
26      * transaction id if test is wrapped in an transaction
27      * 
28      * @var string
29      */
30     protected $_transactionId = null;
31     
32     /**
33      * usernames to be deleted (in sync backend)
34      * 
35      * @var array
36      */
37     protected $_usernamesToDelete = array();
38     
39     /**
40      * groups (ID) to be deleted (in sync backend)
41      * 
42      * @var array
43      */
44     protected $_groupIdsToDelete = array();
45     
46     /**
47      * remove group members, too when deleting groups
48      * 
49      * @var boolean
50      */
51     protected $_removeGroupMembers = true;
52     
53     /**
54      * invalidate roles cache
55      * 
56      * @var boolean
57      */
58     protected $_invalidateRolesCache = false;
59     
60     /**
61      * test personas
62      * 
63      * @var array
64      */
65     protected $_personas = array();
66     
67     /**
68      * unit in test
69      *
70      * @var Object
71      */
72     protected $_uit = null;
73     
74     /**
75      * the test user
76      *
77      * @var Tinebase_Model_FullUser
78      */
79     protected $_originalTestUser;
80     
81     /**
82      * the mailer
83      * 
84      * @var Zend_Mail_Transport_Abstract
85      */
86     protected static $_mailer = null;
87
88     /**
89      * db lock ids to be released
90      *
91      * @var array
92      */
93     protected $_releaseDBLockIds = array();
94
95     /**
96      * customfields that should be deleted later
97      *
98      * @var array
99      */
100     protected $_customfieldIdsToDelete = array();
101
102     /**
103      * set up tests
104      */
105     protected function setUp()
106     {
107         foreach ($this->_customfieldIdsToDelete as $cfd) {
108             Tinebase_CustomField::getInstance()->deleteCustomField($cfd);
109         }
110
111         $this->_transactionId = Tinebase_TransactionManager::getInstance()->startTransaction(Tinebase_Core::getDb());
112         
113         Addressbook_Controller_Contact::getInstance()->setGeoDataForContacts(false);
114
115         if (Zend_Registry::isRegistered('personas')) {
116             $this->_personas = Zend_Registry::get('personas');
117         }
118         
119         $this->_originalTestUser = Tinebase_Core::getUser();
120     }
121     
122     /**
123      * tear down tests
124      */
125     protected function tearDown()
126     {
127         if (in_array(Tinebase_User::getConfiguredBackend(), array(Tinebase_User::LDAP, Tinebase_User::ACTIVEDIRECTORY))) {
128             $this->_deleteUsers();
129             $this->_deleteGroups();
130         }
131         if ($this->_transactionId) {
132             if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG))
133                 Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ . ' Rolling back test transaction');
134             Tinebase_TransactionManager::getInstance()->rollBack();
135         }
136         
137         Addressbook_Controller_Contact::getInstance()->setGeoDataForContacts(true);
138
139         if ($this->_originalTestUser instanceof Tinebase_Model_User) {
140             Tinebase_Core::set(Tinebase_Core::USER, $this->_originalTestUser);
141         }
142         
143         if ($this->_invalidateRolesCache) {
144             Tinebase_Acl_Roles::getInstance()->resetClassCache();
145         }
146
147         Tinebase_Cache_PerRequest::getInstance()->reset();
148
149         $this->_releaseDBLocks();
150     }
151
152     /**
153      * release db locks
154      */
155     protected function _releaseDBLocks()
156     {
157         foreach ($this->_releaseDBLockIds as $lockId) {
158             Tinebase_Lock::releaseDBSessionLock($lockId);
159         }
160
161         $this->_releaseDBLockIds = array();
162     }
163
164     /**
165      * tear down after test class
166      */
167     public static function tearDownAfterClass()
168     {
169         Tinebase_Core::getDbProfiling();
170     }
171     
172     /**
173      * test needs transaction
174      */
175     protected function _testNeedsTransaction()
176     {
177         if ($this->_transactionId) {
178             Tinebase_TransactionManager::getInstance()->commitTransaction($this->_transactionId);
179             $this->_transactionId = null;
180         }
181     }
182     
183     /**
184      * get tag
185      *
186      * @param string $tagType
187      * @param string $tagName
188      * @param array $contexts
189      * @return Tinebase_Model_Tag
190      */
191     protected function _getTag($tagType = Tinebase_Model_Tag::TYPE_SHARED, $tagName = NULL, $contexts = NULL)
192     {
193         if ($tagName) {
194             try {
195                 $tag = Tinebase_Tags::getInstance()->getTagByName($tagName);
196                 return $tag;
197             } catch (Tinebase_Exception_NotFound $tenf) {
198             }
199         } else {
200             $tagName = Tinebase_Record_Abstract::generateUID();
201         }
202     
203         $targ = array(
204             'type'          => $tagType,
205             'name'          => $tagName,
206             'description'   => 'testTagDescription',
207             'color'         => '#009B31',
208         );
209     
210         if ($contexts) {
211             $targ['contexts'] = $contexts;
212         }
213     
214         return new Tinebase_Model_Tag($targ);
215     }
216     
217     /**
218      * delete groups and their members
219      * 
220      * - also deletes groups and users in sync backends
221      */
222     protected function _deleteGroups()
223     {
224         if (! is_array($this->_groupIdsToDelete)) {
225             return;
226         }
227
228         foreach ($this->_groupIdsToDelete as $groupId) {
229             if ($this->_removeGroupMembers) {
230                 foreach (Tinebase_Group::getInstance()->getGroupMembers($groupId) as $userId) {
231                     try {
232                         Tinebase_User::getInstance()->deleteUser($userId);
233                     } catch (Exception $e) {
234                         if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
235                             . ' error while deleting user: ' . $e->getMessage());
236                     }
237                 }
238             }
239             try {
240                 Tinebase_Group::getInstance()->deleteGroups($groupId);
241             } catch (Exception $e) {
242                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
243                     . ' error while deleting group: ' . $e->getMessage());
244             }
245         }
246     }
247     
248     /**
249      * delete users
250      */
251     protected function _deleteUsers()
252     {
253         foreach ($this->_usernamesToDelete as $username) {
254             try {
255                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
256                     . ' Trying to delete user: ' . $username);
257
258                 Tinebase_User::getInstance()->deleteUser(Tinebase_User::getInstance()->getUserByLoginName($username));
259             } catch (Exception $e) {
260                 if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__
261                     . ' Error while deleting user: ' . $e->getMessage());
262             }
263         }
264     }
265
266     /**
267      * removes records and their relations
268      *
269      * @param Tinebase_Record_RecordSet $records
270      */
271     protected function _deleteRecordRelations($records, $modelsToDelete = array(), $typesToDelete = array())
272     {
273         $controller = Tinebase_Core::getApplicationInstance($records->getRecordClassName());
274
275         if (! method_exists($controller, 'deleteLinkedRelations')) {
276             return;
277         }
278
279         foreach ($records as $record) {
280             $controller->deleteLinkedRelations($record, $modelsToDelete, $typesToDelete);
281         }
282     }
283
284     /**
285      * get personal container
286      * 
287      * @param string $applicationName
288      * @param Tinebase_Model_User $user
289      * @return Tinebase_Model_Container
290      */
291     protected function _getPersonalContainer($applicationName, $user = null)
292     {
293         if ($user === null) {
294             $user = Tinebase_Core::getUser();
295         }
296         
297         $personalContainer = Tinebase_Container::getInstance()->getPersonalContainer(
298             $user,
299             $applicationName, 
300             $user,
301             Tinebase_Model_Grants::GRANT_EDIT
302         )->getFirstRecord();
303
304         if (! $personalContainer) {
305             throw new Tinebase_Exception_UnexpectedValue('no personal container found!');
306         }
307
308         return $personalContainer;
309     }
310     
311     /**
312      * get test container
313      * 
314      * @param string $applicationName
315      * @param string $model
316      */
317     protected function _getTestContainer($applicationName, $model = null)
318     {
319         return Tinebase_Container::getInstance()->addContainer(new Tinebase_Model_Container(array(
320             'name'           => 'PHPUnit ' . $model .' container',
321             'type'           => Tinebase_Model_Container::TYPE_PERSONAL,
322             'owner_id'       => Tinebase_Core::getUser(),
323             'backend'        => 'Sql',
324             'application_id' => Tinebase_Application::getInstance()->getApplicationByName($applicationName)->getId(),
325             'model'          => $model,
326         ), true));
327     }
328     
329     /**
330      * get test mail domain
331      * 
332      * @return string
333      */
334     protected function _getMailDomain()
335     {
336         return TestServer::getPrimaryMailDomain();
337     }
338     
339     /**
340      * get test user email address
341      * 
342      * @return test user email address
343      */
344     protected function _getEmailAddress()
345     {
346         $testConfig = TestServer::getInstance()->getConfig();
347         return ($testConfig->email) ? $testConfig->email : Tinebase_Core::getUser()->accountEmailAddress;
348     }
349     
350     /**
351      * lazy init of uit
352      * 
353      * @return Object
354      * 
355      * @todo fix ide object class detection for completions
356      */
357     protected function _getUit()
358     {
359         if ($this->_uit === null) {
360             $uitClass = preg_replace('/Tests{0,1}$/', '', get_class($this));
361             if (@method_exists($uitClass, 'getInstance')) {
362                 $this->_uit = call_user_func($uitClass . '::getInstance');
363             } else if (@class_exists($uitClass)) {
364                 $this->_uit = new $uitClass();
365             } else {
366                 throw new Exception('could not find class ' . $uitClass);
367             }
368         }
369         
370         return $this->_uit;
371     }
372     
373     /**
374      * get messages
375      * 
376      * @return array
377      */
378     public static function getMessages()
379     {
380         // make sure messages are sent if queue is activated
381         if (isset(Tinebase_Core::getConfig()->actionqueue)) {
382             Tinebase_ActionQueue::getInstance()->processQueue(100);
383         }
384         
385         return self::getMailer()->getMessages();
386     }
387     
388     /**
389      * get mailer
390      * 
391      * @return Zend_Mail_Transport_Abstract
392      */
393     public static function getMailer()
394     {
395         if (! self::$_mailer) {
396             self::$_mailer = Tinebase_Smtp::getDefaultTransport();
397         }
398         
399         return self::$_mailer;
400     }
401     
402     /**
403      * flush mailer (send all remaining mails first)
404      */
405     public static function flushMailer()
406     {
407         // make sure all messages are sent if queue is activated
408         if (isset(Tinebase_Core::getConfig()->actionqueue)) {
409             Tinebase_ActionQueue::getInstance()->processQueue(10000);
410         }
411         
412         self::getMailer()->flush();
413     }
414     
415     /**
416      * returns the content.xml of an ods document
417      * 
418      * @param string $filename
419      * @return SimpleXMLElement
420      */
421     protected function _getContentXML($filename)
422     {
423         $zipHandler = zip_open($filename);
424         
425         do {
426             $entry = zip_read($zipHandler);
427         } while ($entry && zip_entry_name($entry) != "content.xml");
428         
429         // open entry
430         zip_entry_open($zipHandler, $entry, "r");
431         
432         // read entry
433         $entryContent = zip_entry_read($entry, zip_entry_filesize($entry));
434         
435         $xml = simplexml_load_string($entryContent);
436         zip_close($zipHandler);
437         
438         return $xml;
439     }
440     
441     /**
442      * get test temp file
443      * 
444      * @return Tinebase_TempFile
445      */
446     protected function _getTempFile()
447     {
448         $tempFileBackend = new Tinebase_TempFile();
449         $tempFile = $tempFileBackend->createTempFile(dirname(__FILE__) . '/Filemanager/files/test.txt');
450         return $tempFile;
451     }
452     
453     /**
454      * remove right in all users roles
455      * 
456      * @param string $applicationName
457      * @param string $right
458      * @param boolean $removeAdminRight
459      * @return array original role rights by role id
460      */
461     protected function _removeRoleRight($applicationName, $rightToRemove, $removeAdminRight = true)
462     {
463         $app = Tinebase_Application::getInstance()->getApplicationByName($applicationName);
464         $rolesOfUser = Tinebase_Acl_Roles::getInstance()->getRoleMemberships(Tinebase_Core::getUser()->getId());
465         $this->_invalidateRolesCache = true;
466
467         $roleRights = array();
468         foreach ($rolesOfUser as $roleId) {
469             $roleRights[$roleId] = $rights = Tinebase_Acl_Roles::getInstance()->getRoleRights($roleId);
470             foreach ($rights as $idx => $right) {
471                 if ($right['application_id'] === $app->getId() && ($right['right'] === $rightToRemove || $right['right'] === Tinebase_Acl_Rights_Abstract::ADMIN)) {
472                     if (Tinebase_Core::isLogLevel(Zend_Log::DEBUG)) Tinebase_Core::getLogger()->debug(__METHOD__ . '::' . __LINE__ 
473                         . ' Removing right ' . $right['right'] . ' from app ' . $applicationName . ' in role (id) ' . $roleId);
474                     unset($rights[$idx]);
475                 }
476             }
477             Tinebase_Acl_Roles::getInstance()->setRoleRights($roleId, $rights);
478         }
479         
480         return $roleRights;
481     }
482     
483     /**
484      * set grants for a persona and the current user
485      * 
486      * @param integer $containerId
487      * @param string $persona
488      * @param string $adminGrant
489      */
490     protected function _setPersonaGrantsForTestContainer($containerId, $persona, $personaAdminGrant = false, $userAdminGrant = true)
491     {
492         $grants = new Tinebase_Record_RecordSet('Tinebase_Model_Grants', array(array(
493             'account_id'    => $this->_personas[$persona]->getId(),
494             'account_type'  => 'user',
495             Tinebase_Model_Grants::GRANT_READ     => true,
496             Tinebase_Model_Grants::GRANT_ADD      => true,
497             Tinebase_Model_Grants::GRANT_EDIT     => true,
498             Tinebase_Model_Grants::GRANT_DELETE   => true,
499             Tinebase_Model_Grants::GRANT_ADMIN    => $personaAdminGrant,
500         ), array(
501             'account_id'    => Tinebase_Core::getUser()->getId(),
502             'account_type'  => 'user',
503             Tinebase_Model_Grants::GRANT_READ     => true,
504             Tinebase_Model_Grants::GRANT_ADD      => true,
505             Tinebase_Model_Grants::GRANT_EDIT     => true,
506             Tinebase_Model_Grants::GRANT_DELETE   => true,
507             Tinebase_Model_Grants::GRANT_ADMIN    => $userAdminGrant,
508         )));
509         
510         Tinebase_Container::getInstance()->setGrants($containerId, $grants, TRUE);
511     }
512
513     /**
514      * set current user
515      *
516      * @param $user
517      * @throws Tinebase_Exception_InvalidArgument
518      */
519     protected function _setUser($user)
520     {
521         Tinebase_Core::set(Tinebase_Core::USER, $user);
522     }
523
524     /**
525      * call handle cli function with params
526      *
527      * @param array $_params
528      */
529     protected function _cliHelper($command, $_params)
530     {
531         $opts = new Zend_Console_Getopt(array($command => $command));
532         $opts->setArguments($_params);
533         ob_start();
534         $this->_cli->handle($opts, false);
535         $out = ob_get_clean();
536         return $out;
537     }
538
539     /**
540      * test record json api
541      *
542      * @param $modelName
543      */
544     protected function _testSimpleRecordApi($modelName)
545     {
546         $uit = $this->_getUit();
547         if (!$uit instanceof Tinebase_Frontend_Json_Abstract) {
548             throw new Exception('only allowed for json frontend tests suites');
549         }
550
551         $newRecord = array(
552             'name' => 'my test ' . $modelName,
553             'description' => 'my test description'
554         );
555         $savedRecord = call_user_func(array($uit, 'save' . $modelName), $newRecord);
556
557         $this->assertEquals('my test ' . $modelName, $savedRecord['name'], print_r($savedRecord, true));
558         $savedRecord['description'] = 'my updated description';
559
560         $updatedRecord = call_user_func(array($uit, 'save' . $modelName), $savedRecord);
561         $this->assertEquals('my updated description', $updatedRecord['description']);
562
563         $filter = array(array('field' => 'id', 'operator' => 'equals', 'value' => $updatedRecord['id']));
564         $result = call_user_func(array($uit, 'search' . $modelName . 's'), $filter, array());
565         $this->assertEquals(1, $result['totalcount']);
566
567         call_user_func(array($uit, 'delete' . $modelName . 's'), array($updatedRecord['id']));
568         try {
569             call_user_func(array($uit, 'get' . $modelName), $updatedRecord['id']);
570             $this->fail('should delete Record');
571         } catch (Tinebase_Exception_NotFound $tenf) {
572             $this->assertTrue($tenf instanceof Tinebase_Exception_NotFound);
573         }
574     }
575
576     /**
577      * returns true if main db adapter is postgresql
578      *
579      * @return bool
580      */
581     protected function _dbIsPgsql()
582     {
583         $db = Tinebase_Core::getDb();
584         return ($db instanceof Zend_Db_Adapter_Pdo_Pgsql);
585     }
586
587     /**
588      * get custom field record
589      *
590      * @param string $name
591      * @param string $model
592      * @return Tinebase_Model_CustomField_Config
593      */
594     protected function _createCustomField($name = 'YomiName', $model = 'Addressbook_Model_Contact')
595     {
596         $application = substr($model, 0, strpos($model, '_'));
597         $cfData = new Tinebase_Model_CustomField_Config(array(
598             'application_id'    => Tinebase_Application::getInstance()->getApplicationByName($application)->getId(),
599             'name'              => $name,
600             'model'             => $model,
601             'definition'        => array(
602                 'label' => Tinebase_Record_Abstract::generateUID(),
603                 'type'  => 'string',
604                 'uiconfig' => array(
605                     'xtype'  => Tinebase_Record_Abstract::generateUID(),
606                     'length' => 10,
607                     'group'  => 'unittest',
608                     'order'  => 100,
609                 )
610             )
611         ));
612
613         try {
614             $result = Tinebase_CustomField::getInstance()->addCustomField($cfData);
615             $this->_customfieldIdsToDelete[] = $result->getId();
616         } catch (Zend_Db_Statement_Exception $zdse) {
617             // customfield already exists
618             $cfs = Tinebase_CustomField::getInstance()->getCustomFieldsForApplication($application);
619             $result = $cfs->filter('name', $name)->getFirstRecord();
620         }
621
622         return $result;
623     }
624 }