Merge branch '2014.11-develop' into 2015.11
[tine20] / tests / tine20 / Admin / CliTest.php
1 <?php
2 /**
3  * Tine 2.0 - http://www.tine20.org
4  * 
5  * @package     Admin
6  * @license     http://www.gnu.org/licenses/agpl.html
7  * @copyright   Copyright (c) 2009-2013 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 dirname(dirname(__FILE__)) . DIRECTORY_SEPARATOR . 'TestHelper.php';
15
16 /**
17  * Test class for Tinebase_Admin
18  */
19 class Admin_CliTest extends TestCase
20 {
21     /**
22      * Backend
23      *
24      * @var Admin_Frontend_Cli
25      */
26     protected $_cli;
27     
28     /**
29      * @var array test objects
30      */
31     protected $objects = array();
32     
33     /**
34      * Sets up the fixture.
35      * This method is called before a test is executed.
36      *
37      * @access protected
38      */
39     
40     /**
41      * config groups
42      * 
43      */
44     protected $_testGroup = array();
45     
46     /**
47      * @var Tinebase_Record_RecordSet
48      */
49     protected $_groupsToDelete = null;
50     
51     protected function setUp()
52     {
53         parent::setUp();
54         
55         $this->_cli = new Admin_Frontend_Cli();
56         
57         $this->_usernamesToDelete = array('hmaster', 'hmeister', 'hmoster', 'irmeli', 'testuser', 'm.muster');
58
59         $this->_groupsToDelete = new Tinebase_Record_RecordSet('Tinebase_Model_Group');
60         $testGroups = array('domainuser', 'teacher', 'student');
61         foreach ($testGroups as $group) {
62             try {
63                 $this->_testGroup[$group] = Tinebase_Group::getInstance()->create(new Tinebase_Model_Group(array(
64                     'name' => $group
65                 )));
66             } catch (Exception $e) {
67                 $this->_testGroup[$group] = Tinebase_Group::getInstance()->getGroupByName($group);
68             }
69             $this->_groupsToDelete->addRecord($this->_testGroup[$group]);
70         }
71
72         $this->objects['config'] = '<?xml version="1.0" encoding="UTF-8"?>
73         <config>
74             <dryrun>0</dryrun>
75             <encoding>ISO-8859-1</encoding>
76             <mapping>
77                 <field>
78                     <source>firstname</source>
79                     <destination>accountFirstName</destination>
80                 </field>
81                 <field>
82                     <source>lastname</source>
83                     <destination>accountLastName</destination>
84                 </field>
85                 <field>
86                     <source>loginname</source>
87                     <destination>accountLoginName</destination>
88                 </field>
89                 <field>
90                     <source>password</source>
91                     <destination>password</destination>
92                 </field>
93                 <field>
94                     <source>email</source>
95                     <destination>accountEmailAddress</destination>
96                 </field>
97             </mapping>
98         </config>';
99         
100         $this->objects['configWithHeadline'] = '<?xml version="1.0" encoding="UTF-8"?>
101         <config>
102             <headline>1</headline>
103             <dryrun>0</dryrun>
104             <encoding>ISO-8859-1</encoding>
105             <mapping>
106                 <field>
107                     <source>firstname</source>
108                     <destination>accountFirstName</destination>
109                 </field>
110                 <field>
111                     <source>lastname</source>
112                     <destination>accountLastName</destination>
113                 </field>
114                 <field>
115                     <source>loginname</source>
116                     <destination>accountLoginName</destination>
117                 </field>
118                 <field>
119                     <source>password</source>
120                     <destination>password</destination>
121                 </field>
122                 <field>
123                     <source>email</source>
124                     <destination>accountEmailAddress</destination>
125                 </field>
126             </mapping>
127         </config>';
128         
129         $this->objects['configSemicolon'] = '<?xml version="1.0" encoding="UTF-8"?>
130         <config>
131             <model>Tinebase_Model_FullUser</model>
132             <plugin>Admin_Import_User_Csv</plugin>
133             <type>import</type>
134             <headline>1</headline>
135             <dryrun>0</dryrun>
136             <extension>csv</extension>
137             <delimiter>;</delimiter>
138             <mapping>
139             <field>
140                     <source>firstname</source>
141                     <destination>accountFirstName</destination>
142                 </field>
143                 <field>
144                     <source>lastname</source>
145                     <destination>accountLastName</destination>
146                 </field>
147                 <field>
148                     <source>loginname</source>
149                     <destination>accountLoginName</destination>
150                 </field>
151                 <field>
152                     <source>password</source>
153                     <destination>password</destination>
154                 </field>
155                 <field>
156                     <source>email</source>
157                     <destination>accountEmailAddress</destination>
158                 </field>
159             </mapping>
160         </config>';
161         $this->objects['configEmailuser'] = '<?xml version="1.0" encoding="UTF-8"?>
162         <config>
163             <model>Tinebase_Model_FullUser</model>
164             <plugin>Admin_Import_User_Csv</plugin>
165             <type>import</type>
166             <headline>1</headline>
167             <dryrun>0</dryrun>
168             <extension>csv</extension>
169             <mapping>
170                 <field>
171                     <source>firstname</source>
172                     <destination>accountFirstName</destination>
173                 </field>
174                 <field>
175                     <source>lastname</source>
176                     <destination>accountLastName</destination>
177                 </field>
178                 <field>
179                     <source>loginname</source>
180                     <destination>accountLoginName</destination>
181                 </field>
182                 <field>
183                     <source>password</source>
184                     <destination>password</destination>
185                 </field>
186                 <field>
187                     <source>email</source>
188                     <destination>accountEmailAddress</destination>
189                 </field>
190                 <field>
191                     <source>emailAliases</source> <!-- leerzeichen separator -->
192                     <destination>emailAliases</destination>
193                 </field>
194                 <field>
195                     <source>emailForwards</source>
196                     <destination>emailForwards</destination>
197                 </field>
198             </mapping>
199         </config>';
200           $this->objects['configAdvanced'] = '<?xml version="1.0" encoding="UTF-8"?>
201         <config>
202            <model>Tinebase_Model_FullUser</model>
203            <plugin>Admin_Import_Csv</plugin>
204            <type>import</type>
205            <headline>1</headline>
206            <dryrun>0</dryrun>
207            <delimiter>,</delimiter>
208            <mapping>
209                <field>
210                    <source>firstname</source>
211                    <destination>accountFirstName</destination>
212                </field>
213                <field>
214                    <source>lastname</source>
215                    <destination>accountLastName</destination>
216                </field>
217                <field>
218                    <source>objectname</source>
219                    <destination>accountLoginName</destination>
220                </field>
221                <field>
222                    <source>password</source>
223                    <destination>password</destination>
224                </field>
225                <field>
226                    <source>primary_group_id</source>
227                    <destination>accountPrimaryGroup</destination>
228                </field>
229                <field>
230                    <source>additional_groups</source>
231                    <destination>groups</destination>
232                </field>
233               <field>
234                    <source>accountHomeDirectory</source>
235                    <destination>accountHomeDirectory</destination>
236                </field>
237                <field>
238                    <source>accountHomeDirectoryPrefix</source>
239                    <destination>accountHomeDirectoryPrefix</destination>
240                </field>
241                <field>
242                    <source>accountLoginShell</source>
243                    <destination>accountLoginShell</destination>
244                </field>
245                <field>
246                    <source>homePath</source>
247                    <destination>homePath</destination>
248                </field>
249                <field>
250                    <source>homeDrive</source>
251                    <destination>homeDrive</destination>
252                </field>
253                <field>
254                    <source>logonScript</source>
255                    <destination>logonScript</destination>
256                </field>
257                <field>
258                    <source>profilePath</source>
259                    <destination>profilePath</destination>
260                </field>
261            </mapping>
262         </config>';
263     }
264     
265     /**
266      * Tears down the fixture
267      * This method is called after a test is executed.
268      *
269      * @access protected
270      */
271     protected function tearDown()
272     {
273         $this->_groupIdsToDelete = $this->_groupsToDelete->getArrayOfIds();
274         parent::tearDown();
275     }
276     
277     /**
278      * test to import admin users
279      *
280      */
281     public function testImportUsers()
282     {
283         $out = $this->_importUsers($this->objects['config'], dirname(__FILE__) . '/files/test.csv', 'admin_user_import_csv_test');
284         $this->_checkResult($out);
285     }
286     
287     /**
288      * import users
289      *
290      * @param string $_config xml config
291      * 
292      * @see 0008300: Import User via CLI don't import all fields
293      */
294     protected function _importUsers($_config, $_filename, $_definition)
295     {
296         // create definition / check if exists
297         try {
298             $definition = Tinebase_ImportExportDefinition::getInstance()->getByName($_definition);
299             $definition->plugin_options = $_config;
300         } catch (Tinebase_Exception_NotFound $e) {
301             $definition = Tinebase_ImportExportDefinition::getInstance()->create(new Tinebase_Model_ImportExportDefinition(array(
302                 'application_id'    => Tinebase_Application::getInstance()->getApplicationByName('Admin')->getId(),
303                 'name'              => $_definition,
304                 'type'              => 'import',
305                 'model'             => 'Tinebase_Model_FullUser',
306                 'plugin'            => 'Admin_Import_User_Csv',
307                 'plugin_options'    => $_config
308             )));
309         }
310         
311         $tempFilename = TestServer::replaceEmailDomainInFile($_filename);
312         
313         $opts = new Zend_Console_Getopt('abp:');
314         $opts->setArguments(array($tempFilename, 'definition=' . $_definition));
315         
316         // start import (dry run)
317         ob_start();
318         $this->_cli->importUser($opts);
319         $out = ob_get_clean();
320         
321         return $out;
322     }
323     
324     /**
325      * check import result
326      * 
327      * @param string $out
328      */
329     protected function _checkResult($out, $username = 'hmoster')
330     {
331         // check output
332         if ($username == 'hmoster') {
333             $this->assertEquals("Imported 3 records. Import failed for 0 records. \n", $out);
334         } else {
335             $this->assertEquals("Imported 1 records. Import failed for 2 records. \n", $out);
336         }
337         
338         // check if users (with their data) have been added to tine20
339         $user = Tinebase_User::getInstance()->getFullUserByLoginName($username);
340         if ($username == 'hmoster') {
341             $this->assertEquals('Hins', $user->accountFirstName);
342         }
343         $maildomain = TestServer::getPrimaryMailDomain();
344         $this->assertEquals($username . '@' . $maildomain, $user->accountEmailAddress);
345     }
346
347     /**
348      * test to import admin users
349      */
350     public function testImportUsersWithHeadline()
351     {
352         $out = $this->_importUsers($this->objects['configWithHeadline'], dirname(__FILE__) . '/files/testHeadline.csv', 'admin_user_import_csv_test_headline');
353         $this->_checkResult($out);
354     }
355     
356     /**
357      * testImportUsersWithEmailAndSemicolon
358      * 
359      * @see 0008300: Import User via CLI don't import all fields
360      */
361     public function testImportUsersWithEmailAndSemicolon()
362     {
363         $out = $this->_importUsers($this->objects['configSemicolon'], dirname(__FILE__) . '/files/tine_user3.csv', 'admin_user_import_csv_test_semicolon');
364         $this->_checkResult($out, 'irmeli');
365     }
366     
367     /**
368      * testImportUsersWithEmailUser
369      */
370     public function testImportUsersWithEmailUser()
371     {
372         $userBackend = Tinebase_User::getInstance();
373         $maildomain = TestServer::getPrimaryMailDomain();
374
375         $readFile = fopen(dirname(__FILE__) . '/files/tine_user5.csv', 'r');
376         $writeFile = fopen('test.csv', 'w');
377         $delimiter = ',';
378         $enclosure = '"';
379         
380         while (($row = fgetcsv($readFile)) !== false) {
381             foreach ($row as $colIndex => &$field) {
382                 $field = str_replace('DOMAIN', $maildomain, $field);
383             }
384             fputcsv($writeFile, $row, $delimiter, $enclosure);
385         }
386         
387         fclose($readFile);
388         fclose($writeFile);
389         
390         if (! array_key_exists('Tinebase_EmailUser_Smtp_Postfix', $userBackend->getPlugins())) {
391             $this->markTestSkipped('Postfix SQL plugin not enabled');
392         }
393         
394         $this->_importUsers($this->objects['configEmailuser'], 'test.csv', 'admin_user_import_csv_test_emailuser');
395         $newUser = $userBackend->getFullUserByLoginName('testuser');
396         $this->assertEquals(array('contact@' . $maildomain, 'kontakt@' . $maildomain), $newUser->smtpUser->emailAliases);
397         $this->assertEquals(array('test@' . $maildomain), $newUser->smtpUser->emailForwards);
398         $this->assertTrue($newUser->smtpUser->emailForwardOnly);
399         unlink("test.csv");
400     }
401     
402     /**
403      * testImportUsersAdvanced
404      */
405     public function testImportUsersAdvanced()
406     {
407         $userBackend = Tinebase_User::getInstance();
408         
409         $readFile = fopen(dirname(__FILE__) . '/files/test_teacher.csv', 'r');
410         $writeFile = fopen('test2.csv', 'w');
411         $delimiter = ',';
412         $enclosure = '"';
413         
414         while (($row = fgetcsv($readFile)) !== false) {
415             foreach ($row as $colIndex => &$field) {
416                 $field = str_replace('PRIMARYGROUP', $this->_testGroup['domainuser']->getId(), $field);
417                 $field = str_replace('GROUP1', $this->_testGroup['teacher']->getId(), $field);
418                 $field = str_replace('GROUP2', $this->_testGroup['student']->getId(), $field);
419             }
420             fputcsv($writeFile, $row, $delimiter, $enclosure);
421         }
422         
423         fclose($readFile);
424         fclose($writeFile);
425         
426         $this->_importUsers($this->objects['configAdvanced'], 'test2.csv', 'admin_user_import_csv_test_advanced');
427         $newUser = Tinebase_User::getInstance()->getFullUserByLoginName('m.muster');
428         
429         $newUserMemberships = Tinebase_Group::getInstance()->getGroupMemberships($newUser);
430         $this->assertTrue(in_array($this->_testGroup['domainuser']->getId(), $newUserMemberships),
431         ' not member of the domainuser group (' . $this->_testGroup['domainuser']->getId() . ') ' . print_r($newUserMemberships, TRUE));
432         $this->assertTrue(in_array($this->_testGroup['student']->getId(), $newUserMemberships),
433         ' not member of the student group (' . $this->_testGroup['student']->getId() . ') ' . print_r($newUserMemberships, TRUE));
434         $this->assertTrue(in_array($this->_testGroup['teacher']->getId(), $newUserMemberships),
435         ' not member of the teacher group (' . $this->_testGroup['teacher']->getId() . ') ' . print_r($newUserMemberships, TRUE));
436         
437         $this->assertEquals('/bin/false', $newUser->accountLoginShell);
438         $this->assertEquals('/storage/lehrer/m.muster', $newUser->accountHomeDirectory);
439         
440         if (array_key_exists('Tinebase_User_Plugin_Samba', $userBackend->getPlugins())) {
441             $this->assertEquals('\\fileserver\profiles\m.muster', $newUser->sambaSAM->profilePath);
442         }
443         unlink("test2.csv");
444     }
445     
446     /**
447      * tests if import with members from csv works correctly
448      */
449     public function testImportGroups()
450     {
451         $opts = new Zend_Console_Getopt('abp:');
452         $opts->setArguments(array(dirname(__FILE__) . '/files/import_groups.csv', 'definition=admin_group_import_csv'));
453         
454         // start import (dry run)
455         ob_start();
456         $this->_cli->importGroups($opts);
457         $out = ob_get_clean();
458         $this->assertStringStartsWith('Imported 4 records.', $out);
459         
460         $expected = array('men' => 3, 'women' => 2, 'highperformers' => 2, 'lowperformers' => 3);
461         $this->_testImportGroupsHelper($expected);
462         
463         $opts->setArguments(array(dirname(__FILE__) . '/files/import_groups_update.csv', 'definition=admin_group_import_csv'));
464         ob_start();
465         $this->_cli->importGroups($opts);
466         $out = ob_get_clean();
467         $this->assertStringStartsWith('Imported 0 records.', $out);
468         
469         $expected = array('men' => 3, 'women' => 2,  'lowperformers' => 2, 'highperformers' => 3);
470         $this->_testImportGroupsHelper($expected);
471     }
472     
473     protected function _testImportGroupsHelper($expected)
474     {
475         $be = new Tinebase_Group_Sql();
476         
477         foreach($expected as $name => $count) {
478             $group = $be->getGroupByName($name);
479             $members = $be->getGroupMembers($group);
480         
481             $this->assertEquals($count, count($members), 'Group ' . $name . ' should have ' . $count . ' members!');
482             $this->assertEquals('displayed', $group->visibility, 'Group ' . $name . ' should be visible!');
483         }
484     }
485 }