fb248c0d33170f864f98dba9e939406971bda337
[tine20] / tests / tine20 / Calendar / Frontend / iMIPTest.php
1 <?php
2 /**
3  * Tine 2.0 - http://www.tine20.org
4  * 
5  * @package     Calendar
6  * @license     http://www.gnu.org/licenses/agpl.html
7  * @copyright   Copyright (c) 2011-2012 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(dirname(__FILE__))) . DIRECTORY_SEPARATOR . 'TestHelper.php';
15
16 /**
17  * Test class for Calendar_Frontend_iMIP
18  */
19 class Calendar_Frontend_iMIPTest extends PHPUnit_Framework_TestCase
20 {
21     /**
22      * event ids that should be deleted in tearDown
23      * 
24      * @var unknown_type
25      */
26     protected $_eventIdsToDelete = array();
27     
28     /**
29      * iMIP frontent to be tested
30      * 
31      * @var Calendar_Frontend_iMIP
32      */
33     protected $_iMIPFrontend = NULL;
34     
35     /**
36      * iMIP frontent to be tested
37      * 
38      * @var Calendar_Frontend_iMIPMock
39      */
40     protected $_iMIPFrontendMock = NULL;
41     
42     /**
43     * email test class
44     *
45     * @var Felamimail_Controller_MessageTest
46     */
47     protected $_emailTestClass;
48         
49     /**
50      * Runs the test methods of this class.
51      *
52      * @access public
53      * @static
54      */
55     public static function main()
56     {
57         $suite  = new PHPUnit_Framework_TestSuite('Tine 2.0 Calendar iMIP Tests');
58         PHPUnit_TextUI_TestRunner::run($suite);
59     }
60
61     /**
62      * Sets up the fixture.
63      * This method is called before a test is executed.
64      *
65      * @access protected
66      */
67     protected function setUp()
68     {
69         Calendar_Controller_Event::getInstance()->sendNotifications(true);
70         
71         $this->_iMIPFrontend = new Calendar_Frontend_iMIP();
72         $this->_iMIPFrontendMock = new Calendar_Frontend_iMIPMock();
73         
74         try {
75             $this->_emailTestClass = new Felamimail_Controller_MessageTest();
76             $this->_emailTestClass->setup();
77         } catch (Exception $e) {
78             // do nothing
79         }
80     }
81
82     /**
83      * Tears down the fixture
84      * This method is called after a test is executed.
85      *
86      * @access protected
87      */
88     protected function tearDown()
89     {
90         Calendar_Controller_Event::getInstance()->sendNotifications(false);
91         
92         if (! empty($this->_eventIdsToDelete)) {
93             Calendar_Controller_Event::getInstance()->delete($this->_eventIdsToDelete);
94         }
95         
96         if ($this->_emailTestClass instanceof Felamimail_Controller_MessageTest) {
97             $this->_emailTestClass->tearDown();
98         }
99     }
100     
101     /**
102      * testExternalInvitationRequestAutoProcess
103      */
104     public function testExternalInvitationRequestAutoProcess()
105     {
106         $ics = file_get_contents(dirname(__FILE__) . '/files/invitation_request_external.ics' );
107         $iMIP = new Calendar_Model_iMIP(array(
108             'id'             => Tinebase_Record_Abstract::generateUID(),
109             'ics'            => $ics,
110             'method'         => 'REQUEST',
111             'originator'     => 'l.kneschke@caldav.org',
112         ));
113         
114         $this->_iMIPFrontend->autoProcess($iMIP);
115         $prepared = $this->_iMIPFrontend->prepareComponent($iMIP);
116
117         $this->assertEquals(3, count($prepared->event->attendee));
118         $this->assertEquals('test mit extern', $prepared->event->summary);
119     }
120
121     /**
122     * testSupportedPrecondition
123     */
124     public function testUnsupportedPrecondition()
125     {
126         $iMIP = $this->_getiMIP('PUBLISH');
127             
128         $prepared = $this->_iMIPFrontend->prepareComponent($iMIP);
129     
130         $this->assertEquals(1, count($prepared->preconditions));
131         $this->assertEquals('processing published events is not supported yet', $prepared->preconditions[Calendar_Model_iMIP::PRECONDITION_SUPPORTED][0]['message']);
132         $this->assertFalse($prepared->preconditions[Calendar_Model_iMIP::PRECONDITION_SUPPORTED][0]['check']);
133     }
134     
135     /**
136      * get iMIP record from internal event
137      * 
138      * @param string $_method
139      * @param boolean $_addEventToiMIP
140      * @return Calendar_Model_iMIP
141      */
142     protected function _getiMIP($_method, $_addEventToiMIP = FALSE, $_testEmptyMethod = FALSE)
143     {
144         $event = $this->_getEvent();
145         $event = Calendar_Controller_Event::getInstance()->create($event);
146         $this->_eventIdsToDelete[] = $event->getId();
147         
148         // get iMIP invitation for event
149         $converter = Calendar_Convert_Event_VCalendar_Factory::factory(Calendar_Convert_Event_VCalendar_Factory::CLIENT_GENERIC);
150         $vevent = $converter->fromTine20Model($event);
151         $vevent->METHOD = $_method;
152         $ics = $vevent->serialize();
153         
154         $testConfig = Zend_Registry::get('testConfig');
155         $email = ($testConfig->email) ? $testConfig->email : Tinebase_Core::getUser()->accountEmailAddress;
156         
157         $iMIP = new Calendar_Model_iMIP(array(
158             'id'             => Tinebase_Record_Abstract::generateUID(),
159             'ics'            => $ics,
160             'method'         => ($_testEmptyMethod) ? NULL : $_method,
161             'originator'     => $email,
162         ));
163         
164         if ($_addEventToiMIP) {
165             $iMIP->event = $event;
166         }
167         
168         return $iMIP;
169     }
170     
171     /**
172      * testInternalInvitationRequestAutoProcess
173      */
174     public function testInternalInvitationRequestAutoProcess()
175     {
176         $iMIP = $this->_getiMIP('REQUEST');
177         
178         $this->_iMIPFrontend->autoProcess($iMIP);
179         $prepared = $this->_iMIPFrontend->prepareComponent($iMIP);
180         
181         $this->assertEquals(2, count($prepared->event->attendee), 'expected 2 attendee');
182         $this->assertEquals('Sleep very long', $prepared->event->summary);
183         $this->assertTrue(empty($prepared->preconditions));
184     }
185
186     /**
187     * testInternalInvitationRequestAutoProcessOwnStatusAlreadySet
188     */
189     public function testInternalInvitationRequestPreconditionOwnStatusAlreadySet()
190     {
191         $iMIP = $this->_getiMIP('REQUEST', TRUE);
192         // set own status
193         $ownAttender = Calendar_Model_Attender::getOwnAttender($iMIP->getEvent()->attendee);
194         $ownAttender->status = Calendar_Model_Attender::STATUS_ACCEPTED;
195         Calendar_Controller_Event::getInstance()->attenderStatusUpdate($iMIP->getEvent(), $ownAttender, $ownAttender->status_authkey);
196         
197         $prepared = $this->_iMIPFrontend->prepareComponent($iMIP);
198         $this->assertFalse(empty($prepared->preconditions));
199         $this->assertTrue((isset($prepared->preconditions[Calendar_Model_iMIP::PRECONDITION_RECENT]) || array_key_exists(Calendar_Model_iMIP::PRECONDITION_RECENT, $prepared->preconditions)));
200     }
201     
202     /**
203     * returns a simple event
204     *
205     * @return Calendar_Model_Event
206     */
207     protected function _getEvent()
208     {
209         return new Calendar_Model_Event(array(
210             'summary'     => 'Sleep very long',
211             'dtstart'     => '2012-03-25 01:00:00',
212             'dtend'       => '2012-03-25 11:15:00',
213             'description' => 'Early to bed and early to rise, makes a men healthy, wealthy and wise ... not.',
214             'attendee'    => $this->_getAttendee(),
215             'organizer'   => Tinebase_Core::getUser()->contact_id,
216             'uid'         => Calendar_Model_Event::generateUID(),
217         ));
218     }
219     
220     /**
221      * get test attendee
222      *
223      * @return Tinebase_Record_RecordSet
224      */
225     protected function _getAttendee()
226     {
227         $personas = Zend_Registry::get('personas');
228         $sclever = $personas['sclever'];
229         
230         return new Tinebase_Record_RecordSet('Calendar_Model_Attender', array(
231             array(
232                 'user_id'        => Tinebase_Core::getUser()->contact_id,
233                 'user_type'      => Calendar_Model_Attender::USERTYPE_USER,
234                 'role'           => Calendar_Model_Attender::ROLE_REQUIRED,
235                 'status_authkey' => Tinebase_Record_Abstract::generateUID(),
236             ),
237             array(
238                 'user_id'        => $sclever->contact_id,
239                 'user_type'      => Calendar_Model_Attender::USERTYPE_USER,
240                 'role'           => Calendar_Model_Attender::ROLE_REQUIRED,
241                 'status_authkey' => Tinebase_Record_Abstract::generateUID(),
242             ),
243         ));
244     }
245     
246     /**
247      * testExternalInvitationRequestProcess
248      * - uses felamimail to cache external invitation message
249      * 
250      * -> external invitation requests are not supported atm
251      */
252     public function testExternalInvitationRequestProcess()
253     {
254         $this->_checkIMAPConfig();
255         
256         $testConfig = Zend_Registry::get('testConfig');
257         $email = ($testConfig->email) ? $testConfig->email : Tinebase_Core::getUser()->accountEmailAddress;
258         
259         // handle message with fmail (add to cache)
260         $message = $this->_emailTestClass->messageTestHelper('calendar_request.eml', NULL, NULL, array('unittest@tine20.org', $email));
261         $complete = Felamimail_Controller_Message::getInstance()->getCompleteMessage($message);
262         
263         $iMIP = $complete->preparedParts->getFirstRecord()->preparedData;
264         
265         Calendar_Controller_EventNotificationsTests::flushMailer();
266         $result = $this->_iMIPFrontend->process($iMIP, Calendar_Model_Attender::STATUS_ACCEPTED);
267         $this->_iMIPFrontend->prepareComponent($iMIP);
268         $this->_eventIdsToDelete[] = $iMIP->event->getId();
269         
270         // assert external organizer
271         $this->assertEquals('l.kneschke@caldav.org', $iMIP->event->organizer->email, 'wrong organizer');
272         $this->assertTrue(empty($iMIP->event->organizer->account_id), 'organizer must not have an account');
273         
274         // assert attendee
275         $this->assertEquals(1, count($iMIP->event->attendee), 'all attendee but curruser must be whiped');
276         $this->assertEquals($email, $iMIP->event->attendee->getFirstRecord()->user_id->email, 'wrong attendee mail');
277         $this->assertEquals(Tinebase_Core::getUser()->getId(), $iMIP->event->attendee->getFirstRecord()->user_id->account_id, 'wrong attendee');
278         $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $iMIP->event->attendee->getFirstRecord()->status);
279         
280         // assert REPLY message
281         $messages = Calendar_Controller_EventNotificationsTests::getMessages();
282         $this->assertEquals(1, count($messages), 'only one mails should be send');
283         $this->assertTrue(in_array('l.kneschke@caldav.org', $messages[0]->getRecipients()), 'organizer is not a receipient');
284         $this->assertContains('METHOD:REPLY', var_export($messages[0], TRUE), 'method missing');
285     }
286     
287     /**
288      * check IMAP config and marks test as skipped if no IMAP backend is configured
289      */
290     protected function _checkIMAPConfig()
291     {
292         $imapConfig = Tinebase_Config::getInstance()->get(Tinebase_Config::IMAP);
293         if (! $imapConfig || ! isset($imapConfig->useSystemAccount)
294             || $imapConfig->useSystemAccount != TRUE
295             || ! $this->_emailTestClass instanceof Felamimail_Controller_MessageTest
296         ) {
297             $this->markTestSkipped('IMAP backend not configured');
298         }
299     }
300
301     /**
302      * testExternalPublishProcess
303      * - uses felamimail to cache external publish message
304      * 
305      * NOTE: meetup sends REQUEST w.o. attendee. We might think of autoconvert this to PUBLISH
306      */
307     public function testExternalPublishProcess()
308     {
309         $this->_checkIMAPConfig();
310         
311         // handle message with fmail (add to cache)
312         $message = $this->_emailTestClass->messageTestHelper('meetup.eml');
313         $complete = Felamimail_Controller_Message::getInstance()->getCompleteMessage($message);
314         
315         $iMIP = $complete->preparedParts->getFirstRecord()->preparedData;
316         
317         $this->setExpectedException('Calendar_Exception_iMIP', 'iMIP preconditions failed: ATTENDEE');
318         $result = $this->_iMIPFrontend->process($iMIP);
319     }
320
321     /**
322      * testInternalInvitationRequestProcess
323      */
324     public function testInternalInvitationRequestProcess()
325     {
326         $iMIP = $this->_getiMIP('REQUEST');
327         $result = $this->_iMIPFrontendMock->process($iMIP, Calendar_Model_Attender::STATUS_TENTATIVE);
328         
329         $event = Calendar_Controller_MSEventFacade::getInstance()->lookupExistingEvent($iMIP->getEvent());
330         
331         $attender = Calendar_Model_Attender::getOwnAttender($event->attendee);
332         $this->assertEquals(Calendar_Model_Attender::STATUS_TENTATIVE, $attender->status);
333     }
334
335     /**
336      * testEmptyMethod
337      */
338     public function testEmptyMethod()
339     {
340         $iMIP = $this->_getiMIP('REQUEST', FALSE, TRUE);
341         
342         $this->assertEquals('REQUEST', $iMIP->method);
343     }
344     
345     /**
346      * testInvitationInternalReplyPreconditions
347      */
348     public function testInvitationInternalReplyPreconditions()
349     {
350         $iMIP = $this->_getiMIP('REPLY');
351         $prepared = $this->_iMIPFrontend->prepareComponent($iMIP);
352         
353         $this->assertFalse(empty($prepared->preconditions), 'empty preconditions');
354         $this->assertTrue((isset($prepared->preconditions[Calendar_Model_iMIP::PRECONDITION_TOPROCESS]) || array_key_exists(Calendar_Model_iMIP::PRECONDITION_TOPROCESS, $prepared->preconditions)), 'missing PRECONDITION_TOPROCESS');
355     }
356     
357     /**
358      * test no seq update
359      * test no notifications
360      *
361     public function testInvitationInternalReplyAutoProcess()
362     {
363         // flush mailer
364         if (isset(Tinebase_Core::getConfig()->actionqueue)) {
365             Tinebase_ActionQueue::getInstance()->processQueue(10000);
366         }
367         Tinebase_Smtp::getDefaultTransport()->flush();
368         
369         $iMIP = $this->_getiMIP('REPLY', TRUE);
370         $event = $iMIP->getEvent();
371         
372         print_r($event->getId());
373         try {
374             $this->_iMIPFrontend->autoProcess($iMIP);
375         } catch (Exception $e) {
376             $this->fail('autoProcess throwed Exception');
377         }
378         
379         
380     }
381     */
382     
383     /**
384      * testInvitationExternalReply
385      */
386     public function testInvitationExternalReply()
387     {
388         $testConfig = Zend_Registry::get('testConfig');
389         $email = ($testConfig->email) ? $testConfig->email : Tinebase_Core::getUser()->accountEmailAddress;
390         
391         $ics = file_get_contents(dirname(__FILE__) . '/files/invitation_reply_external_accepted.ics' );
392         $ics = preg_replace('/unittest@tine20\.org/', $email, $ics);
393         
394         $iMIP = new Calendar_Model_iMIP(array(
395             'id'             => Tinebase_Record_Abstract::generateUID(),
396             'ics'            => $ics,
397             'method'         => 'REPLY',
398             'originator'     => 'mail@corneliusweiss.de',
399         ));
400         
401         $this->assertEquals(1, $iMIP->getEvent()->seq);
402         $this->assertTrue(! empty($iMIP->getEvent()->last_modified_time));
403         
404         // force creation of external attendee
405         $externalAttendee = new Calendar_Model_Attender(array(
406             'user_type'     => Calendar_Model_Attender::USERTYPE_USER,
407             'user_id'       => $iMIP->getEvent()->attendee->getFirstRecord()->user_id,
408             'status'        => Calendar_Model_Attender::STATUS_NEEDSACTION
409         ));
410         
411         // create matching event
412         $event = new Calendar_Model_Event(array(
413             'summary'     => 'TEST7',
414             'dtstart'     => '2011-11-30 14:00:00',
415             'dtend'       => '2011-11-30 15:00:00',
416             'description' => 'Early to bed and early to rise, makes a men healthy, wealthy and wise ...',
417             'attendee'    => $this->_getAttendee(),
418             'organizer'   => Tinebase_Core::getUser()->contact_id,
419             'uid'         => 'a8d10369e051094ae9322bd65e8afecac010bfc8',
420         ));
421         $event->attendee->addRecord($externalAttendee);
422         $event = Calendar_Controller_Event::getInstance()->create($event);
423         $this->_eventIdsToDelete[] = $event->getId();
424         
425         // TEST NORMAL REPLY
426         try {
427             $this->_iMIPFrontend->autoProcess($iMIP);
428         } catch (Exception $e) {
429             $this->fail('TEST NORMAL REPLY autoProcess throws Exception: ' . $e);
430         }
431         unset($iMIP->existing_event);
432         
433         $updatedEvent = Calendar_Controller_Event::getInstance()->get($event->getId());
434         $updatedExternalAttendee = Calendar_Model_Attender::getAttendee($updatedEvent->attendee, $externalAttendee);
435         
436         $this->assertEquals(3, count($updatedEvent->attendee));
437         $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $updatedExternalAttendee->status, 'status not updated');
438     
439         // TEST ACCEPTABLE NON RECENT REPLY
440         $updatedExternalAttendee->status = Calendar_Model_Attender::STATUS_NEEDSACTION;
441         Calendar_Controller_Event::getInstance()->attenderStatusUpdate($updatedEvent, $updatedExternalAttendee, $updatedExternalAttendee->status_authkey);
442         try {
443             $iMIP->preconditionsChecked = false;
444             $this->_iMIPFrontend->autoProcess($iMIP);
445         } catch (Exception $e) {
446             $this->fail('TEST ACCEPTABLE NON RECENT REPLY autoProcess throws Exception: ' . $e);
447         }
448         unset($iMIP->existing_event);
449         
450         $updatedEvent = Calendar_Controller_Event::getInstance()->get($event->getId());
451         $updatedExternalAttendee = Calendar_Model_Attender::getAttendee($updatedEvent->attendee, $externalAttendee);
452         
453         $this->assertEquals(3, count($updatedEvent->attendee));
454         $this->assertEquals(Calendar_Model_Attender::STATUS_ACCEPTED, $updatedExternalAttendee->status, 'status not updated');
455     
456         // TEST NON ACCEPTABLE NON RECENT REPLY
457         $this->setExpectedException('Calendar_Exception_iMIP', 'iMIP preconditions failed: RECENT');
458         $iMIP->preconditionsChecked = false;
459         $this->_iMIPFrontend->autoProcess($iMIP);
460     }
461
462 //     /**
463 //      * testInvitationCancel
464 //      * 
465 //      * @todo implement
466 //      */
467 //     public function testInvitationCancel()
468 //     {
469         
470 //     }
471     
472 //     public function testOrganizerSendBy()
473 //     {
474         
475 //     }
476 }