Ejemplo n.º 1
0
    def testAbsoluteURL(self):
        """Version 0.7.0 does not handle responses with absolute URLs very well, ref https://github.com/python-caldav/caldav/pull/103"""
        ## none of this should initiate any communication
        client = DAVClient(url='http://cal.example.com/')
        principal = Principal(client=client,
                              url='http://cal.example.com/home/bernard/')
        ## now, ask for the calendar_home_set, but first we need to mock up client.propfind
        mocked_response = mock.MagicMock()
        mocked_response.status_code = 207
        mocked_response.reason = 'multistatus'
        mocked_response.headers = {}
        mocked_response.content = """
<xml>
<d:multistatus xmlns:d="DAV:" xmlns:c="urn:ietf:params:xml:ns:caldav">
    <d:response>
        <d:href>http://cal.example.com/home/bernard/</d:href>
        <d:propstat>
            <d:prop>
                <c:calendar-home-set>
                    <d:href>http://cal.example.com/home/bernard/calendars/</d:href>
                </c:calendar-home-set>
            </d:prop>
            <d:status>HTTP/1.1 200 OK</d:status>
        </d:propstat>
    </d:response>
</d:multistatus>
</xml>"""
        mocked_davresponse = DAVResponse(mocked_response)
        client.propfind = mock.MagicMock(return_value=mocked_davresponse)
        bernards_calendars = principal.calendar_home_set
        assert_equal(bernards_calendars.url,
                     URL('http://cal.example.com/home/bernard/calendars/'))
Ejemplo n.º 2
0
    def setup(self):
        logging.debug("############## test setup")

        if self.server_params.get('unique_calendar_ids', False):
            self.testcal_id = 'testcalendar-' + str(uuid.uuid4())
            self.testcal_id2 = 'testcalendar-' + str(uuid.uuid4())
        else:
            self.testcal_id = "pythoncaldav-test"
            self.testcal_id2 = "pythoncaldav-test2"

        self.conn_params = self.server_params.copy()
        for x in list(self.conn_params.keys()):
            if x not in ('url', 'proxy', 'username', 'password',
                         'ssl_verify_cert'):
                self.conn_params.pop(x)
        self.caldav = DAVClient(**self.conn_params)
        self.principal = self.caldav.principal()

        logging.debug("## going to tear down old test calendars, "
                      "in case teardown wasn't properly executed "
                      "last time tests were run")
        self._teardown()

        logging.debug("##############################")
        logging.debug("############## test setup done")
        logging.debug("##############################")
Ejemplo n.º 3
0
def MockedDAVClient(xml_returned):
    """
    For unit testing - a mocked DAVClient returning some specific content every time
    a request is performed
    """
    client = DAVClient(
        url='https://somwhere.in.the.universe.example/some/caldav/root')
    client.request = mock.MagicMock(
        return_value=MockedDAVResponse(xml_returned))
    return client
Ejemplo n.º 4
0
    def setup(self):
        logging.debug("############## test setup")
        self.conn_params = self.server_params.copy()
        for x in self.conn_params.keys():
            if not x in ('url', 'proxy', 'username', 'password'):
                self.conn_params.pop(x)
        self.caldav = DAVClient(**self.conn_params)
        self.principal = self.caldav.principal()

        ## tear down old test calendars, in case teardown wasn't properly
        ## executed last time tests were run
        self._teardown()

        logging.debug("############## test setup done")
Ejemplo n.º 5
0
 def testDefaultClient(self):
     """When no client is given to a DAVObject, but the parent is given, parent.client will be used"""
     client = DAVClient(url="http://*****:*****@calendar.example:80/")
     calhome = CalendarSet(client,
                           "http://*****:*****@calendar.example:80/me/")
     calendar = Calendar(parent=calhome)
     assert_equal(calendar.client, calhome.client)
Ejemplo n.º 6
0
def push_caldavs(icals):
    '''Regist ical formatted events to CalDAV server'''
    client = DAVClient(url=CALDAV_URL,
                       username=CALDAV_USER,
                       password=CALDAV_PASS)
    principal = client.principal()
    # get target calendar and remove all events
    calendar = [c for c in principal.calendars() if c.name == CAL_NAME][0]
    for event in calendar.events():
        event.delete()
    # put events
    if icals:
        for ical in icals:
            try:
                calendar.add_event(ical)
            except BaseException:
                print(ical)
Ejemplo n.º 7
0
    def testRequestNonAscii(self, mocked):
        """
        ref https://github.com/python-caldav/caldav/issues/83
        """
        mocked().status_code = 200
        cal_url = "http://*****:*****@calendar.møøh.example:80/"
        client = DAVClient(url=cal_url)
        response = client.put('/foo/møøh/bar', 'bringebærsyltetøy 北京 пиво', {})
        assert_equal(response.status, 200)
        assert (response.tree is None)

        if PY3:
            response = client.put('/foo/møøh/bar'.encode('utf-8'),
                                  'bringebærsyltetøy 北京 пиво'.encode('utf-8'),
                                  {})
        else:
            response = client.put(u'/foo/møøh/bar',
                                  u'bringebærsyltetøy 北京 пиво', {})
        assert_equal(response.status, 200)
        assert (response.tree is None)
Ejemplo n.º 8
0
    def testProxy(self):
        if self.caldav.url.scheme == 'https':
            logging.info("Skipping %s.testProxy as the TinyHTTPProxy implementation doesn't support https")
            return

        server_address = ('127.0.0.1', 8080)
        proxy_httpd = NonThreadingHTTPServer (server_address, ProxyHandler, logging.getLogger ("TinyHTTPProxy"))

        threadobj = threading.Thread(target=proxy_httpd.serve_forever)
        try:
            threadobj.start()
            assert(threadobj.is_alive())
            conn_params = self.conn_params.copy()
            conn_params['proxy'] = proxy
            c = DAVClient(**conn_params)
            p = c.principal()
            assert_not_equal(len(p.calendars()), 0)
        finally:
            proxy_httpd.shutdown()
            ## this should not be necessary, but I've observed some failures
            if threadobj.is_alive():
                time.sleep(0.05)
            assert(not threadobj.is_alive())

        threadobj = threading.Thread(target=proxy_httpd.serve_forever)
        try:
            threadobj.start()
            assert(threadobj.is_alive())
            conn_params = self.conn_params.copy()
            conn_params['proxy'] = proxy_noport
            c = DAVClient(**conn_params)
            p = c.principal()
            assert_not_equal(len(p.calendars()), 0)
            assert(threadobj.is_alive())
        finally:
            proxy_httpd.shutdown()
            ## this should not be necessary
            if threadobj.is_alive():
                time.sleep(0.05)
            assert(not threadobj.is_alive())
Ejemplo n.º 9
0
    def setup(self):
        logging.debug("############## test setup")
        self.conn_params = self.server_params.copy()
        for x in self.conn_params.keys():
            if not x in ('url', 'proxy', 'username', 'password'):
                self.conn_params.pop(x)
        self.caldav = DAVClient(**self.conn_params)
        self.principal = self.caldav.principal()

        ## tear down old test calendars, in case teardown wasn't properly 
        ## executed last time tests were run
        self._teardown()

        logging.debug("############## test setup done")
Ejemplo n.º 10
0
 def testInstance(self):
     cal_url = "http://*****:*****@calendar.example:80/"
     client = DAVClient(url=cal_url)
     my_event = Event(client, data=ev1)
     my_event.vobject_instance.vevent.summary.value = 'new summary'
     assert ('new summary' in my_event.data)
     icalobj = my_event.icalendar_instance
     icalobj.subcomponents[0]['SUMMARY'] = 'yet another summary'
     assert_equal(my_event.vobject_instance.vevent.summary.value,
                  'yet another summary')
     ## Now the data has been converted from string to vobject to string to icalendar to string to vobject and ... will the string still match the original?
     lines_now = my_event.data.split('\r\n')
     lines_orig = ev1.replace('Bastille Day Party',
                              'yet another summary').split('\n')
     lines_now.sort()
     lines_orig.sort()
     assert_equal(lines_now, lines_orig)
Ejemplo n.º 11
0
    def testCalendar(self):
        """
        Principal.calendar() and CalendarSet.calendar() should create
        Calendar objects without initiating any communication with the
        server.  Calendar.event() should create Event object without
        initiating any communication with the server.
        DAVClient.__init__ also doesn't do any communication
        Principal.__init__ as well, if the principal_url is given
        Principal.calendar_home_set needs to be set or the server will be queried
        """
        cal_url = "http://*****:*****@calendar.example:80/"
        client = DAVClient(url=cal_url)

        principal = Principal(client, cal_url + "me/")
        principal.calendar_home_set = cal_url + "me/calendars/"
        # calendar_home_set is actually a CalendarSet object
        assert (isinstance(principal.calendar_home_set, CalendarSet))
        calendar1 = principal.calendar(name="foo", cal_id="bar")
        calendar2 = principal.calendar_home_set.calendar(name="foo",
                                                         cal_id="bar")
        calendar3 = principal.calendar(cal_id="bar")
        assert_equal(calendar1.url, calendar2.url)
        assert_equal(calendar1.url, calendar3.url)
        assert_equal(calendar1.url,
                     "http://calendar.example:80/me/calendars/bar/")

        # principal.calendar_home_set can also be set to an object
        # This should be noop
        principal.calendar_home_set = principal.calendar_home_set
        calendar1 = principal.calendar(name="foo", cal_id="bar")
        assert_equal(calendar1.url, calendar2.url)

        # When building a calendar from a relative URL and a client,
        # the relative URL should be appended to the base URL in the client
        calendar1 = Calendar(client, 'someoneelse/calendars/main_calendar')
        calendar2 = Calendar(
            client,
            'http://*****:*****@calendar.example:80/someoneelse/calendars/main_calendar'
        )
        assert_equal(calendar1.url, calendar2.url)
Ejemplo n.º 12
0
    def testFailedQuery(self):
        """
        ref https://github.com/python-caldav/caldav/issues/54
        """
        cal_url = "http://*****:*****@calendar.example:80/"
        client = DAVClient(url=cal_url)
        calhome = CalendarSet(client, cal_url + "me/")

        ## syntesize a failed response
        class FailedResp:
            pass

        failedresp = FailedResp()
        failedresp.status = 400
        failedresp.reason = "you are wrong"
        failedresp.raw = "your request does not adhere to standards"

        ## synthesize a new http method
        calhome.client.unknown_method = lambda url, body, depth: failedresp

        ## call it.
        assert_raises(error.DAVError,
                      calhome._query,
                      query_method='unknown_method')
Ejemplo n.º 13
0
    def testProxy(self):
        if self.caldav.url.scheme == 'https':
            logging.info(
                "Skipping %s.testProxy as the TinyHTTPProxy implementation doesn't support https"
            )
            return

        server_address = ('127.0.0.1', 8080)
        proxy_httpd = NonThreadingHTTPServer(
            server_address, ProxyHandler, logging.getLogger("TinyHTTPProxy"))

        threadobj = threading.Thread(target=proxy_httpd.serve_forever)
        try:
            threadobj.start()
            assert (threadobj.is_alive())
            conn_params = self.conn_params.copy()
            conn_params['proxy'] = proxy
            c = DAVClient(**conn_params)
            p = c.principal()
            assert_not_equal(len(p.calendars()), 0)
        finally:
            proxy_httpd.shutdown()
            ## this should not be necessary, but I've observed some failures
            if threadobj.is_alive():
                time.sleep(0.05)
            assert (not threadobj.is_alive())

        threadobj = threading.Thread(target=proxy_httpd.serve_forever)
        try:
            threadobj.start()
            assert (threadobj.is_alive())
            conn_params = self.conn_params.copy()
            conn_params['proxy'] = proxy_noport
            c = DAVClient(**conn_params)
            p = c.principal()
            assert_not_equal(len(p.calendars()), 0)
            assert (threadobj.is_alive())
        finally:
            proxy_httpd.shutdown()
            ## this should not be necessary
            if threadobj.is_alive():
                time.sleep(0.05)
            assert (not threadobj.is_alive())
Ejemplo n.º 14
0
class RepeatedFunctionalTestsBaseClass(object):
    """
    This is a class with functional tests (tests that goes through
    basic functionality and actively communicates with third parties)
    that we want to repeat for all configured caldav_servers.
    
    (what a truely ugly name for this class - any better ideas?)

    NOTE: this tests relies heavily on the assumption that we can create 
    calendars on the remote caldav server, but the RFC says ...

       Support for MKCALENDAR on the server is only RECOMMENDED and not
       REQUIRED because some calendar stores only support one calendar per
       user (or principal), and those are typically pre-created for each
       account.

    However, iCloud is the only server where I have been denied creating a
    calendar.  Creating a calendar through the WebUI works, and creating an
    event through the library fails, so I don't think the problem is lack of 
    MKCALENDAR support.
    """
    def setup(self):
        logging.debug("############## test setup")
        self.conn_params = self.server_params.copy()
        for x in list(self.conn_params.keys()):
            if not x in ('url', 'proxy', 'username', 'password', 'ssl_verify_cert'):
                self.conn_params.pop(x)
        self.caldav = DAVClient(**self.conn_params)
        self.principal = self.caldav.principal()

        ## tear down old test calendars, in case teardown wasn't properly 
        ## executed last time tests were run
        self._teardown()

        logging.debug("############## test setup done")

    def teardown(self):
        logging.debug("############## test teardown")
        self._teardown()
        logging.debug("############## test teardown done")

    def _teardown(self):
        for combos in (('Yep', testcal_id), ('Yep', testcal_id2), ('Yølp', testcal_id), ('Yep', 'Yep'), ('Yølp', 'Yølp')):
            try:                        
                cal = self.principal.calendar(name="Yep", cal_id=testcal_id)
                cal.delete()
            except:
                pass
 

    def testPropfind(self):
        """
        Test of the propfind methods. (This is sort of redundant, since
        this is implicitly run by the setup)
        """
        ## first a raw xml propfind to the root URL
        foo = self.caldav.propfind(self.principal.url, props="""<?xml version="1.0" encoding="UTF-8"?>
<D:propfind xmlns:D="DAV:">
  <D:allprop/>
        </D:propfind>""")
        assert('resourcetype' in to_local(foo.raw))
        
        ## next, the internal _query_properties, returning an xml tree ...
        foo2 = self.principal._query_properties([dav.Status(),])
        assert('resourcetype' in to_local(foo.raw))
        ## TODO: more advanced asserts

    def testGetCalendarHomeSet(self):
        chs = self.principal.get_properties([cdav.CalendarHomeSet()])
        assert '{urn:ietf:params:xml:ns:caldav}calendar-home-set' in chs

    def testGetCalendars(self):
        assert_not_equal(len(self.principal.calendars()), 0)

    def testProxy(self):
        if self.caldav.url.scheme == 'https':
            logging.info("Skipping %s.testProxy as the TinyHTTPProxy implementation doesn't support https")
            return

        server_address = ('127.0.0.1', 8080)
        proxy_httpd = NonThreadingHTTPServer (server_address, ProxyHandler, logging.getLogger ("TinyHTTPProxy"))

        threadobj = threading.Thread(target=proxy_httpd.serve_forever)
        try:
            threadobj.start()
            assert(threadobj.is_alive())
            conn_params = self.conn_params.copy()
            conn_params['proxy'] = proxy
            c = DAVClient(**conn_params)
            p = c.principal()
            assert_not_equal(len(p.calendars()), 0)
        finally:
            proxy_httpd.shutdown()
            ## this should not be necessary, but I've observed some failures
            if threadobj.is_alive():
                time.sleep(0.05)
            assert(not threadobj.is_alive())

        threadobj = threading.Thread(target=proxy_httpd.serve_forever)
        try:
            threadobj.start()
            assert(threadobj.is_alive())
            conn_params = self.conn_params.copy()
            conn_params['proxy'] = proxy_noport
            c = DAVClient(**conn_params)
            p = c.principal()
            assert_not_equal(len(p.calendars()), 0)
            assert(threadobj.is_alive())
        finally:
            proxy_httpd.shutdown()
            ## this should not be necessary
            if threadobj.is_alive():
                time.sleep(0.05)
            assert(not threadobj.is_alive())

    def testPrincipal(self):
        collections = self.principal.calendars()
        if 'principal_url' in self.server_params:
            assert_equal(self.principal.url, self.server_params['principal_url'])
        for c in collections:
            assert_equal(c.__class__.__name__, "Calendar")

    def testCreateDeleteCalendar(self):
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)
        events = c.events()
        assert_equal(len(events), 0)
        events = self.principal.calendar(name="Yep", cal_id=testcal_id).events()
        ## huh ... we're quite constantly getting out a list with one item, the URL for
	## the caldav server.  This needs to be investigated, it is surely a bug in our
	## code.  Anyway, better to ignore it now than to have broken test code.
        assert_equal(len(events), 0)
        c.delete()
        
        ## verify that calendar does not exist - this breaks with zimbra :-(
        ## COMPATIBILITY PROBLEM - todo, look more into it
        if 'zimbra' not in str(c.url):
            assert_raises(error.NotFoundError, self.principal.calendar(name="Yep", cal_id=testcal_id).events)

    def testCreateCalendarAndEvent(self):
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)

        ## add event
        e1 = c.add_event(ev1)

        ## c.events() should give a full list of events
        events = c.events()
        assert_equal(len(events), 1)

        ## We should be able to access the calender through the URL
        c2 = Calendar(client=self.caldav, url=c.url)
        events2 = c.events()
        assert_equal(len(events2), 1)
        assert_equal(events2[0].url, events[0].url)

    def testCreateJournalListAndJournalEntry(self):
        """
        This test demonstrates the support for journals.
        * It will create a journal list
        * It will add some journal entries to it
        * It will list out all journal entries
        """
        if '/remote.php/caldav' in str(self.caldav.url) or self.caldav.url.path.startswith('/dav/'):
            ## COMPATIBILITY TODO: read the RFC.  sabredav/owncloud:
            ## got the error: "This calendar only supports VEVENT,
            ## VTODO. We found a VJOURNAL".  Should probably learn
            ## that some other way.  (why doesn't make_calendar break?
            ## what does the RFC say on that?)  Same with zimbra,
            ## though different error.
            return
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id, supported_calendar_component_set=['VJOURNAL'])
        j1 = c.add_journal(journal)
        journals = c.journals()
        assert_equal(len(journals), 1)
        todos = c.todos()
        events = c.events()
        assert_equal(todos + events, [])

    def testCreateTaskListAndTodo(self):
        """
        This test demonstrates the support for task lists.
        * It will create a "task list"
        * It will add some tasks to it
        * It will list out all pending tasks, sorted by due date
        * It will list out all pending tasks, sorted by priority
        * It will complete a task
        * It will delete a task
        """
        ## For all servers I've tested against except Zimbra, it's
        ## possible to create a calendar and add todo-items to it.
        ## Zimbra has separate calendars and task lists, and it's not
        ## allowed to put TODO-tasks into the calendar.  We need to
        ## tell Zimbra that the new "calendar" is a task list.  This
        ## is done though the supported_calendar_compontent_set
        ## property - hence the extra parameter here:
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id, supported_calendar_component_set=['VTODO'])

        ## add todo-item
        t1 = c.add_todo(todo)

        ## c.todos() should give a full list of todo items
        todos = c.todos()
        assert_equal(len(todos), 1)

        ## c.events() should NOT return todo-items
        events = c.events()
        assert_equal(len(events), 0)

        t2 = c.add_todo(todo2)
        t3 = c.add_todo(todo3)

        todos = c.todos()

        assert_equal(len(todos), 3)
        uids = lambda lst: [x.instance.vtodo.uid for x in lst]
        assert_equal(uids(todos), uids([t2, t3, t1]))

        todos = c.todos(sort_key='priority')
        pri = lambda lst: [x.instance.vtodo.priority.value for x in lst if hasattr(x.instance.vtodo, 'priority')]
        assert_equal(pri(todos), pri([t3, t2]))

        t3.complete()
        todos = c.todos()
        assert_equal(len(todos), 2)

        todos = c.todos(include_completed=True)
        assert_equal(len(todos), 3)

        t2.delete()

        todos = c.todos(include_completed=True)
        assert_equal(len(todos), 2)

    def testUtf8Event(self):
        c = self.principal.make_calendar(name="Yølp", cal_id=testcal_id)

        ## add event
        e1 = c.add_event(ev1.replace("Bastille Day Party", "Bringebærsyltetøyfestival"))

        events = c.events()
        todos = c.todos()

        assert_equal(len(todos), 0)

        ## COMPATIBILITY PROBLEM - todo, look more into it
        if not 'zimbra' in str(c.url):
            assert_equal(len(events), 1)

    def testUnicodeEvent(self):
        c = self.principal.make_calendar(name="Yølp", cal_id=testcal_id)

        ## add event
        e1 = c.add_event(to_str(ev1.replace("Bastille Day Party", "Bringebærsyltetøyfestival")))

        ## c.events() should give a full list of events
        events = c.events()

        ## COMPATIBILITY PROBLEM - todo, look more into it
        if not 'zimbra' in str(c.url):
            assert_equal(len(events), 1)

    def testSetCalendarProperties(self):
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)

        props = c.get_properties([dav.DisplayName(),])
        assert_equal("Yep", props[dav.DisplayName.tag])

        ## Creating a new calendar with different ID but with existing name - fails on zimbra only.
        ## This is OK to fail.
        if 'zimbra' in str(c.url):
            assert_raises(Exception, self.principal.make_calendar, "Yep", testcal_id2)

        c.set_properties([dav.DisplayName("hooray"),])
        props = c.get_properties([dav.DisplayName(),])
        assert_equal(props[dav.DisplayName.tag], "hooray")

        ## Creating a new calendar with different ID and old name - should never fail
        cc = self.principal.make_calendar(name="Yep", cal_id=testcal_id2).save()
        assert_not_equal(cc.url, None)
        cc.delete()

    def testLookupEvent(self):
        """
        Makes sure we can add events and look them up by URL and ID
        """
        ## Create calendar
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)

        ## add event
        e1 = c.add_event(ev1)
        assert_not_equal(e1.url, None)

        ## Verify that we can look it up, both by URL and by ID
        e2 = c.event_by_url(e1.url)
        e3 = c.event_by_uid("*****@*****.**")
        assert_equal(e2.instance.vevent.uid, e1.instance.vevent.uid)
        assert_equal(e3.instance.vevent.uid, e1.instance.vevent.uid)

        ## Knowing the URL of an event, we should be able to get to it
        ## without going through a calendar object
        e4 = Event(client=self.caldav, url=e1.url)
        e4.load()
        assert_equal(e4.instance.vevent.uid, e1.instance.vevent.uid)

    def testDeleteEvent(self):
        """
        Makes sure we can add events and delete them
        """
        ## Create calendar
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)

        ## add event
        e1 = c.add_event(ev1)
        assert_not_equal(e1.url, None)

        ## delete event
        e1.delete()

        ## Verify that we can't look it up, both by URL and by ID
        assert_raises(error.NotFoundError, c.event_by_url, e1.url)
        assert_raises(error.NotFoundError, c.event_by_uid, "*****@*****.**")

    def testDateSearchAndFreeBusy(self):
        """
        Verifies that date search works with a non-recurring event
        Also verifies that it's possible to change a date of a
        non-recurring event
        """
        ## Create calendar, add event ...
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)

        e = c.add_event(ev1)

        ## .. and search for it.
        r = c.date_search(datetime(2006,7,13,17,00,00),
                          datetime(2006,7,15,17,00,00))

        assert_equal(e.instance.vevent.uid, r[0].instance.vevent.uid)
        assert_equal(len(r), 1)

        ## ev2 is same UID, but one year ahead.
        ## The timestamp should change.
        e.data = ev2
        e.save()
        r = c.date_search(datetime(2006,7,13,17,00,00),
                          datetime(2006,7,15,17,00,00))
        assert_equal(len(r), 0)

        r = c.date_search(datetime(2007,7,13,17,00,00),
                          datetime(2007,7,15,17,00,00))
        assert_equal(len(r), 1)

        ## date search without closing date should also find it
        r = c.date_search(datetime(2007,7,13,17,00,00))
        assert_equal(len(r), 1)

        ## Lets try a freebusy request as well
        ## except for on my own DAViCal, it returns 500 Internal Server Error for me.  Should look more into that.  TODO.
        if 'calendar.bekkenstenveien53c.oslo' in str(self.caldav.url):
            return
        freebusy = c.freebusy_request(datetime(2007,7,13,17,00,00),
                                      datetime(2007,7,15,17,00,00))
        ## TODO: assert something more complex on the return object
        assert(isinstance(freebusy, FreeBusy))
        assert(freebusy.instance.vfreebusy)

    def testRecurringDateSearch(self):
        """
        This is more sanity testing of the server side than testing of the
        library per se.  How will it behave if we serve it a recurring
        event?
        """
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)

        ## evr is a yearly event starting at 1997-02-11
        e = c.add_event(evr)
        r = c.date_search(datetime(2008,11,1,17,00,00),
                          datetime(2008,11,3,17,00,00))
        assert_equal(len(r), 1)
        assert_equal(r[0].data.count("END:VEVENT"), 1)
        r = c.date_search(datetime(2008,11,1,17,00,00),
                          datetime(2009,11,3,17,00,00))
        assert_equal(len(r), 1)
        
        ## So much for standards ... seems like different servers
        ## behaves differently
        ## COMPATIBILITY PROBLEMS - look into it
        if "RRULE" in r[0].data and not "BEGIN:STANDARD" in r[0].data:
            assert_equal(r[0].data.count("END:VEVENT"), 1)
        else:
            assert_equal(r[0].data.count("END:VEVENT"), 2)

        ## The recurring events should not be expanded when using the
        ## events() method
        r = c.events()
        assert_equal(len(r), 1)

    def testBackwardCompatibility(self):
        """
        Tobias Brox has done some API changes - but this thing should
        still be backward compatible.
        """
        if not 'backwards_compatibility_url' in self.server_params:
            return
        caldav = DAVClient(self.server_params['backwards_compatibility_url'])
        principal = Principal(caldav, self.server_params['backwards_compatibility_url'])
        c = Calendar(caldav, name="Yep", parent = principal, id = testcal_id).save()
        assert_not_equal(c.url, None)

        c.set_properties([dav.DisplayName("hooray"),])
        props = c.get_properties([dav.DisplayName(),])
        assert_equal(props[dav.DisplayName.tag], "hooray")

        cc = Calendar(caldav, name="Yep", parent = principal).save()
        assert_not_equal(cc.url, None)
        cc.delete()

        e = Event(caldav, data = ev1, parent = c).save()
        assert_not_equal(e.url, None)

        ee = Event(caldav, url = url.make(e.url), parent = c)
        ee.load()
        assert_equal(e.instance.vevent.uid, ee.instance.vevent.uid)

        r = c.date_search(datetime(2006,7,13,17,00,00),
                          datetime(2006,7,15,17,00,00))
        assert_equal(e.instance.vevent.uid, r[0].instance.vevent.uid)
        assert_equal(len(r), 1)

        all = c.events()
        assert_equal(len(all), 1)

        e2 = Event(caldav, data = ev2, parent = c).save()
        assert_not_equal(e.url, None)

        tmp = c.event("*****@*****.**")
        assert_equal(e2.instance.vevent.uid, tmp.instance.vevent.uid)

        r = c.date_search(datetime(2007,7,13,17,00,00),
                          datetime(2007,7,15,17,00,00))
        assert_equal(len(r), 1)

        e.data = ev2
        e.save()

        r = c.date_search(datetime(2007,7,13,17,00,00),
                          datetime(2007,7,15,17,00,00))
        for e in r: print(e.data)
        assert_equal(len(r), 1)

        e.instance = e2.instance
        e.save()
        r = c.date_search(datetime(2007,7,13,17,00,00),
                          datetime(2007,7,15,17,00,00))
        for e in r: print(e.data)
        assert_equal(len(r), 1)
        

    def testObjects(self):
        ## TODO: description ... what are we trying to test for here?
        o = DAVObject(self.caldav)
        assert_raises(Exception, o.save)
Ejemplo n.º 15
0
    def testBackwardCompatibility(self):
        """
        Tobias Brox has done some API changes - but this thing should
        still be backward compatible.
        """
        if not 'backwards_compatibility_url' in self.server_params:
            return
        caldav = DAVClient(self.server_params['backwards_compatibility_url'])
        principal = Principal(
            caldav, self.server_params['backwards_compatibility_url'])
        c = Calendar(caldav, name="Yep", parent=principal,
                     id=testcal_id).save()
        assert_not_equal(c.url, None)

        c.set_properties([
            dav.DisplayName("hooray"),
        ])
        props = c.get_properties([
            dav.DisplayName(),
        ])
        assert_equal(props[dav.DisplayName.tag], "hooray")

        cc = Calendar(caldav, name="Yep", parent=principal).save()
        assert_not_equal(cc.url, None)
        cc.delete()

        e = Event(caldav, data=ev1, parent=c).save()
        assert_not_equal(e.url, None)

        ee = Event(caldav, url=url.make(e.url), parent=c)
        ee.load()
        assert_equal(e.instance.vevent.uid, ee.instance.vevent.uid)

        r = c.date_search(datetime(2006, 7, 13, 17, 00, 00),
                          datetime(2006, 7, 15, 17, 00, 00))
        assert_equal(e.instance.vevent.uid, r[0].instance.vevent.uid)
        assert_equal(len(r), 1)

        all = c.events()
        assert_equal(len(all), 1)

        e2 = Event(caldav, data=ev2, parent=c).save()
        assert_not_equal(e.url, None)

        tmp = c.event("*****@*****.**")
        assert_equal(e2.instance.vevent.uid, tmp.instance.vevent.uid)

        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00))
        assert_equal(len(r), 1)

        e.data = ev2
        e.save()

        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00))
        for e in r:
            print(e.data)
        assert_equal(len(r), 1)

        e.instance = e2.instance
        e.save()
        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00))
        for e in r:
            print(e.data)
        assert_equal(len(r), 1)
Ejemplo n.º 16
0
class RepeatedFunctionalTestsBaseClass(object):
    """
    This is a class with functional tests (tests that goes through
    basic functionality and actively communicates with third parties)
    that we want to repeat for all configured caldav_servers.
    
    (what a truely ugly name for this class - any better ideas?)

    NOTE: this tests relies heavily on the assumption that we can create 
    calendars on the remote caldav server, but the RFC says ...

       Support for MKCALENDAR on the server is only RECOMMENDED and not
       REQUIRED because some calendar stores only support one calendar per
       user (or principal), and those are typically pre-created for each
       account.

    However, iCloud is the only server where I have been denied creating a
    calendar.  Creating a calendar through the WebUI works, and creating an
    event through the library fails, so I don't think the problem is lack of 
    MKCALENDAR support.
    """
    def setup(self):
        logging.debug("############## test setup")
        self.conn_params = self.server_params.copy()
        for x in list(self.conn_params.keys()):
            if not x in ('url', 'proxy', 'username', 'password',
                         'ssl_verify_cert'):
                self.conn_params.pop(x)
        self.caldav = DAVClient(**self.conn_params)
        self.principal = self.caldav.principal()

        ## tear down old test calendars, in case teardown wasn't properly
        ## executed last time tests were run
        self._teardown()

        logging.debug("############## test setup done")

    def teardown(self):
        logging.debug("############## test teardown")
        self._teardown()
        logging.debug("############## test teardown done")

    def _teardown(self):
        for combos in (('Yep', testcal_id), ('Yep', testcal_id2),
                       ('Yølp', testcal_id), ('Yep', 'Yep'), ('Yølp', 'Yølp')):
            try:
                cal = self.principal.calendar(name="Yep", cal_id=testcal_id)
                cal.delete()
            except:
                pass

    def testPropfind(self):
        """
        Test of the propfind methods. (This is sort of redundant, since
        this is implicitly run by the setup)
        """
        ## first a raw xml propfind to the root URL
        foo = self.caldav.propfind(
            self.principal.url,
            props="""<?xml version="1.0" encoding="UTF-8"?>
<D:propfind xmlns:D="DAV:">
  <D:allprop/>
        </D:propfind>""")
        assert ('resourcetype' in to_local(foo.raw))

        ## next, the internal _query_properties, returning an xml tree ...
        foo2 = self.principal._query_properties([
            dav.Status(),
        ])
        assert ('resourcetype' in to_local(foo.raw))
        ## TODO: more advanced asserts

    def testGetCalendarHomeSet(self):
        chs = self.principal.get_properties([cdav.CalendarHomeSet()])
        assert '{urn:ietf:params:xml:ns:caldav}calendar-home-set' in chs

    def testGetCalendars(self):
        assert_not_equal(len(self.principal.calendars()), 0)

    def testProxy(self):
        if self.caldav.url.scheme == 'https':
            logging.info(
                "Skipping %s.testProxy as the TinyHTTPProxy implementation doesn't support https"
            )
            return

        server_address = ('127.0.0.1', 8080)
        proxy_httpd = NonThreadingHTTPServer(
            server_address, ProxyHandler, logging.getLogger("TinyHTTPProxy"))

        threadobj = threading.Thread(target=proxy_httpd.serve_forever)
        try:
            threadobj.start()
            assert (threadobj.is_alive())
            conn_params = self.conn_params.copy()
            conn_params['proxy'] = proxy
            c = DAVClient(**conn_params)
            p = c.principal()
            assert_not_equal(len(p.calendars()), 0)
        finally:
            proxy_httpd.shutdown()
            ## this should not be necessary, but I've observed some failures
            if threadobj.is_alive():
                time.sleep(0.05)
            assert (not threadobj.is_alive())

        threadobj = threading.Thread(target=proxy_httpd.serve_forever)
        try:
            threadobj.start()
            assert (threadobj.is_alive())
            conn_params = self.conn_params.copy()
            conn_params['proxy'] = proxy_noport
            c = DAVClient(**conn_params)
            p = c.principal()
            assert_not_equal(len(p.calendars()), 0)
            assert (threadobj.is_alive())
        finally:
            proxy_httpd.shutdown()
            ## this should not be necessary
            if threadobj.is_alive():
                time.sleep(0.05)
            assert (not threadobj.is_alive())

    def testPrincipal(self):
        collections = self.principal.calendars()
        if 'principal_url' in self.server_params:
            assert_equal(self.principal.url,
                         self.server_params['principal_url'])
        for c in collections:
            assert_equal(c.__class__.__name__, "Calendar")

    def testCreateDeleteCalendar(self):
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)
        events = c.events()
        assert_equal(len(events), 0)
        events = self.principal.calendar(name="Yep",
                                         cal_id=testcal_id).events()
        ## huh ... we're quite constantly getting out a list with one item, the URL for
        ## the caldav server.  This needs to be investigated, it is surely a bug in our
        ## code.  Anyway, better to ignore it now than to have broken test code.
        assert_equal(len(events), 0)
        c.delete()

        ## verify that calendar does not exist - this breaks with zimbra :-(
        ## COMPATIBILITY PROBLEM - todo, look more into it
        if 'zimbra' not in str(c.url):
            assert_raises(
                error.NotFoundError,
                self.principal.calendar(name="Yep", cal_id=testcal_id).events)

    def testCreateCalendarAndEvent(self):
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)

        ## add event
        e1 = c.add_event(ev1)

        ## c.events() should give a full list of events
        events = c.events()
        assert_equal(len(events), 1)

        ## We should be able to access the calender through the URL
        c2 = Calendar(client=self.caldav, url=c.url)
        events2 = c.events()
        assert_equal(len(events2), 1)
        assert_equal(events2[0].url, events[0].url)

    def testCreateJournalListAndJournalEntry(self):
        """
        This test demonstrates the support for journals.
        * It will create a journal list
        * It will add some journal entries to it
        * It will list out all journal entries
        """
        if '/remote.php/caldav' in str(
                self.caldav.url) or self.caldav.url.path.startswith('/dav/'):
            ## COMPATIBILITY TODO: read the RFC.  sabredav/owncloud:
            ## got the error: "This calendar only supports VEVENT,
            ## VTODO. We found a VJOURNAL".  Should probably learn
            ## that some other way.  (why doesn't make_calendar break?
            ## what does the RFC say on that?)  Same with zimbra,
            ## though different error.
            return
        c = self.principal.make_calendar(
            name="Yep",
            cal_id=testcal_id,
            supported_calendar_component_set=['VJOURNAL'])
        j1 = c.add_journal(journal)
        journals = c.journals()
        assert_equal(len(journals), 1)
        todos = c.todos()
        events = c.events()
        assert_equal(todos + events, [])

    def testCreateTaskListAndTodo(self):
        """
        This test demonstrates the support for task lists.
        * It will create a "task list"
        * It will add some tasks to it
        * It will list out all pending tasks, sorted by due date
        * It will list out all pending tasks, sorted by priority
        * It will complete a task
        * It will delete a task
        """
        ## For all servers I've tested against except Zimbra, it's
        ## possible to create a calendar and add todo-items to it.
        ## Zimbra has separate calendars and task lists, and it's not
        ## allowed to put TODO-tasks into the calendar.  We need to
        ## tell Zimbra that the new "calendar" is a task list.  This
        ## is done though the supported_calendar_compontent_set
        ## property - hence the extra parameter here:
        c = self.principal.make_calendar(
            name="Yep",
            cal_id=testcal_id,
            supported_calendar_component_set=['VTODO'])

        ## add todo-item
        t1 = c.add_todo(todo)

        ## c.todos() should give a full list of todo items
        todos = c.todos()
        assert_equal(len(todos), 1)

        ## c.events() should NOT return todo-items
        events = c.events()
        assert_equal(len(events), 0)

        t2 = c.add_todo(todo2)
        t3 = c.add_todo(todo3)

        todos = c.todos()

        assert_equal(len(todos), 3)
        uids = lambda lst: [x.instance.vtodo.uid for x in lst]
        assert_equal(uids(todos), uids([t2, t3, t1]))

        todos = c.todos(sort_key='priority')
        pri = lambda lst: [
            x.instance.vtodo.priority.value for x in lst
            if hasattr(x.instance.vtodo, 'priority')
        ]
        assert_equal(pri(todos), pri([t3, t2]))

        t3.complete()
        todos = c.todos()
        assert_equal(len(todos), 2)

        todos = c.todos(include_completed=True)
        assert_equal(len(todos), 3)

        t2.delete()

        todos = c.todos(include_completed=True)
        assert_equal(len(todos), 2)

    def testUtf8Event(self):
        c = self.principal.make_calendar(name="Yølp", cal_id=testcal_id)

        ## add event
        e1 = c.add_event(
            ev1.replace("Bastille Day Party", "Bringebærsyltetøyfestival"))

        events = c.events()
        todos = c.todos()

        assert_equal(len(todos), 0)

        ## COMPATIBILITY PROBLEM - todo, look more into it
        if not 'zimbra' in str(c.url):
            assert_equal(len(events), 1)

    def testUnicodeEvent(self):
        c = self.principal.make_calendar(name="Yølp", cal_id=testcal_id)

        ## add event
        e1 = c.add_event(
            to_str(
                ev1.replace("Bastille Day Party",
                            "Bringebærsyltetøyfestival")))

        ## c.events() should give a full list of events
        events = c.events()

        ## COMPATIBILITY PROBLEM - todo, look more into it
        if not 'zimbra' in str(c.url):
            assert_equal(len(events), 1)

    def testSetCalendarProperties(self):
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)

        props = c.get_properties([
            dav.DisplayName(),
        ])
        assert_equal("Yep", props[dav.DisplayName.tag])

        ## Creating a new calendar with different ID but with existing name - fails on zimbra only.
        ## This is OK to fail.
        if 'zimbra' in str(c.url):
            assert_raises(Exception, self.principal.make_calendar, "Yep",
                          testcal_id2)

        c.set_properties([
            dav.DisplayName("hooray"),
        ])
        props = c.get_properties([
            dav.DisplayName(),
        ])
        assert_equal(props[dav.DisplayName.tag], "hooray")

        ## Creating a new calendar with different ID and old name - should never fail
        cc = self.principal.make_calendar(name="Yep",
                                          cal_id=testcal_id2).save()
        assert_not_equal(cc.url, None)
        cc.delete()

    def testLookupEvent(self):
        """
        Makes sure we can add events and look them up by URL and ID
        """
        ## Create calendar
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)

        ## add event
        e1 = c.add_event(ev1)
        assert_not_equal(e1.url, None)

        ## Verify that we can look it up, both by URL and by ID
        e2 = c.event_by_url(e1.url)
        e3 = c.event_by_uid("*****@*****.**")
        assert_equal(e2.instance.vevent.uid, e1.instance.vevent.uid)
        assert_equal(e3.instance.vevent.uid, e1.instance.vevent.uid)

        ## Knowing the URL of an event, we should be able to get to it
        ## without going through a calendar object
        e4 = Event(client=self.caldav, url=e1.url)
        e4.load()
        assert_equal(e4.instance.vevent.uid, e1.instance.vevent.uid)

    def testDeleteEvent(self):
        """
        Makes sure we can add events and delete them
        """
        ## Create calendar
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)

        ## add event
        e1 = c.add_event(ev1)
        assert_not_equal(e1.url, None)

        ## delete event
        e1.delete()

        ## Verify that we can't look it up, both by URL and by ID
        assert_raises(error.NotFoundError, c.event_by_url, e1.url)
        assert_raises(error.NotFoundError, c.event_by_uid,
                      "*****@*****.**")

    def testDateSearchAndFreeBusy(self):
        """
        Verifies that date search works with a non-recurring event
        Also verifies that it's possible to change a date of a
        non-recurring event
        """
        ## Create calendar, add event ...
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)

        e = c.add_event(ev1)

        ## .. and search for it.
        r = c.date_search(datetime(2006, 7, 13, 17, 00, 00),
                          datetime(2006, 7, 15, 17, 00, 00))

        assert_equal(e.instance.vevent.uid, r[0].instance.vevent.uid)
        assert_equal(len(r), 1)

        ## ev2 is same UID, but one year ahead.
        ## The timestamp should change.
        e.data = ev2
        e.save()
        r = c.date_search(datetime(2006, 7, 13, 17, 00, 00),
                          datetime(2006, 7, 15, 17, 00, 00))
        assert_equal(len(r), 0)

        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00))
        assert_equal(len(r), 1)

        ## date search without closing date should also find it
        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00))
        assert_equal(len(r), 1)

        ## Lets try a freebusy request as well
        ## except for on my own DAViCal, it returns 500 Internal Server Error for me.  Should look more into that.  TODO.
        if 'calendar.bekkenstenveien53c.oslo' in str(self.caldav.url):
            return
        freebusy = c.freebusy_request(datetime(2007, 7, 13, 17, 00, 00),
                                      datetime(2007, 7, 15, 17, 00, 00))
        ## TODO: assert something more complex on the return object
        assert (isinstance(freebusy, FreeBusy))
        assert (freebusy.instance.vfreebusy)

    def testRecurringDateSearch(self):
        """
        This is more sanity testing of the server side than testing of the
        library per se.  How will it behave if we serve it a recurring
        event?
        """
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)

        ## evr is a yearly event starting at 1997-02-11
        e = c.add_event(evr)
        r = c.date_search(datetime(2008, 11, 1, 17, 00, 00),
                          datetime(2008, 11, 3, 17, 00, 00))
        assert_equal(len(r), 1)
        assert_equal(r[0].data.count("END:VEVENT"), 1)
        r = c.date_search(datetime(2008, 11, 1, 17, 00, 00),
                          datetime(2009, 11, 3, 17, 00, 00))
        assert_equal(len(r), 1)

        ## So much for standards ... seems like different servers
        ## behaves differently
        ## COMPATIBILITY PROBLEMS - look into it
        if "RRULE" in r[0].data and not "BEGIN:STANDARD" in r[0].data:
            assert_equal(r[0].data.count("END:VEVENT"), 1)
        else:
            assert_equal(r[0].data.count("END:VEVENT"), 2)

        ## The recurring events should not be expanded when using the
        ## events() method
        r = c.events()
        assert_equal(len(r), 1)

    def testBackwardCompatibility(self):
        """
        Tobias Brox has done some API changes - but this thing should
        still be backward compatible.
        """
        if not 'backwards_compatibility_url' in self.server_params:
            return
        caldav = DAVClient(self.server_params['backwards_compatibility_url'])
        principal = Principal(
            caldav, self.server_params['backwards_compatibility_url'])
        c = Calendar(caldav, name="Yep", parent=principal,
                     id=testcal_id).save()
        assert_not_equal(c.url, None)

        c.set_properties([
            dav.DisplayName("hooray"),
        ])
        props = c.get_properties([
            dav.DisplayName(),
        ])
        assert_equal(props[dav.DisplayName.tag], "hooray")

        cc = Calendar(caldav, name="Yep", parent=principal).save()
        assert_not_equal(cc.url, None)
        cc.delete()

        e = Event(caldav, data=ev1, parent=c).save()
        assert_not_equal(e.url, None)

        ee = Event(caldav, url=url.make(e.url), parent=c)
        ee.load()
        assert_equal(e.instance.vevent.uid, ee.instance.vevent.uid)

        r = c.date_search(datetime(2006, 7, 13, 17, 00, 00),
                          datetime(2006, 7, 15, 17, 00, 00))
        assert_equal(e.instance.vevent.uid, r[0].instance.vevent.uid)
        assert_equal(len(r), 1)

        all = c.events()
        assert_equal(len(all), 1)

        e2 = Event(caldav, data=ev2, parent=c).save()
        assert_not_equal(e.url, None)

        tmp = c.event("*****@*****.**")
        assert_equal(e2.instance.vevent.uid, tmp.instance.vevent.uid)

        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00))
        assert_equal(len(r), 1)

        e.data = ev2
        e.save()

        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00))
        for e in r:
            print(e.data)
        assert_equal(len(r), 1)

        e.instance = e2.instance
        e.save()
        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00))
        for e in r:
            print(e.data)
        assert_equal(len(r), 1)

    def testObjects(self):
        ## TODO: description ... what are we trying to test for here?
        o = DAVObject(self.caldav)
        assert_raises(Exception, o.save)
Ejemplo n.º 17
0
 def testProxy(self):
     c = DAVClient(principal_url, proxy)
     p = Principal(c, principal_url)
     assert_not_equal(len(p.calendars()), 0)
Ejemplo n.º 18
0
class RepeatedFunctionalTestsBaseClass(object):
    """This is a class with functional tests (tests that goes through
    basic functionality and actively communicates with third parties)
    that we want to repeat for all configured caldav_servers.

    (what a truely ugly name for this class - any better ideas?)

    NOTE: this tests relies heavily on the assumption that we can create
    calendars on the remote caldav server, but the RFC says ...

       Support for MKCALENDAR on the server is only RECOMMENDED and not
       REQUIRED because some calendar stores only support one calendar per
       user (or principal), and those are typically pre-created for each
       account.

    On iCloud I've been denied creating a calendar.  Creating a
    calendar through the WebUI works, and creating an event through
    the library fails, so I don't think the problem is lack of
    MKCALENDAR support.

    On Radicale, apparently there is some bug with MKCALENDAR, ref
    https://github.com/Kozea/Radicale/issues/330
    """
    def setup(self):
        logging.debug("############## test setup")

        if self.server_params.get('unique_calendar_ids', False):
            self.testcal_id = 'testcalendar-' + str(uuid.uuid4())
            self.testcal_id2 = 'testcalendar-' + str(uuid.uuid4())
        else:
            self.testcal_id = "pythoncaldav-test"
            self.testcal_id2 = "pythoncaldav-test2"

        self.conn_params = self.server_params.copy()
        for x in list(self.conn_params.keys()):
            if x not in ('url', 'proxy', 'username', 'password',
                         'ssl_verify_cert'):
                self.conn_params.pop(x)
        self.caldav = DAVClient(**self.conn_params)
        self.principal = self.caldav.principal()

        logging.debug("## going to tear down old test calendars, "
                      "in case teardown wasn't properly executed "
                      "last time tests were run")
        self._teardown()

        logging.debug("##############################")
        logging.debug("############## test setup done")
        logging.debug("##############################")

    def teardown(self):
        logging.debug("############################")
        logging.debug("############## test teardown")
        logging.debug("############################")
        self._teardown()
        logging.debug("############## test teardown done")

    def _teardown(self):
        for combos in (('Yep', self.testcal_id), ('Yep', self.testcal_id2),
                       ('Yølp', self.testcal_id), ('Yep', 'Yep'), ('Yølp',
                                                                   'Yølp')):
            try:
                cal = self.principal.calendar(name="Yep",
                                              cal_id=self.testcal_id)
                cal.delete()
            except:
                pass

    def testPropfind(self):
        """
        Test of the propfind methods. (This is sort of redundant, since
        this is implicitly run by the setup)
        """
        # ResourceType MUST be defined, and SHOULD be returned on a propfind
        # for "allprop" if I have the permission to see it.
        # So, no ResourceType returned seems like a bug in bedework
        if 'nopropfind' in self.server_params:
            raise SkipTest("Skipping propfind test, "
                           "re test suite configuration.  "
                           "Perhaps the caldav server is not adhering to "
                           "the standards")

        # first a raw xml propfind to the root URL
        foo = self.caldav.propfind(
            self.principal.url,
            props='<?xml version="1.0" encoding="UTF-8"?>'
            '<D:propfind xmlns:D="DAV:">'
            '  <D:allprop/>'
            '</D:propfind>')
        assert ('resourcetype' in to_local(foo.raw))

        # next, the internal _query_properties, returning an xml tree ...
        foo2 = self.principal._query_properties([
            dav.Status(),
        ])
        assert ('resourcetype' in to_local(foo.raw))
        # TODO: more advanced asserts

    def testGetCalendarHomeSet(self):
        chs = self.principal.get_properties([cdav.CalendarHomeSet()])
        assert '{urn:ietf:params:xml:ns:caldav}calendar-home-set' in chs

    def testGetDefaultCalendar(self):
        if 'nodefaultcalendar' in self.server_params:
            raise SkipTest(
                "Skipping GetDefaultCalendar, caldav server has no default calendar for the user?"
            )
        assert_not_equal(len(self.principal.calendars()), 0)

    def testGetCalendar(self):
        # Create calendar
        c = self.principal.make_calendar(name="Yep", cal_id=self.testcal_id)
        assert_not_equal(c.url, None)
        assert_not_equal(len(self.principal.calendars()), 0)

    def testProxy(self):
        if self.caldav.url.scheme == 'https' or 'noproxy' in self.server_params:
            raise SkipTest("Skipping %s.testProxy as the TinyHTTPProxy "
                           "implementation doesn't support https")

        server_address = ('127.0.0.1', 8080)
        proxy_httpd = NonThreadingHTTPServer(
            server_address, ProxyHandler, logging.getLogger("TinyHTTPProxy"))

        threadobj = threading.Thread(target=proxy_httpd.serve_forever)
        try:
            threadobj.start()
            assert (threadobj.is_alive())
            conn_params = self.conn_params.copy()
            conn_params['proxy'] = proxy
            c = DAVClient(**conn_params)
            p = c.principal()
            assert_not_equal(len(p.calendars()), 0)
        finally:
            proxy_httpd.shutdown()
            # this should not be necessary, but I've observed some failures
            if threadobj.is_alive():
                time.sleep(0.05)
            assert (not threadobj.is_alive())

        threadobj = threading.Thread(target=proxy_httpd.serve_forever)
        try:
            threadobj.start()
            assert (threadobj.is_alive())
            conn_params = self.conn_params.copy()
            conn_params['proxy'] = proxy_noport
            c = DAVClient(**conn_params)
            p = c.principal()
            assert_not_equal(len(p.calendars()), 0)
            assert (threadobj.is_alive())
        finally:
            proxy_httpd.shutdown()
            # this should not be necessary
            if threadobj.is_alive():
                time.sleep(0.05)
            assert (not threadobj.is_alive())

    def testPrincipal(self):
        collections = self.principal.calendars()
        if 'principal_url' in self.server_params:
            assert_equal(self.principal.url,
                         self.server_params['principal_url'])
        for c in collections:
            assert_equal(c.__class__.__name__, "Calendar")

    def testCreateDeleteCalendar(self):
        c = self.principal.make_calendar(name="Yep", cal_id=self.testcal_id)
        assert_not_equal(c.url, None)
        events = c.events()
        assert_equal(len(events), 0)
        events = self.principal.calendar(name="Yep",
                                         cal_id=self.testcal_id).events()
        # huh ... we're quite constantly getting out a list with one item,
        # the URL for the caldav server.  This needs to be investigated,
        # it is surely a bug in our code.
        # Anyway, better to ignore it now than to have broken test code.
        assert_equal(len(events), 0)
        c.delete()

        # verify that calendar does not exist - this breaks with zimbra :-(
        # (also breaks with radicale, which by default creates a new calendar)
        # COMPATIBILITY PROBLEM - todo, look more into it
        if 'nocalendarnotfound' not in self.server_params:
            assert_raises(
                error.NotFoundError,
                self.principal.calendar(name="Yep",
                                        cal_id=self.testcal_id).events)

    def testCreateCalendarAndEvent(self):
        c = self.principal.make_calendar(name="Yep", cal_id=self.testcal_id)

        # add event
        c.add_event(ev1)

        # c.events() should give a full list of events
        events = c.events()
        assert_equal(len(events), 1)

        # We should be able to access the calender through the URL
        c2 = Calendar(client=self.caldav, url=c.url)
        events2 = c2.events()
        assert_equal(len(events2), 1)
        assert_equal(events2[0].url, events[0].url)

    def testCreateCalendarAndEventFromVobject(self):
        c = self.principal.make_calendar(name="Yep", cal_id=self.testcal_id)

        # add event from vobject data
        ve1 = vobject.readOne(ev1)
        c.add_event(ve1)

        # c.events() should give a full list of events
        events = c.events()
        assert_equal(len(events), 1)

        # We should be able to access the calender through the URL
        c2 = Calendar(client=self.caldav, url=c.url)
        events2 = c2.events()
        assert_equal(len(events2), 1)
        assert_equal(events2[0].url, events[0].url)

    def testCreateJournalListAndJournalEntry(self):
        """
        This test demonstrates the support for journals.
        * It will create a journal list
        * It will add some journal entries to it
        * It will list out all journal entries
        """
        if 'nojournal' in self.server_params:
            # COMPATIBILITY TODO: read the RFC.  sabredav/owncloud:
            # got the error: "This calendar only supports VEVENT,
            # VTODO. We found a VJOURNAL".  Should probably learn
            # that some other way.  (why doesn't make_calendar break?
            # what does the RFC say on that?)  Same with zimbra,
            # though different error.
            raise SkipTest("Journal testing skipped due to test configuration")
        c = self.principal.make_calendar(
            name="Yep",
            cal_id=self.testcal_id,
            supported_calendar_component_set=['VJOURNAL'])
        j1 = c.add_journal(journal)
        journals = c.journals()
        assert_equal(len(journals), 1)
        todos = c.todos()
        events = c.events()
        assert_equal(todos + events, [])

    def testCreateTaskListAndTodo(self):
        """
        This test demonstrates the support for task lists.
        * It will create a "task list"
        * It will add a task to it
        * Verify the cal.todos() method
        * Verify that cal.events() method returns nothing
        """
        # bedeworks does not support VTODO
        if 'notodo' in self.server_params:
            raise SkipTest("VTODO testing skipped due to test configuration")

        # For all servers I've tested against except Zimbra, it's
        # possible to create a calendar and add todo-items to it.
        # Zimbra has separate calendars and task lists, and it's not
        # allowed to put TODO-tasks into the calendar.  We need to
        # tell Zimbra that the new "calendar" is a task list.  This
        # is done though the supported_calendar_compontent_set
        # property - hence the extra parameter here:
        logging.info("Creating calendar Yep for tasks")
        c = self.principal.make_calendar(
            name="Yep",
            cal_id=self.testcal_id,
            supported_calendar_component_set=['VTODO'])

        # add todo-item
        logging.info("Adding todo item to calendar Yep")
        t1 = c.add_todo(todo)

        # c.todos() should give a full list of todo items
        logging.info("Fetching the full list of todo items (should be one)")
        todos = c.todos()
        todos2 = c.todos(include_completed=True)
        assert_equal(len(todos), 1)
        assert_equal(len(todos2), 1)

        logging.info("Fetching the events (should be none)")
        # c.events() should NOT return todo-items
        events = c.events()
        assert_equal(len(events), 0)

    def testTodos(self):
        """
        This test will excercise the cal.todos() method,
        and in particular the sort_keys attribute.
        * It will list out all pending tasks, sorted by due date
        * It will list out all pending tasks, sorted by priority
        """
        # bedeworks does not support VTODO
        if 'notodo' in self.server_params:
            raise SkipTest("VTODO testing skipped due to test configuration")
        c = self.principal.make_calendar(
            name="Yep",
            cal_id=self.testcal_id,
            supported_calendar_component_set=['VTODO'])

        # add todo-item
        t1 = c.add_todo(todo)
        t2 = c.add_todo(todo2)
        t3 = c.add_todo(todo3)

        todos = c.todos()
        assert_equal(len(todos), 3)

        def uids(lst):
            return [x.instance.vtodo.uid for x in lst]

        assert_equal(uids(todos), uids([t2, t3, t1]))

        todos = c.todos(sort_keys=('priority', ))

        def pri(lst):
            return [
                x.instance.vtodo.priority.value for x in lst
                if hasattr(x.instance.vtodo, 'priority')
            ]

        assert_equal(pri(todos), pri([t3, t2]))

        todos = c.todos(sort_keys=(
            'summary',
            'priority',
        ))
        assert_equal(uids(todos), uids([t3, t2, t1]))

    def testTodoDatesearch(self):
        """
        Let's see how the date search method works for todo events
        """
        # bedeworks does not support VTODO
        if 'notodo' in self.server_params:
            raise SkipTest("VTODO testing skipped due to test configuration")
        c = self.principal.make_calendar(
            name="Yep",
            cal_id=self.testcal_id,
            supported_calendar_component_set=['VTODO'])

        # add todo-item
        t1 = c.add_todo(todo)
        t2 = c.add_todo(todo2)
        t3 = c.add_todo(todo3)
        t4 = c.add_todo(todo4)
        todos = c.todos()
        assert_equal(len(todos), 4)

        notodos = c.date_search(  # default compfilter is events
            start=datetime(1997, 4, 14),
            end=datetime(2015, 5, 14),
            expand=False)
        assert (not notodos)

        # Now, this is interesting.  2 events have dtstart set, 3 has
        # due set and 1 has neither due nor dtstart set.  None has
        # duration set.  What will a date search yield?
        todos = c.date_search(start=datetime(1997, 4, 14),
                              end=datetime(2015, 5, 14),
                              compfilter='VTODO',
                              expand=False)
        # The RFCs are pretty clear on this.  rfc5545 states:

        # A "VTODO" calendar component without the "DTSTART" and "DUE" (or
        # "DURATION") properties specifies a to-do that will be associated
        # with each successive calendar date, until it is completed.

        # and RFC4791, section 9.9 also says that events without
        # dtstart or due should be counted.  Since we have "expand"
        # set, it could even imply that we should get two VTODO items
        # our for each day in the time range!  In any case, less than
        # 4 todos returned is a breach of the RFCs.

        # TODO: prod the caldav server implementators about the RFC
        # breakages.

        # This is probably correct, and most server implementations
        # give this:
        # assert_equal(len(todos), 4)
        # ... but some caldav implementations yields 2 and 3:
        assert (len(todos) >= 2)

    def testTodoCompletion(self):
        """
        Will check that todo-items can be completed and deleted
        """
        # bedeworks does not support VTODO
        if 'notodo' in self.server_params:
            raise SkipTest("VTODO testing skipped due to test configuration")
        c = self.principal.make_calendar(
            name="Yep",
            cal_id=self.testcal_id,
            supported_calendar_component_set=['VTODO'])

        # add todo-items
        t1 = c.add_todo(todo)
        t2 = c.add_todo(todo2)
        t3 = c.add_todo(todo3)

        # There are now three todo-items at the calendar
        todos = c.todos()
        assert_equal(len(todos), 3)

        # Complete one of them
        t3.complete()

        # There are now two todo-items at the calendar
        todos = c.todos()
        assert_equal(len(todos), 2)

        # The historic todo-item can still be accessed
        todos = c.todos(include_completed=True)
        assert_equal(len(todos), 3)

        t2.delete()

        # ... the deleted one is gone ...
        todos = c.todos(include_completed=True)
        assert_equal(len(todos), 2)

        # date search should not include completed events ... hum.
        # TODO, fixme.
        # todos = c.date_search(
        #     start=datetime(1990, 4, 14), end=datetime(2015,5,14),
        #     compfilter='VTODO', hide_completed_todos=True)
        # assert_equal(len(todos), 1)

    def testUtf8Event(self):
        c = self.principal.make_calendar(name="Yølp", cal_id=self.testcal_id)

        # add event
        e1 = c.add_event(
            ev1.replace("Bastille Day Party", "Bringebærsyltetøyfestival"))

        events = c.events()
        todos = c.todos()

        assert_equal(len(todos), 0)

        # COMPATIBILITY PROBLEM - todo, look more into it
        if 'zimbra' not in str(c.url):
            assert_equal(len(events), 1)

    def testUnicodeEvent(self):
        c = self.principal.make_calendar(name="Yølp", cal_id=self.testcal_id)

        # add event
        e1 = c.add_event(
            to_str(
                ev1.replace("Bastille Day Party",
                            "Bringebærsyltetøyfestival")))

        # c.events() should give a full list of events
        events = c.events()

        # COMPATIBILITY PROBLEM - todo, look more into it
        if 'zimbra' not in str(c.url):
            assert_equal(len(events), 1)

    def testSetCalendarProperties(self):
        c = self.principal.make_calendar(name="Yep", cal_id=self.testcal_id)
        assert_not_equal(c.url, None)

        props = c.get_properties([
            dav.DisplayName(),
        ])
        assert_equal("Yep", props[dav.DisplayName.tag])

        # Creating a new calendar with different ID but with existing name
        # - fails on zimbra only.
        # This is OK to fail.
        if 'zimbra' in str(c.url):
            assert_raises(Exception, self.principal.make_calendar, "Yep",
                          self.testcal_id2)
        else:
            # This may fail, and if it fails, add an exception to the test
            # (see the "if" above)
            cc = self.principal.make_calendar("Yep", self.testcal_id2)
            cc.delete()

        c.set_properties([
            dav.DisplayName("hooray"),
        ])
        props = c.get_properties([
            dav.DisplayName(),
        ])
        assert_equal(props[dav.DisplayName.tag], "hooray")

        # Creating a new calendar with different ID and old name, this should
        # work, shouldn't it?
        # ... ouch, now it fails with a 409 on zimbra (it didn't fail
        # earlier)
        if not 'zimbra' in str(c.url):
            cc = self.principal.make_calendar(name="Yep",
                                              cal_id=self.testcal_id2).save()
            assert_not_equal(cc.url, None)
            cc.delete()

    def testLookupEvent(self):
        """
        Makes sure we can add events and look them up by URL and ID
        """
        # Create calendar
        c = self.principal.make_calendar(name="Yep", cal_id=self.testcal_id)
        assert_not_equal(c.url, None)

        # add event
        e1 = c.add_event(ev1)
        assert_not_equal(e1.url, None)

        # Verify that we can look it up, both by URL and by ID
        e2 = c.event_by_url(e1.url)
        e3 = c.event_by_uid("*****@*****.**")
        assert_equal(e2.instance.vevent.uid, e1.instance.vevent.uid)
        assert_equal(e3.instance.vevent.uid, e1.instance.vevent.uid)

        # Knowing the URL of an event, we should be able to get to it
        # without going through a calendar object
        e4 = Event(client=self.caldav, url=e1.url)
        e4.load()
        assert_equal(e4.instance.vevent.uid, e1.instance.vevent.uid)

        assert_raises(error.NotFoundError, c.event_by_uid, "0")
        c.add_event(evr)
        assert_raises(error.NotFoundError, c.event_by_uid, "0")

    def testDeleteEvent(self):
        """
        Makes sure we can add events and delete them
        """
        # Create calendar
        c = self.principal.make_calendar(name="Yep", cal_id=self.testcal_id)
        assert_not_equal(c.url, None)

        # add event
        e1 = c.add_event(ev1)
        assert_not_equal(e1.url, None)

        # delete event
        e1.delete()

        # Verify that we can't look it up, both by URL and by ID
        assert_raises(error.NotFoundError, c.event_by_url, e1.url)
        assert_raises(error.NotFoundError, c.event_by_uid,
                      "*****@*****.**")

    def testDateSearchAndFreeBusy(self):
        """
        Verifies that date search works with a non-recurring event
        Also verifies that it's possible to change a date of a
        non-recurring event
        """
        # Create calendar, add event ...
        c = self.principal.make_calendar(name="Yep", cal_id=self.testcal_id)
        assert_not_equal(c.url, None)

        e = c.add_event(ev1)

        # .. and search for it.
        r = c.date_search(datetime(2006, 7, 13, 17, 00, 00),
                          datetime(2006, 7, 15, 17, 00, 00),
                          expand=False)

        assert_equal(e.instance.vevent.uid, r[0].instance.vevent.uid)
        assert_equal(len(r), 1)

        # ev2 is same UID, but one year ahead.
        # The timestamp should change.
        e.data = ev2
        e.save()
        r = c.date_search(datetime(2006, 7, 13, 17, 00, 00),
                          datetime(2006, 7, 15, 17, 00, 00),
                          expand=False)
        assert_equal(len(r), 0)

        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00),
                          expand=False)
        assert_equal(len(r), 1)

        # date search without closing date should also find it
        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00), expand=False)
        assert_equal(len(r), 1)

        # Lets try a freebusy request as well
        if 'nofreebusy' in self.server_params:
            raise SkipTest("FreeBusy test skipped - not supported by server?")
        freebusy = c.freebusy_request(datetime(2007, 7, 13, 17, 00, 00),
                                      datetime(2007, 7, 15, 17, 00, 00))
        # TODO: assert something more complex on the return object
        assert (isinstance(freebusy, FreeBusy))
        assert (freebusy.instance.vfreebusy)

    def testRecurringDateSearch(self):
        """
        This is more sanity testing of the server side than testing of the
        library per se.  How will it behave if we serve it a recurring
        event?
        """
        if 'norecurring' in self.server_params:
            raise SkipTest("recurring date search test skipped due to "
                           "test configuration")
        c = self.principal.make_calendar(name="Yep", cal_id=self.testcal_id)

        # evr is a yearly event starting at 1997-02-11
        e = c.add_event(evr)
        r = c.date_search(datetime(2008, 11, 1, 17, 00, 00),
                          datetime(2008, 11, 3, 17, 00, 00),
                          expand=True)
        assert_equal(len(r), 1)
        assert_equal(r[0].data.count("END:VEVENT"), 1)
        r = c.date_search(datetime(2008, 11, 1, 17, 00, 00),
                          datetime(2009, 11, 3, 17, 00, 00),
                          expand=True)
        assert_equal(len(r), 1)

        # So much for standards ... seems like different servers
        # behaves differently
        # COMPATIBILITY PROBLEMS - look into it
        if "RRULE" in r[0].data and "BEGIN:STANDARD" not in r[0].data:
            assert_equal(r[0].data.count("END:VEVENT"), 1)
        else:
            assert_equal(r[0].data.count("END:VEVENT"), 2)

        # The recurring events should not be expanded when using the
        # events() method
        r = c.events()
        assert_equal(len(r), 1)

    def testBackwardCompatibility(self):
        """
        Tobias Brox has done some API changes - but this thing should
        still be backward compatible.
        """
        if 'backwards_compatibility_url' not in self.server_params:
            return
        caldav = DAVClient(self.server_params['backwards_compatibility_url'])
        principal = Principal(
            caldav, self.server_params['backwards_compatibility_url'])
        c = Calendar(caldav, name="Yep", parent=principal,
                     id=self.testcal_id).save()
        assert_not_equal(c.url, None)

        c.set_properties([
            dav.DisplayName("hooray"),
        ])
        props = c.get_properties([
            dav.DisplayName(),
        ])
        assert_equal(props[dav.DisplayName.tag], "hooray")

        cc = Calendar(caldav, name="Yep", parent=principal).save()
        assert_not_equal(cc.url, None)
        cc.delete()

        e = Event(caldav, data=ev1, parent=c).save()
        assert_not_equal(e.url, None)

        ee = Event(caldav, url=url.make(e.url), parent=c)
        ee.load()
        assert_equal(e.instance.vevent.uid, ee.instance.vevent.uid)

        r = c.date_search(datetime(2006, 7, 13, 17, 00, 00),
                          datetime(2006, 7, 15, 17, 00, 00),
                          expand=False)
        assert_equal(e.instance.vevent.uid, r[0].instance.vevent.uid)
        assert_equal(len(r), 1)

        all = c.events()
        assert_equal(len(all), 1)

        e2 = Event(caldav, data=ev2, parent=c).save()
        assert_not_equal(e.url, None)

        tmp = c.event("*****@*****.**")
        assert_equal(e2.instance.vevent.uid, tmp.instance.vevent.uid)

        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00),
                          expand=False)
        assert_equal(len(r), 1)

        e.data = ev2
        e.save()

        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00),
                          expand=False)
        # for e in r: print(e.data)
        assert_equal(len(r), 1)

        e.instance = e2.instance
        e.save()
        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00),
                          expand=False)
        # for e in r: print(e.data)
        assert_equal(len(r), 1)

    def testObjects(self):
        # TODO: description ... what are we trying to test for here?
        o = DAVObject(self.caldav)
        assert_raises(Exception, o.save)
Ejemplo n.º 19
0
class RepeatedFunctionalTestsBaseClass(object):
    """
    This is a class with functional tests (tests that goes through
    basic functionality and actively communicates with third parties)
    that we want to repeat for all configured caldav_servers.
    
    (what a truely ugly name for this class - any better ideas?)
    """
    def setup(self):
        logging.debug("############## test setup")
        self.conn_params = self.server_params.copy()
        for x in self.conn_params.keys():
            if not x in ('url', 'proxy', 'username', 'password'):
                self.conn_params.pop(x)
        self.caldav = DAVClient(**self.conn_params)
        self.principal = self.caldav.principal()

        ## tear down old test calendars, in case teardown wasn't properly
        ## executed last time tests were run
        self._teardown()

        logging.debug("############## test setup done")

    def teardown(self):
        logging.debug("############## test teardown")
        self._teardown()
        logging.debug("############## test teardown done")

    def _teardown(self):
        for combos in (('Yep', testcal_id), ('Yep', testcal_id2),
                       ('Yølp', testcal_id), ('Yep', 'Yep'), ('Yølp', 'Yølp')):
            try:
                cal = self.principal.calendar(name="Yep", cal_id=testcal_id)
                cal.delete()
            except:
                pass

    def testPropfind(self):
        """
        Test of the propfind methods. (This is sort of redundant, since
        this is implicitly run by the setup)
        """
        ## first a raw xml propfind to the root URL
        foo = self.caldav.propfind(
            self.principal.url,
            props="""<?xml version="1.0" encoding="UTF-8"?>
<D:propfind xmlns:D="DAV:">
  <D:allprop/>
        </D:propfind>""")
        assert ('resourcetype' in foo.raw)

        ## next, the internal _query_properties, returning an xml tree ...
        foo2 = self.principal._query_properties([
            dav.Status(),
        ])
        assert ('resourcetype' in foo.raw)
        ## TODO: more advanced asserts

    def testGetCalendarHomeSet(self):
        chs = self.principal.get_properties([cdav.CalendarHomeSet()])
        assert '{urn:ietf:params:xml:ns:caldav}calendar-home-set' in chs

    def testGetCalendars(self):
        assert_not_equal(len(self.principal.calendars()), 0)

    def testProxy(self):
        if self.caldav.url.scheme == 'https':
            logging.info(
                "Skipping %s.testProxy as the TinyHTTPProxy implementation doesn't support https"
            )
            return

        server_address = ('127.0.0.1', 8080)
        proxy_httpd = NonThreadingHTTPServer(
            server_address, ProxyHandler, logging.getLogger("TinyHTTPProxy"))

        threadobj = threading.Thread(target=proxy_httpd.serve_forever)
        try:
            threadobj.start()
            assert (threadobj.is_alive())
            conn_params = self.conn_params.copy()
            conn_params['proxy'] = proxy
            c = DAVClient(**conn_params)
            p = c.principal()
            assert_not_equal(len(p.calendars()), 0)
        finally:
            proxy_httpd.shutdown()
            ## this should not be necessary, but I've observed some failures
            if threadobj.is_alive():
                time.sleep(0.05)
            assert (not threadobj.is_alive())

        threadobj = threading.Thread(target=proxy_httpd.serve_forever)
        try:
            threadobj.start()
            assert (threadobj.is_alive())
            conn_params = self.conn_params.copy()
            conn_params['proxy'] = proxy_noport
            c = DAVClient(**conn_params)
            p = c.principal()
            assert_not_equal(len(p.calendars()), 0)
            assert (threadobj.is_alive())
        finally:
            proxy_httpd.shutdown()
            ## this should not be necessary
            if threadobj.is_alive():
                time.sleep(0.05)
            assert (not threadobj.is_alive())

    def testPrincipal(self):
        collections = self.principal.calendars()
        if 'principal_url' in self.server_params:
            assert_equal(self.principal.url,
                         self.server_params['principal_url'])
        for c in collections:
            assert_equal(c.__class__.__name__, "Calendar")

    def testCreateDeleteCalendar(self):
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)
        events = c.events()
        assert_equal(len(events), 0)
        events = self.principal.calendar(name="Yep",
                                         cal_id=testcal_id).events()
        assert_equal(len(events), 0)
        c.delete()

        ## verify that calendar does not exist - this breaks with zimbra :-(
        ## COMPATIBILITY PROBLEM - todo, look more into it
        if 'zimbra' not in str(c.url):
            assert_raises(
                error.NotFoundError,
                self.principal.calendar(name="Yep", cal_id=testcal_id).events)

    def testCreateCalendarAndEvent(self):
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)

        ## add event
        e1 = c.add_event(ev1)

        ## c.events() should give a full list of events
        events = c.events()
        assert_equal(len(events), 1)

        ## We should be able to access the calender through the URL
        c2 = Calendar(client=self.caldav, url=c.url)
        events2 = c.events()
        assert_equal(len(events2), 1)
        assert_equal(events2[0].url, events[0].url)

    def testUtf8Event(self):
        c = self.principal.make_calendar(name="Yølp", cal_id=testcal_id)

        ## add event
        e1 = c.add_event(
            ev1.replace("Bastille Day Party", "Bringebærsyltetøyfestival"))

        events = c.events()

        ## COMPATIBILITY PROBLEM - todo, look more into it
        if not 'zimbra' in str(c.url):
            assert_equal(len(events), 1)

    def testUnicodeEvent(self):
        c = self.principal.make_calendar(name=u"Yølp", cal_id=testcal_id)

        ## add event
        e1 = c.add_event(
            unicode(
                ev1.replace("Bastille Day Party", "Bringebærsyltetøyfestival"),
                'utf-8'))

        ## c.events() should give a full list of events
        events = c.events()

        ## COMPATIBILITY PROBLEM - todo, look more into it
        if not 'zimbra' in str(c.url):
            assert_equal(len(events), 1)

    def testSetCalendarProperties(self):
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)

        props = c.get_properties([
            dav.DisplayName(),
        ])
        assert_equal("Yep", props[dav.DisplayName.tag])

        ## Creating a new calendar with different ID but with existing name - fails on zimbra only.
        ## This is OK to fail.
        if 'zimbra' in str(c.url):
            assert_raises(Exception, self.principal.make_calendar, "Yep",
                          testcal_id2)

        c.set_properties([
            dav.DisplayName("hooray"),
        ])
        props = c.get_properties([
            dav.DisplayName(),
        ])
        assert_equal(props[dav.DisplayName.tag], "hooray")

        ## Creating a new calendar with different ID and old name - should never fail
        cc = self.principal.make_calendar(name="Yep",
                                          cal_id=testcal_id2).save()
        assert_not_equal(cc.url, None)
        cc.delete()

    def testLookupEvent(self):
        """
        Makes sure we can add events and look them up by URL and ID
        """
        ## Create calendar
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)

        ## add event
        e1 = c.add_event(ev1)
        assert_not_equal(e1.url, None)

        ## Verify that we can look it up, both by URL and by ID
        e2 = c.event_by_url(e1.url)
        e3 = c.event_by_uid("*****@*****.**")
        assert_equal(e2.instance.vevent.uid, e1.instance.vevent.uid)
        assert_equal(e3.instance.vevent.uid, e1.instance.vevent.uid)

        ## Knowing the URL of an event, we should be able to get to it
        ## without going through a calendar object
        e4 = Event(client=self.caldav, url=e1.url)
        e4.load()
        assert_equal(e4.instance.vevent.uid, e1.instance.vevent.uid)

    def testDateSearch(self):
        """
        Verifies that date search works with a non-recurring event
        Also verifies that it's possible to change a date of a
        non-recurring event
        """
        ## Create calendar, add event ...
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)

        e = c.add_event(ev1)

        ## .. and search for it.
        r = c.date_search(datetime(2006, 7, 13, 17, 00, 00),
                          datetime(2006, 7, 15, 17, 00, 00))

        assert_equal(e.instance.vevent.uid, r[0].instance.vevent.uid)
        assert_equal(len(r), 1)

        ## ev2 is same UID, but one year ahead.
        ## The timestamp should change.
        e.data = ev2
        e.save()
        r = c.date_search(datetime(2006, 7, 13, 17, 00, 00),
                          datetime(2006, 7, 15, 17, 00, 00))
        assert_equal(len(r), 0)

        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00))
        assert_equal(len(r), 1)

        ## date search without closing date should also find it
        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00))
        ## ... but alas, some servers don't support it
        ## COMPATIBILITY PROBLEM - todo, look more into it
        if not 'baikal' in str(c.url) and not 'owncloud' in str(c.url):
            assert_equal(len(r), 1)

    def testRecurringDateSearch(self):
        """
        This is more sanity testing of the server side than testing of the
        library per se.  How will it behave if we serve it a recurring
        event?
        """
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)

        ## evr is a yearly event starting at 1997-02-11
        e = c.add_event(evr)
        r = c.date_search(datetime(2008, 11, 1, 17, 00, 00),
                          datetime(2008, 11, 3, 17, 00, 00))
        assert_equal(len(r), 1)
        assert_equal(r[0].data.count("END:VEVENT"), 1)
        r = c.date_search(datetime(2008, 11, 1, 17, 00, 00),
                          datetime(2009, 11, 3, 17, 00, 00))
        assert_equal(len(r), 1)

        ## So much for standards ... seems like different servers
        ## behaves differently
        ## COMPATIBILITY PROBLEMS - look into it
        if "RRULE" in r[0].data and not "BEGIN:STANDARD" in r[0].data:
            assert_equal(r[0].data.count("END:VEVENT"), 1)
        else:
            assert_equal(r[0].data.count("END:VEVENT"), 2)

        ## The recurring events should not be expanded when using the
        ## events() method
        r = c.events()
        assert_equal(len(r), 1)

    def testBackwardCompatibility(self):
        """
        Tobias Brox has done some API changes - but this thing should
        still be backward compatible.
        """
        if not 'backwards_compatibility_url' in self.server_params:
            return
        caldav = DAVClient(self.server_params['backwards_compatibility_url'])
        principal = Principal(
            caldav, self.server_params['backwards_compatibility_url'])
        c = Calendar(caldav, name="Yep", parent=principal,
                     id=testcal_id).save()
        assert_not_equal(c.url, None)

        c.set_properties([
            dav.DisplayName("hooray"),
        ])
        props = c.get_properties([
            dav.DisplayName(),
        ])
        assert_equal(props[dav.DisplayName.tag], "hooray")

        cc = Calendar(caldav, name="Yep", parent=principal).save()
        assert_not_equal(cc.url, None)
        cc.delete()

        e = Event(caldav, data=ev1, parent=c).save()
        assert_not_equal(e.url, None)

        ee = Event(caldav, url=url.make(e.url), parent=c)
        ee.load()
        assert_equal(e.instance.vevent.uid, ee.instance.vevent.uid)

        r = c.date_search(datetime(2006, 7, 13, 17, 00, 00),
                          datetime(2006, 7, 15, 17, 00, 00))
        assert_equal(e.instance.vevent.uid, r[0].instance.vevent.uid)
        assert_equal(len(r), 1)

        all = c.events()
        assert_equal(len(all), 1)

        e2 = Event(caldav, data=ev2, parent=c).save()
        assert_not_equal(e.url, None)

        tmp = c.event("*****@*****.**")
        assert_equal(e2.instance.vevent.uid, tmp.instance.vevent.uid)

        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00))
        assert_equal(len(r), 1)

        e.data = ev2
        e.save()

        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00))
        for e in r:
            print e.data
        assert_equal(len(r), 1)

        e.instance = e2.instance
        e.save()
        r = c.date_search(datetime(2007, 7, 13, 17, 00, 00),
                          datetime(2007, 7, 15, 17, 00, 00))
        for e in r:
            print e.data
        assert_equal(len(r), 1)

    def testObjects(self):
        ## TODO: description ... what are we trying to test for here?
        o = DAVObject(self.caldav)
        assert_raises(Exception, o.save)
Ejemplo n.º 20
0
 def setup(self):
     self.caldav = DAVClient(principal_url)
     self.principal = Principal(self.caldav, principal_url)
Ejemplo n.º 21
0
class RepeatedFunctionalTestsBaseClass(object):
    """
    This is a class with functional tests (tests that goes through
    basic functionality and actively communicates with third parties)
    that we want to repeat for all configured caldav_servers.
    
    (what a truely ugly name for this class - any better ideas?)
    """
    def setup(self):
        logging.debug("############## test setup")
        self.conn_params = self.server_params.copy()
        for x in self.conn_params.keys():
            if not x in ('url', 'proxy', 'username', 'password'):
                self.conn_params.pop(x)
        self.caldav = DAVClient(**self.conn_params)
        self.principal = self.caldav.principal()

        ## tear down old test calendars, in case teardown wasn't properly 
        ## executed last time tests were run
        self._teardown()

        logging.debug("############## test setup done")

    def teardown(self):
        logging.debug("############## test teardown")
        self._teardown()
        logging.debug("############## test teardown done")

    def _teardown(self):
        for combos in (('Yep', testcal_id), ('Yep', testcal_id2), ('Yølp', testcal_id), ('Yep', 'Yep'), ('Yølp', 'Yølp')):
            try:                        
                cal = self.principal.calendar(name="Yep", cal_id=testcal_id)
                cal.delete()
            except:
                pass
 

    def testPropfind(self):
        """
        Test of the propfind methods. (This is sort of redundant, since
        this is implicitly run by the setup)
        """
        ## first a raw xml propfind to the root URL
        foo = self.caldav.propfind(self.principal.url, props="""<?xml version="1.0" encoding="UTF-8"?>
<D:propfind xmlns:D="DAV:">
  <D:allprop/>
        </D:propfind>""")
        assert('resourcetype' in foo.raw)
        
        ## next, the internal _query_properties, returning an xml tree ...
        foo2 = self.principal._query_properties([dav.Status(),])
        assert('resourcetype' in foo.raw)
        ## TODO: more advanced asserts

    def testGetCalendarHomeSet(self):
        chs = self.principal.get_properties([cdav.CalendarHomeSet()])
        assert '{urn:ietf:params:xml:ns:caldav}calendar-home-set' in chs

    def testGetCalendars(self):
        assert_not_equal(len(self.principal.calendars()), 0)

    def testProxy(self):
        if self.caldav.url.scheme == 'https':
            logging.info("Skipping %s.testProxy as the TinyHTTPProxy implementation doesn't support https")
            return

        server_address = ('127.0.0.1', 8080)
        proxy_httpd = NonThreadingHTTPServer (server_address, ProxyHandler, logging.getLogger ("TinyHTTPProxy"))

        threadobj = threading.Thread(target=proxy_httpd.serve_forever)
        try:
            threadobj.start()
            assert(threadobj.is_alive())
            conn_params = self.conn_params.copy()
            conn_params['proxy'] = proxy
            c = DAVClient(**conn_params)
            p = c.principal()
            assert_not_equal(len(p.calendars()), 0)
        finally:
            proxy_httpd.shutdown()
            ## this should not be necessary, but I've observed some failures
            if threadobj.is_alive():
                time.sleep(0.05)
            assert(not threadobj.is_alive())

        threadobj = threading.Thread(target=proxy_httpd.serve_forever)
        try:
            threadobj.start()
            assert(threadobj.is_alive())
            conn_params = self.conn_params.copy()
            conn_params['proxy'] = proxy_noport
            c = DAVClient(**conn_params)
            p = c.principal()
            assert_not_equal(len(p.calendars()), 0)
            assert(threadobj.is_alive())
        finally:
            proxy_httpd.shutdown()
            ## this should not be necessary
            if threadobj.is_alive():
                time.sleep(0.05)
            assert(not threadobj.is_alive())

    def testPrincipal(self):
        collections = self.principal.calendars()
        if 'principal_url' in self.server_params:
            assert_equal(self.principal.url, self.server_params['principal_url'])
        for c in collections:
            assert_equal(c.__class__.__name__, "Calendar")

    def testCreateDeleteCalendar(self):
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)
        events = c.events()
        assert_equal(len(events), 0)
        events = self.principal.calendar(name="Yep", cal_id=testcal_id).events()
        assert_equal(len(events), 0)
        c.delete()
        
        ## verify that calendar does not exist - this breaks with zimbra :-(
        ## COMPATIBILITY PROBLEM - todo, look more into it
        if 'zimbra' not in str(c.url):
            assert_raises(error.NotFoundError, self.principal.calendar(name="Yep", cal_id=testcal_id).events)

    def testCreateCalendarAndEvent(self):
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)

        ## add event
        e1 = c.add_event(ev1)

        ## c.events() should give a full list of events
        events = c.events()
        assert_equal(len(events), 1)

        ## We should be able to access the calender through the URL
        c2 = Calendar(client=self.caldav, url=c.url)
        events2 = c.events()
        assert_equal(len(events2), 1)
        assert_equal(events2[0].url, events[0].url)
        
    def testUtf8Event(self):
        c = self.principal.make_calendar(name="Yølp", cal_id=testcal_id)

        ## add event
        e1 = c.add_event(ev1.replace("Bastille Day Party", "Bringebærsyltetøyfestival"))

        events = c.events()

        ## COMPATIBILITY PROBLEM - todo, look more into it
        if not 'zimbra' in str(c.url):
            assert_equal(len(events), 1)

    def testUnicodeEvent(self):
        c = self.principal.make_calendar(name=u"Yølp", cal_id=testcal_id)

        ## add event
        e1 = c.add_event(unicode(ev1.replace("Bastille Day Party", "Bringebærsyltetøyfestival"), 'utf-8'))

        ## c.events() should give a full list of events
        events = c.events()

        ## COMPATIBILITY PROBLEM - todo, look more into it
        if not 'zimbra' in str(c.url):
            assert_equal(len(events), 1)

    def testSetCalendarProperties(self):
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)

        props = c.get_properties([dav.DisplayName(),])
        assert_equal("Yep", props[dav.DisplayName.tag])

        ## Creating a new calendar with different ID but with existing name - fails on zimbra only.
        ## This is OK to fail.
        if 'zimbra' in str(c.url):
            assert_raises(Exception, self.principal.make_calendar, "Yep", testcal_id2)

        c.set_properties([dav.DisplayName("hooray"),])
        props = c.get_properties([dav.DisplayName(),])
        assert_equal(props[dav.DisplayName.tag], "hooray")

        ## Creating a new calendar with different ID and old name - should never fail
        cc = self.principal.make_calendar(name="Yep", cal_id=testcal_id2).save()
        assert_not_equal(cc.url, None)
        cc.delete()

    def testLookupEvent(self):
        """
        Makes sure we can add events and look them up by URL and ID
        """
        ## Create calendar
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)

        ## add event
        e1 = c.add_event(ev1)
        assert_not_equal(e1.url, None)

        ## Verify that we can look it up, both by URL and by ID
        e2 = c.event_by_url(e1.url)
        e3 = c.event_by_uid("*****@*****.**")
        assert_equal(e2.instance.vevent.uid, e1.instance.vevent.uid)
        assert_equal(e3.instance.vevent.uid, e1.instance.vevent.uid)

        ## Knowing the URL of an event, we should be able to get to it
        ## without going through a calendar object
        e4 = Event(client=self.caldav, url=e1.url)
        e4.load()
        assert_equal(e4.instance.vevent.uid, e1.instance.vevent.uid)

    def testDateSearch(self):
        """
        Verifies that date search works with a non-recurring event
        Also verifies that it's possible to change a date of a
        non-recurring event
        """
        ## Create calendar, add event ...
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)
        assert_not_equal(c.url, None)

        e = c.add_event(ev1)

        ## .. and search for it.
        r = c.date_search(datetime(2006,7,13,17,00,00),
                          datetime(2006,7,15,17,00,00))

        assert_equal(e.instance.vevent.uid, r[0].instance.vevent.uid)
        assert_equal(len(r), 1)

        ## ev2 is same UID, but one year ahead.
        ## The timestamp should change.
        e.data = ev2
        e.save()
        r = c.date_search(datetime(2006,7,13,17,00,00),
                          datetime(2006,7,15,17,00,00))
        assert_equal(len(r), 0)

        r = c.date_search(datetime(2007,7,13,17,00,00),
                          datetime(2007,7,15,17,00,00))
        assert_equal(len(r), 1)

        ## date search without closing date should also find it
        r = c.date_search(datetime(2007,7,13,17,00,00))
        ## ... but alas, some servers don't support it
        ## COMPATIBILITY PROBLEM - todo, look more into it
        if not 'baikal' in str(c.url) and not 'owncloud' in str(c.url):
            assert_equal(len(r), 1)

    def testRecurringDateSearch(self):
        """
        This is more sanity testing of the server side than testing of the
        library per se.  How will it behave if we serve it a recurring
        event?
        """
        c = self.principal.make_calendar(name="Yep", cal_id=testcal_id)

        ## evr is a yearly event starting at 1997-02-11
        e = c.add_event(evr)
        r = c.date_search(datetime(2008,11,1,17,00,00),
                          datetime(2008,11,3,17,00,00))
        assert_equal(len(r), 1)
        assert_equal(r[0].data.count("END:VEVENT"), 1)
        r = c.date_search(datetime(2008,11,1,17,00,00),
                          datetime(2009,11,3,17,00,00))
        assert_equal(len(r), 1)
        
        ## So much for standards ... seems like different servers
        ## behaves differently
        ## COMPATIBILITY PROBLEMS - look into it
        if "RRULE" in r[0].data and not "BEGIN:STANDARD" in r[0].data:
            assert_equal(r[0].data.count("END:VEVENT"), 1)
        else:
            assert_equal(r[0].data.count("END:VEVENT"), 2)

        ## The recurring events should not be expanded when using the
        ## events() method
        r = c.events()
        assert_equal(len(r), 1)

    def testBackwardCompatibility(self):
        """
        Tobias Brox has done some API changes - but this thing should
        still be backward compatible.
        """
        if not 'backwards_compatibility_url' in self.server_params:
            return
        caldav = DAVClient(self.server_params['backwards_compatibility_url'])
        principal = Principal(caldav, self.server_params['backwards_compatibility_url'])
        c = Calendar(caldav, name="Yep", parent = principal, id = testcal_id).save()
        assert_not_equal(c.url, None)

        c.set_properties([dav.DisplayName("hooray"),])
        props = c.get_properties([dav.DisplayName(),])
        assert_equal(props[dav.DisplayName.tag], "hooray")

        cc = Calendar(caldav, name="Yep", parent = principal).save()
        assert_not_equal(cc.url, None)
        cc.delete()

        e = Event(caldav, data = ev1, parent = c).save()
        assert_not_equal(e.url, None)

        ee = Event(caldav, url = url.make(e.url), parent = c)
        ee.load()
        assert_equal(e.instance.vevent.uid, ee.instance.vevent.uid)

        r = c.date_search(datetime(2006,7,13,17,00,00),
                          datetime(2006,7,15,17,00,00))
        assert_equal(e.instance.vevent.uid, r[0].instance.vevent.uid)
        assert_equal(len(r), 1)

        all = c.events()
        assert_equal(len(all), 1)

        e2 = Event(caldav, data = ev2, parent = c).save()
        assert_not_equal(e.url, None)

        tmp = c.event("*****@*****.**")
        assert_equal(e2.instance.vevent.uid, tmp.instance.vevent.uid)

        r = c.date_search(datetime(2007,7,13,17,00,00),
                          datetime(2007,7,15,17,00,00))
        assert_equal(len(r), 1)

        e.data = ev2
        e.save()

        r = c.date_search(datetime(2007,7,13,17,00,00),
                          datetime(2007,7,15,17,00,00))
        for e in r: print e.data
        assert_equal(len(r), 1)

        e.instance = e2.instance
        e.save()
        r = c.date_search(datetime(2007,7,13,17,00,00),
                          datetime(2007,7,15,17,00,00))
        for e in r: print e.data
        assert_equal(len(r), 1)
        

    def testObjects(self):
        ## TODO: description ... what are we trying to test for here?
        o = DAVObject(self.caldav)
        assert_raises(Exception, o.save)