0013268: show user report (CLI)
[tine20] / tests / tine20 / Tinebase / Frontend / CliTest.php
1 <?php
2 /**
3  * Tine 2.0 - http://www.tine20.org
4  * 
5  * @package     Tinebase
6  * @license     http://www.gnu.org/licenses/agpl.html
7  * @copyright   Copyright (c) 2010-2016 Metaways Infosystems GmbH (http://www.metaways.de)
8  * @author      Philipp Schüle <p.schuele@metaways.de>
9  */
10
11 /**
12  * Test class for Tinebase_Frontend_Cli
13  */
14 class Tinebase_Frontend_CliTest extends TestCase
15 {
16     /**
17      * Backend
18      *
19      * @var Tinebase_Frontend_Cli
20      */
21     protected $_cli;
22     
23     /**
24      * test user
25      * 
26      * @var Tinebase_Model_FullUser
27      */
28     protected $_testUser;
29     
30     /**
31      * user plugins, need to be reset after triggerAsyncEvents run
32      * 
33      * @var array
34      */
35     protected $_userPlugins = array();
36     
37     /**
38      * Sets up the fixture.
39      * This method is called before a test is executed.
40      *
41      * @access protected
42      */
43     protected function setUp()
44     {
45         parent::setUp();
46         
47         $this->_cli = new Tinebase_Frontend_Cli();
48         $this->_testUser = Tinebase_Core::getUser();
49         $this->_userPlugins = Tinebase_User::getInstance()->getPlugins();
50     }
51
52     /**
53      * Tears down the fixture
54      * This method is called after a test is executed.
55      *
56      * @access protected
57      */
58     protected function tearDown()
59     {
60         // need to be reset after triggerAsyncEvents run (singleton ...)
61         Tinebase_User::getInstance()->unregisterAllPlugins();
62         Tinebase_User::getInstance()->registerPlugins($this->_userPlugins);
63         
64         $currentUser = Tinebase_Core::getUser();
65         if ($currentUser->accountLoginName !== $this->_testUser->accountLoginName) {
66             Tinebase_Core::set(Tinebase_Core::USER, $this->_testUser);
67         }
68         
69         parent::tearDown();
70     }
71     
72     /**
73      * test to clear accesslog table
74      */
75     public function testClearTableAccessLogWithDate()
76     {
77         $accessLogsBefore = Admin_Controller_AccessLog::getInstance()->search();
78         $opts = $this->_getOpts('access_log');
79         
80         ob_start();
81         $this->_cli->clearTable($opts);
82         // TODO check $out
83         $out = ob_get_clean();
84         
85         $accessLogsAfter = Admin_Controller_AccessLog::getInstance()->search();
86         $this->assertGreaterThan(count($accessLogsAfter), count($accessLogsBefore));
87         $this->assertEquals(0, count($accessLogsAfter));
88     }
89     
90     /**
91      * get options
92      * 
93      * @param string $_table
94      * @return Zend_Console_Getopt
95      */
96     protected function _getOpts($_table = NULL)
97     {
98         $opts = new Zend_Console_Getopt('abp:');
99         $tomorrow = Tinebase_DateTime::now()->addDay(1)->toString('Y-m-d');
100         $params = array('date=' . $tomorrow);
101         if ($_table !== NULL) {
102             $params[] = $_table;
103         }
104         $opts->setArguments($params);
105         
106         return $opts;
107     }
108
109     /**
110      * test purge deleted records
111      */
112     public function testPurgeDeletedRecordsAddressbook()
113     {
114         $opts = $this->_getOpts('addressbook');
115         $deletedRecord = $this->_addAndDeleteContact();
116         
117         ob_start();
118         $this->_cli->purgeDeletedRecords($opts);
119         $out = ob_get_clean();
120         
121         $this->assertContains('Removing all deleted entries before', $out);
122         $this->assertContains('Cleared table addressbook (deleted ', $out);
123
124         $contactBackend = Addressbook_Backend_Factory::factory(Addressbook_Backend_Factory::SQL);
125         $this->setExpectedException('Tinebase_Exception_NotFound');
126         $contactBackend->get($deletedRecord->getId(), TRUE);
127     }
128
129     /**
130      * test purge deleted records
131      *
132      * @see 0010249: Tinebase.purgeDeletedRecords fails
133      */
134     public function testPurgeDeletedRecordsAllTables()
135     {
136         $opts = $this->_getOpts();
137         $deletedContact = $this->_addAndDeleteContact();
138         $deletedLead = $this->_addAndDeleteLead();
139
140         // test deleted contact is still there
141         $contactBackend = Addressbook_Backend_Factory::factory(Addressbook_Backend_Factory::SQL);
142         $contacts = $contactBackend->getMultipleByProperty($deletedContact->getId(), 'id', TRUE);
143         $this->assertEquals(1, count($contacts));
144
145         // delete tag too
146         Tinebase_Tags::getInstance()->deleteTags($deletedContact->tags->getFirstRecord()->getId());
147         
148         ob_start();
149         $this->_cli->purgeDeletedRecords($opts);
150         $out = ob_get_clean();
151
152         $this->assertContains('Removing all deleted entries before', $out);
153         $this->assertContains('Cleared table addressbook (deleted ', $out);
154         $this->assertContains('Cleared table metacrm_lead (deleted ', $out);
155         $this->assertNotContains('Failed to purge', $out);
156
157         // test deleted contact is gone
158         $contacts = $contactBackend->getMultipleByProperty($deletedContact->getId(), 'id', TRUE);
159         $this->assertEquals(0, count($contacts));
160
161         $leadsBackend = new Crm_Backend_Lead();
162         $leads = $leadsBackend->getMultipleByProperty($deletedLead->getId(), 'id', TRUE);
163         $this->assertEquals(0, count($leads));
164     }
165     
166     /**
167      * creates and deletes a contact + returns the deleted record
168      * 
169      * @return Addressbook_Model_Contact
170      */
171     protected function _addAndDeleteContact()
172     {
173         $newContact = new Addressbook_Model_Contact(array(
174             'n_family'          => 'PHPUNIT',
175             'container_id'      => $this->_getPersonalContainer('Addressbook')->getId(),
176             'tel_cell_private'  => '+49TELCELLPRIVATE',
177             'tags'              => array(array('name' => 'temptag')),
178         ));
179         $newContact = Addressbook_Controller_Contact::getInstance()->create($newContact);
180         Addressbook_Controller_Contact::getInstance()->delete($newContact->getId());
181
182         return $newContact;
183     }
184
185     /**
186      * creates and deletes a lead + returns the deleted record
187      * 
188      * @return Crm_Model_Lead
189      */
190     protected function _addAndDeleteLead()
191     {
192         $newLead = new Crm_Model_Lead(array(
193             'lead_name'     => 'PHPUNIT Lead',
194             'container_id'  => Tinebase_Container::getInstance()->getDefaultContainer('Crm')->getId(),
195             'leadstate_id'  => 1,
196             'leadtype_id'   => 1,
197             'leadsource_id' => 1,
198             'start'         => Tinebase_DateTime::now(),
199         ));
200         $newLead = Crm_Controller_Lead::getInstance()->create($newLead);
201         Crm_Controller_Lead::getInstance()->delete($newLead->getId());
202         
203         return $newLead;
204     }
205     
206     /**
207      * test trigger events
208      */
209     public function testTriggerAsyncEvents()
210     {
211         $opts = new Zend_Console_Getopt('abp:');
212         $opts->setArguments(array());
213         $this->_usernamesToDelete[] = 'cronuser';
214         $this->_releaseDBLockIds[] = 'Tinebase_Frontend_Cli::triggerAsyncEvents';
215
216         ob_start();
217         $this->_cli->triggerAsyncEvents($opts);
218         $out = ob_get_clean();
219         
220         $userPlugins = Tinebase_User::getInstance()->getPlugins();
221         $this->assertEquals(0, count($userPlugins), 'got user plugins: ' . print_r($userPlugins, true));
222         
223         $cronuserId = Tinebase_Config::getInstance()->get(Tinebase_Config::CRONUSERID);
224         $this->assertTrue(! empty($cronuserId), 'got empty cronuser id');
225         $cronuser = Tinebase_User::getInstance()->getFullUserById($cronuserId);
226         $this->assertEquals('cronuser', $cronuser->accountLoginName);
227         $adminGroup = Tinebase_Group::getInstance()->getDefaultAdminGroup();
228         
229         $this->assertEquals($adminGroup->getId(), $cronuser->accountPrimaryGroup);
230         $this->assertContains('Tine 2.0 scheduler run', $out, $out);
231     }
232
233     /**
234      * testMonitoringCheckDB
235      * 
236      * NOTE deactivated this test as it might affect other tests
237      * 
238      * @todo fix this test / make cli method testable
239      */
240     public function _testMonitoringCheckDB()
241     {
242         ob_start();
243         $result = $this->_cli->monitoringCheckDB();
244         $out = ob_get_clean();
245         
246         $this->assertEquals("DB CONNECTION OK\n", $out);
247         $this->assertEquals(0, $result);
248     }
249
250     /**
251      * testMonitoringCheckConfig
252      */
253     public function testMonitoringCheckConfig()
254     {
255         ob_start();
256         $result = $this->_cli->monitoringCheckConfig();
257         $out = ob_get_clean();
258         
259         $this->assertEquals("CONFIG FILE OK\n", $out);
260         $this->assertEquals(0, $result);
261     }
262
263     /**
264      * testMonitoringCheckCron
265      */
266     public function testMonitoringCheckCron()
267     {
268         ob_start();
269         $result = $this->_cli->monitoringCheckCron();
270         $out = ob_get_clean();
271         
272         $lastJob = Tinebase_AsyncJob::getInstance()->getLastJob('Tinebase_Event_Async_Minutely');
273         if ($lastJob) {
274             $this->assertContains('CRON OK', $out);
275             $this->assertEquals(0, $result);
276         } else {
277             $this->assertEquals("CRON FAIL: NO LAST JOB FOUND\n", $out);
278             $this->assertEquals(1, $result);
279         }
280     }
281
282     /**
283      * testMonitoringLoginNumber
284      */
285     public function testMonitoringLoginNumber()
286     {
287         ob_start();
288         $result = $this->_cli->monitoringLoginNumber();
289         $out = ob_get_clean();
290         $this->assertEquals(0, $result);
291
292         preg_match('/LOGINS OK \| count=(\d+);;;;/', $out, $matches);
293         $this->assertGreaterThan(1, count($matches));
294         $this->assertGreaterThanOrEqual(0, $matches[1]);
295     }
296
297     /**
298      * testMonitoringActiveUsers
299      *
300      * TODO generalize monitoring tests
301      */
302     public function testMonitoringActiveUsers()
303     {
304         ob_start();
305         $result = $this->_cli->monitoringActiveUsers();
306         $out = ob_get_clean();
307         $this->assertEquals(0, $result);
308
309         preg_match('/ACTIVE USERS OK \| count=(\d+);;;;/', $out, $matches);
310         $this->assertGreaterThan(1, count($matches));
311         $this->assertGreaterThanOrEqual(1, $matches[1], 'at least unittest user should have logged in once');
312     }
313
314     /**
315      * test cleanNotes
316      */
317     public function testCleanNotes()
318     {
319         // initial clean... tests don't clean up properly
320         ob_start();
321         $this->_cli->cleanNotes();
322         $out = ob_get_clean();
323
324         $noteController = Tinebase_Notes::getInstance();
325         $models = Tinebase_Application::getInstance()->getModelsOfAllApplications();
326
327         $allNotes = $noteController->getAllNotes();
328         $dbArtifacts = $allNotes->count();
329
330         $notesCreated = 0;
331         $realDataNotes = 0;
332         foreach($models as $model) {
333             /** @var Tinebase_Record_Interface $instance */
334             $instance = new $model();
335             if ($instance->has('notes')) {
336
337                 if (strpos($model, 'Tinebase') === 0) {
338                     continue;
339                 }
340
341                 if (! $this->_idPropertyIsVarChar($instance, $model)) {
342                     continue;
343                 }
344
345                 //create dead notes for each of those models
346                 $note = new Tinebase_Model_Note(array(
347                     'note_type_id' => 1,
348                     'note'  => 'test note text',
349                     'record_id' => Tinebase_Record_Abstract::generateUID(),
350                     'record_model' => $model,
351                 ));
352
353                 $noteController->addNote($note);
354                 ++$notesCreated;
355             }
356         }
357
358         // add some real data
359         $contact = new Addressbook_Model_Contact(array(
360             'n_family' => 'someone',
361             'notes' => array(array(
362                 'note_type_id' => 1,
363                 'note'  => 'test note text for real record',
364             ))
365         ));
366         try {
367             Addressbook_Controller_Contact::getInstance()->create($contact);
368         } catch (Tinebase_Exception_Duplicate $ted) {
369
370         }
371         $realDataNotes += 2; // created a custom note
372
373         $event = new Calendar_Model_Event(array(
374             'organizer' => 'a@b.shooho',
375             'dtstart'   => '2015-01-01 00:00:00',
376             'dtend'     => '2015-01-01 01:00:00',
377             'summary'   => 'test event',
378             'notes' => array(array(
379                 'note_type_id' => 1,
380                 'note'  => 'test note text for real record',
381             ))
382         ));
383         Calendar_Controller_Event::getInstance()->create($event);
384         $realDataNotes += 2;  // created a custom note
385
386         $allNotes = $noteController->getAllNotes();
387         $this->assertEquals($notesCreated + $realDataNotes + $dbArtifacts, $allNotes->count(), 'notes created and notes in DB mismatch');
388
389         ob_start();
390         $this->_cli->cleanNotes();
391         $out = ob_get_clean();
392
393         $this->assertTrue(preg_match('/deleted \d+ notes/', $out) == 1, 'CLI job produced output: ' . $out);
394
395         $allNotes = $noteController->getAllNotes();
396         $this->assertEquals($realDataNotes + $dbArtifacts, $allNotes->count(), 'notes not completely cleaned');
397     }
398
399     protected function _idPropertyIsVarChar($instance, $model)
400     {
401         $controller = Tinebase_Core::getApplicationInstance($model);
402         $backend = $controller->getBackend();
403         if (method_exists($backend, 'getSchema')) {
404             $schema = $backend->getSchema();
405             if (isset($schema[$instance->getIdProperty()]) && strtoupper($schema[$instance->getIdProperty()]['DATA_TYPE']) != 'VARCHAR'
406                 && strtoupper($schema[$instance->getIdProperty()]['DATA_TYPE']) != 'CHAR'
407             ) {
408                 return false;
409             }
410         }
411
412         return true;
413     }
414
415     /**
416      * test cleanCustomfields
417      */
418     public function testCleanCustomfields()
419     {
420         $customFieldController = Tinebase_CustomField::getInstance();
421         $models = Tinebase_Application::getInstance()->getModelsOfAllApplications();
422
423         $customFieldConfigs = $customFieldController->searchConfig();
424         foreach($customFieldConfigs as $customFieldConfig) {
425             $filter = new Tinebase_Model_CustomField_ValueFilter(array(
426                 array('field' => 'customfield_id', 'operator' => 'equals', 'value' => $customFieldConfig->id)
427             ));
428             $customFieldValues = $customFieldController->search($filter);
429
430             $this->assertEquals(0, $customFieldValues->count(), 'custom field values found!');
431         }
432
433         $customFieldsCreated = 0;
434         $realDataCustomFields = 0;
435         foreach($models as $model) {
436             /** @var Tinebase_Record_Interface $instance */
437             $instance = new $model();
438             list($appName) = explode('_', $model);
439
440             if ($instance->has('customfields')) {
441
442                 if (strpos($model, 'Tinebase') === 0) {
443                     continue;
444                 }
445
446                 if (! $this->_idPropertyIsVarChar($instance, $model)) {
447                     continue;
448                 }
449
450                 $cf = $customFieldController->addCustomField(
451                     new Tinebase_Model_CustomField_Config(array(
452                         'application_id'    => Tinebase_Application::getInstance()->getApplicationByName($appName)->getId(),
453                         'model'             => $model,
454                         'name'              => $model,
455                         'definition'        => array(
456                             'label'             => $model,
457                             'length'            => 255,
458                             'required'          => false,
459                             'type'              => 'string',
460                         ),
461                     ))
462                 );
463
464                 //create dead customfield value for each of those models
465                 $customFieldValue = new Tinebase_Model_CustomField_Value(array(
466                     'record_id' => Tinebase_Record_Abstract::generateUID(),
467                     'customfield_id' => $cf->getId(),
468                     'value' => 'shoo value',
469                 ));
470
471                 $customFieldController->saveCustomFieldValue($customFieldValue);
472                 ++$customFieldsCreated;
473             }
474         }
475
476         // add some real data
477         $contact = new Addressbook_Model_Contact(array(
478             'n_family' => 'someone',
479             'customfields' => array(
480                 'Addressbook_Model_Contact' => 'test customfield text for real record',
481             )
482         ));
483         Addressbook_Controller_Contact::getInstance()->create($contact);
484         $realDataCustomFields += 1;
485
486         $event = new Calendar_Model_Event(array(
487             'organizer' => 'a@b.shooho',
488             'dtstart'   => '2015-01-01 00:00:00',
489             'dtend'     => '2015-01-01 01:00:00',
490             'summary'   => 'test event',
491             'customfields' => array(
492                 'Calendar_Model_Event' => 'test customfield text for real record',
493             )
494         ));
495         Calendar_Controller_Event::getInstance()->create($event);
496         $realDataCustomFields += 1;
497
498         $sum = 0;
499         $customFieldConfigs = $customFieldController->searchConfig();
500         foreach($customFieldConfigs as $customFieldConfig) {
501             $filter = new Tinebase_Model_CustomField_ValueFilter(array(
502                 array('field' => 'customfield_id', 'operator' => 'equals', 'value' => $customFieldConfig->id)
503             ));
504             $customFieldValues = $customFieldController->search($filter);
505
506             $sum += $customFieldValues->count();
507         }
508         $this->assertEquals($customFieldsCreated + $realDataCustomFields, $sum, 'customfields created and customfields in DB mismatch');
509
510         ob_start();
511         $this->_cli->cleanCustomfields();
512         $out = ob_get_clean();
513
514         $this->assertTrue(preg_match('/deleted \d+ customfield values/', $out) == 1, 'CLI job produced output: ' . $out);
515
516         $sum = 0;
517         foreach($customFieldConfigs as $customFieldConfig) {
518             $filter = new Tinebase_Model_CustomField_ValueFilter(array(
519                 array('field' => 'customfield_id', 'operator' => 'equals', 'value' => $customFieldConfig->id)
520             ));
521             $customFieldValues = $customFieldController->search($filter);
522
523             $sum += $customFieldValues->count();
524         }
525         $this->assertEquals($realDataCustomFields, $sum, 'customfields not completely cleaned');
526     }
527
528
529     /**
530      * testUserReport
531      *
532      * TODO maybe set locale to EN to ease string testing
533      */
534     public function testUserReport()
535     {
536         ob_start();
537         $result = $this->_cli->userReport();
538         $out = ob_get_clean();
539         $this->assertEquals(0, $result);
540
541         preg_match('/Number of users \(total\): (\d+)/', $out, $matches);
542         $this->assertGreaterThan(1, count($matches), $out);
543         $this->assertGreaterThanOrEqual(1, $matches[1], 'at least unittest user should be in users: ' . $out);
544
545         preg_match('/Number of users \(enabled\): (\d+)/', $out, $matches);
546         $this->assertGreaterThan(1, count($matches));
547         $this->assertGreaterThanOrEqual(1, $matches[1], 'at least unittest user should be in users');
548
549         preg_match('/Number of users \(disabled\): (\d+)/', $out, $matches);
550         $this->assertGreaterThan(1, count($matches));
551         $this->assertGreaterThanOrEqual(0, $matches[1]);
552
553         preg_match('/Number of users \(blocked\): (\d+)/', $out, $matches);
554         $this->assertGreaterThan(1, count($matches));
555         $this->assertGreaterThanOrEqual(0, $matches[1]);
556
557         preg_match('/Number of users \(expired\): (\d+)/', $out, $matches);
558         $this->assertGreaterThan(1, count($matches));
559         $this->assertGreaterThanOrEqual(0, $matches[1]);
560
561         preg_match('/Number of distinct users \(lastmonth\): (\d+)/', $out, $matches);
562         $this->assertGreaterThan(1, count($matches));
563         $this->assertGreaterThanOrEqual(1, $matches[1]);
564
565         preg_match('/Number of distinct users \(last 3 months\): (\d+)/', $out, $matches);
566         $this->assertGreaterThan(1, count($matches));
567         $this->assertGreaterThanOrEqual(1, $matches[1]);
568
569         $this->assertContains(Tinebase_Core::getUser()->accountLoginName, $out, 'unittest login name should be found');
570         $this->assertContains('Unit Test Client', $out, 'unittest should have a user agent');
571     }
572 }