[CalendarServer-changes] [10696] CalendarServer/trunk/txdav

source_changes at macosforge.org source_changes at macosforge.org
Tue Feb 12 07:46:34 PST 2013


Revision: 10696
          http://trac.calendarserver.org//changeset/10696
Author:   cdaboo at apple.com
Date:     2013-02-12 07:46:33 -0800 (Tue, 12 Feb 2013)
Log Message:
-----------
Fix tests to account for dropbox disable after managed attachment migration. Also add a bunch of tests for the new
managed attachment apis.

Modified Paths:
--------------
    CalendarServer/trunk/txdav/caldav/datastore/sql.py
    CalendarServer/trunk/txdav/caldav/datastore/test/common.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
    CalendarServer/trunk/txdav/common/datastore/test/util.py
    CalendarServer/trunk/txdav/common/datastore/upgrade/test/test_migrate.py

Modified: CalendarServer/trunk/txdav/caldav/datastore/sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/sql.py	2013-02-12 01:07:09 UTC (rev 10695)
+++ CalendarServer/trunk/txdav/caldav/datastore/sql.py	2013-02-12 15:46:33 UTC (rev 10696)
@@ -2171,6 +2171,8 @@
 
 
     def store(self, contentType, dispositionName=None):
+        if not self._name:
+            self._name = dispositionName
         return AttachmentStorageTransport(self, contentType, dispositionName, self._justCreated)
 
 

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/common.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/common.py	2013-02-12 01:07:09 UTC (rev 10695)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/common.py	2013-02-12 15:46:33 UTC (rev 10696)
@@ -20,7 +20,6 @@
 """
 
 from StringIO import StringIO
-import os
 
 from twisted.internet.defer import Deferred, inlineCallbacks, returnValue, \
     maybeDeferred
@@ -31,7 +30,6 @@
 from twext.python.vcomponent import VComponent
 from twext.python.filepath import CachingFilePath as FilePath
 from twext.enterprise.ienterprise import AlreadyFinishedError
-from twext.web2.http_headers import MimeType
 
 from txdav.xml.element import WebDAVUnknownElement, ResourceType
 from txdav.idav import IPropertyStore, IDataStore
@@ -48,14 +46,10 @@
 
 from txdav.caldav.icalendarstore import (
     ICalendarObject, ICalendarHome,
-    ICalendar, IAttachment, ICalendarTransaction)
+    ICalendar, ICalendarTransaction)
 
 from twistedcaldav.customxml import InviteNotification, InviteSummary
-from txdav.caldav.icalendarstore import IAttachmentStorageTransport
-from txdav.caldav.icalendarstore import QuotaExceeded
-from txdav.common.datastore.test.util import (
-    deriveQuota, withSpecialQuota, transactionClean
-)
+from txdav.common.datastore.test.util import transactionClean
 from txdav.common.icommondatastore import ConcurrentModification
 from twistedcaldav.ical import Component
 from twistedcaldav.config import config
@@ -314,48 +308,6 @@
     }
 
 
-    def storeUnderTest(self):
-        """
-        Subclasses must override this to return an L{ICommonDataStore} provider
-        which adheres to the structure detailed by L{CommonTests.requirements}.
-        This attribute is a dict of dict of dicts; the outermost layer
-        representing UIDs mapping to calendar homes, then calendar names mapping
-        to calendar collections, and finally calendar object names mapping to
-        calendar object text.
-        """
-        raise NotImplementedError()
-
-
-    @inlineCallbacks
-    def homeUnderTest(self, txn=None, name="home1"):
-        """
-        Get the calendar home detailed by C{requirements['home1']}.
-        """
-        if txn is None:
-            txn = self.transactionUnderTest()
-        returnValue((yield txn.calendarHomeWithUID(name)))
-
-
-    @inlineCallbacks
-    def calendarUnderTest(self, txn=None, name="calendar_1", home="home1"):
-        """
-        Get the calendar detailed by C{requirements['home1']['calendar_1']}.
-        """
-        returnValue((yield
-            (yield self.homeUnderTest(txn, home)).calendarWithName(name))
-        )
-
-
-    @inlineCallbacks
-    def calendarObjectUnderTest(self, name="1.ics", txn=None):
-        """
-        Get the calendar detailed by
-        C{requirements['home1']['calendar_1'][name]}.
-        """
-        returnValue((yield (yield self.calendarUnderTest(txn))
-                     .calendarObjectWithName(name)))
-
-
     def test_calendarStoreProvides(self):
         """
         The calendar store provides L{IDataStore} and its required attributes.
@@ -1756,60 +1708,7 @@
                 propertyContent
             )
 
-    eventWithDropbox = "\r\n".join("""
-BEGIN:VCALENDAR
-CALSCALE:GREGORIAN
-PRODID:-//Example Inc.//Example Calendar//EN
-VERSION:2.0
-BEGIN:VTIMEZONE
-LAST-MODIFIED:20040110T032845Z
-TZID:US/Eastern
-BEGIN:DAYLIGHT
-DTSTART:20000404T020000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
-TZNAME:EDT
-TZOFFSETFROM:-0500
-TZOFFSETTO:-0400
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20001026T020000
-RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
-TZNAME:EST
-TZOFFSETFROM:-0400
-TZOFFSETTO:-0500
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VEVENT
-DTSTAMP:20051222T205953Z
-CREATED:20060101T150000Z
-DTSTART;TZID=US/Eastern:20060101T100000
-DURATION:PT1H
-SUMMARY:event 1
-UID:event1 at ninevah.local
-ORGANIZER:user01
-ATTENDEE;PARTSTAT=ACCEPTED:user01
-ATTACH;VALUE=URI:/calendars/users/home1/some-dropbox-id/some-dropbox-id/caldavd.plist
-X-APPLE-DROPBOX:/calendars/users/home1/dropbox/some-dropbox-id
-END:VEVENT
-END:VCALENDAR
-    """.strip().split("\n"))
 
-
-    @inlineCallbacks
-    def test_dropboxID(self):
-        """
-        L{ICalendarObject.dropboxID} should synthesize its dropbox from the X
-        -APPLE-DROPBOX property, if available.
-        """
-        cal = yield self.calendarUnderTest()
-        yield cal.createCalendarObjectWithName("drop.ics", VComponent.fromString(
-                self.eventWithDropbox
-            )
-        )
-        obj = yield cal.calendarObjectWithName("drop.ics")
-        self.assertEquals((yield obj.dropboxID()), "some-dropbox-id")
-
-
     def token2revision(self, token):
         """
         FIXME: the API names for L{syncToken}() and L{resourceNamesSinceToken}()
@@ -1835,7 +1734,7 @@
         cal = yield self.calendarUnderTest()
         st = yield home.syncToken()
         yield cal.createCalendarObjectWithName("new.ics", VComponent.fromString(
-                self.eventWithDropbox
+                test_event_text
             )
         )
 
@@ -1870,7 +1769,7 @@
         st = yield cal.syncToken()
         rev = self.token2revision(st)
         yield cal.createCalendarObjectWithName("new.ics", VComponent.fromString(
-                self.eventWithDropbox
+                test_event_text
             )
         )
         yield cal.removeCalendarObjectWithName("2.ics")
@@ -1885,354 +1784,6 @@
 
 
     @inlineCallbacks
-    def test_dropboxIDs(self):
-        """
-        L{ICalendarObject.getAllDropboxIDs} returns a L{Deferred} that fires
-        with a C{list} of all Dropbox IDs.
-        """
-        home = yield self.homeUnderTest()
-        # The only item in the home which has an ATTACH or X-APPLE-DROPBOX
-        # property.
-        allDropboxIDs = set([
-            u'FE5CDC6F-7776-4607-83A9-B90FF7ACC8D0.dropbox',
-        ])
-        self.assertEquals(set((yield home.getAllDropboxIDs())),
-                          allDropboxIDs)
-
-
-    @inlineCallbacks
-    def test_indexByDropboxProperty(self):
-        """
-        L{ICalendarHome.calendarObjectWithDropboxID} will return a calendar
-        object in the calendar home with the given final segment in its C{X
-        -APPLE-DROPBOX} property URI.
-        """
-        objName = "with-dropbox.ics"
-        cal = yield self.calendarUnderTest()
-        yield cal.createCalendarObjectWithName(
-            objName, VComponent.fromString(
-                self.eventWithDropbox
-            )
-        )
-        yield self.commit()
-        home = yield self.homeUnderTest()
-        cal = yield self.calendarUnderTest()
-        fromName = yield cal.calendarObjectWithName(objName)
-        fromDropbox = yield home.calendarObjectWithDropboxID("some-dropbox-id")
-        self.assertEquals(fromName, fromDropbox)
-
-
-    @inlineCallbacks
-    def createAttachmentTest(self, refresh):
-        """
-        Common logic for attachment-creation tests.
-        """
-        obj = yield self.calendarObjectUnderTest()
-        attachment = yield obj.createAttachmentWithName(
-            "new.attachment",
-        )
-        t = attachment.store(MimeType("text", "x-fixture"), "")
-        self.assertProvides(IAttachmentStorageTransport, t)
-        t.write("new attachment")
-        t.write(" text")
-        yield t.loseConnection()
-        obj = yield refresh(obj)
-        attachment = yield obj.attachmentWithName("new.attachment")
-        self.assertProvides(IAttachment, attachment)
-        data = yield self.attachmentToString(attachment)
-        self.assertEquals(data, "new attachment text")
-        contentType = attachment.contentType()
-        self.assertIsInstance(contentType, MimeType)
-        self.assertEquals(contentType, MimeType("text", "x-fixture"))
-        self.assertEquals(attachment.md5(), '50a9f27aeed9247a0833f30a631f1858')
-        self.assertEquals(
-            [attachment.name() for attachment in (yield obj.attachments())],
-            ['new.attachment']
-        )
-
-
-    @inlineCallbacks
-    def test_twoAttachmentsWithTheSameName(self):
-        """
-        Attachments are uniquely identified by their associated object and path;
-        two attachments with the same name won't overwrite each other.
-        """
-        obj = yield self.calendarObjectUnderTest()
-        obj2 = yield self.calendarObjectUnderTest("2.ics")
-        att1 = yield self.stringToAttachment(obj, "sample.attachment",
-                                             "test data 1")
-        att2 = yield self.stringToAttachment(obj2, "sample.attachment",
-                                             "test data 2")
-        data1 = yield self.attachmentToString(att1)
-        data2 = yield self.attachmentToString(att2)
-        self.assertEquals(data1, "test data 1")
-        self.assertEquals(data2, "test data 2")
-
-
-    @inlineCallbacks
-    def stringToAttachment(self, obj, name, contents,
-                           mimeType=MimeType("text", "x-fixture")):
-        """
-        Convenience for producing an attachment from a calendar object.
-
-        @param obj: the calendar object which owns the dropbox associated with
-            the to-be-created attachment.
-
-        @param name: the (utf-8 encoded) name to create the attachment with.
-
-        @type name: C{bytes}
-
-        @param contents: the desired contents of the new attachment.
-
-        @type contents: C{bytes}
-
-        @param mimeType: the mime type of the incoming bytes.
-
-        @return: a L{Deferred} that fires with the L{IAttachment} that is
-            created, once all the bytes have been stored.
-        """
-        att = yield obj.createAttachmentWithName(name)
-        t = att.store(mimeType, "")
-        t.write(contents)
-        yield t.loseConnection()
-        returnValue(att)
-
-
-    def attachmentToString(self, attachment):
-        """
-        Convenience to convert an L{IAttachment} to a string.
-
-        @param attachment: an L{IAttachment} provider to convert into a string.
-
-        @return: a L{Deferred} that fires with the contents of the attachment.
-
-        @rtype: L{Deferred} firing C{bytes}
-        """
-        capture = CaptureProtocol()
-        attachment.retrieve(capture)
-        return capture.deferred
-
-
-    def test_createAttachment(self):
-        """
-        L{ICalendarObject.createAttachmentWithName} will store an
-        L{IAttachment} object that can be retrieved by
-        L{ICalendarObject.attachmentWithName}.
-        """
-        return self.createAttachmentTest(lambda x: x)
-
-
-    def test_createAttachmentCommit(self):
-        """
-        L{ICalendarObject.createAttachmentWithName} will store an
-        L{IAttachment} object that can be retrieved by
-        L{ICalendarObject.attachmentWithName} in subsequent transactions.
-        """
-        @inlineCallbacks
-        def refresh(obj):
-            yield self.commit()
-            result = yield self.calendarObjectUnderTest()
-            returnValue(result)
-        return self.createAttachmentTest(refresh)
-
-
-    @inlineCallbacks
-    def test_attachmentTemporaryFileCleanup(self):
-        """
-        L{IAttachmentStream} object cleans-up its temporary file on txn abort.
-        """
-        obj = yield self.calendarObjectUnderTest()
-        attachment = yield obj.createAttachmentWithName(
-            "new.attachment",
-        )
-        t = attachment.store(MimeType("text", "x-fixture"))
-
-        temp = t._path.path
-
-        yield self.abort()
-
-        self.assertFalse(os.path.exists(temp))
-
-        obj = yield self.calendarObjectUnderTest()
-        attachment = yield obj.createAttachmentWithName(
-            "new.attachment",
-        )
-        t = attachment.store(MimeType("text", "x-fixture"))
-
-        temp = t._path.path
-        os.remove(temp)
-
-        yield self.abort()
-
-        self.assertFalse(os.path.exists(temp))
-
-
-    @inlineCallbacks
-    def test_quotaAllowedBytes(self):
-        """
-        L{ICalendarHome.quotaAllowedBytes} should return the configuration value
-        passed to the calendar store's constructor.
-        """
-        expected = deriveQuota(self)
-        home = yield self.homeUnderTest()
-        actual = home.quotaAllowedBytes()
-        self.assertEquals(expected, actual)
-
-
-    @withSpecialQuota(None)
-    @inlineCallbacks
-    def test_quotaUnlimited(self):
-        """
-        When L{ICalendarHome.quotaAllowedBytes} returns C{None}, quota is
-        unlimited; any sized attachment can be stored.
-        """
-        home = yield self.homeUnderTest()
-        allowed = home.quotaAllowedBytes()
-        self.assertIdentical(allowed, None)
-        yield self.test_createAttachment()
-
-
-    @inlineCallbacks
-    def test_quotaTransportAddress(self):
-        """
-        Since L{IAttachmentStorageTransport} is a subinterface of L{ITransport},
-        it must provide peer and host addresses.
-        """
-        obj = yield self.calendarObjectUnderTest()
-        name = 'a-fun-attachment'
-        attachment = yield obj.createAttachmentWithName(name)
-        transport = attachment.store(MimeType("test", "x-something"), "")
-        peer = transport.getPeer()
-        host = transport.getHost()
-        self.assertIdentical(peer.attachment, attachment)
-        self.assertIdentical(host.attachment, attachment)
-        self.assertIn(name, repr(peer))
-        self.assertIn(name, repr(host))
-
-
-    @inlineCallbacks
-    def exceedQuotaTest(self, getit):
-        """
-        If too many bytes are passed to the transport returned by
-        L{ICalendarObject.createAttachmentWithName},
-        L{IAttachmentStorageTransport.loseConnection} will return a L{Deferred}
-        that fails with L{QuotaExceeded}.
-        """
-        home = yield self.homeUnderTest()
-        attachment = yield getit()
-        t = attachment.store(MimeType("text", "x-fixture"), "")
-        sample = "all work and no play makes jack a dull boy"
-        chunk = (sample * (home.quotaAllowedBytes() / len(sample)))
-
-        t.write(chunk)
-        t.writeSequence([chunk, chunk])
-
-        d = t.loseConnection()
-        yield self.failUnlessFailure(d, QuotaExceeded)
-
-
-    @inlineCallbacks
-    def test_exceedQuotaNew(self):
-        """
-        When quota is exceeded on a new attachment, that attachment will no
-        longer exist.
-        """
-        obj = yield self.calendarObjectUnderTest()
-        yield self.exceedQuotaTest(
-            lambda: obj.createAttachmentWithName("too-big.attachment")
-        )
-        self.assertEquals((yield obj.attachments()), [])
-        yield self.commit()
-        obj = yield self.calendarObjectUnderTest()
-        self.assertEquals((yield obj.attachments()), [])
-
-
-    @inlineCallbacks
-    def test_exceedQuotaReplace(self):
-        """
-        When quota is exceeded while replacing an attachment, that attachment's
-        contents will not be replaced.
-        """
-        obj = yield self.calendarObjectUnderTest()
-        create = lambda: obj.createAttachmentWithName("exists.attachment")
-        get = lambda: obj.attachmentWithName("exists.attachment")
-        attachment = yield create()
-        t = attachment.store(MimeType("text", "x-fixture"), "")
-        sampleData = "a reasonably sized attachment"
-        t.write(sampleData)
-        yield t.loseConnection()
-        yield self.exceedQuotaTest(get)
-        @inlineCallbacks
-        def checkOriginal():
-            actual = yield self.attachmentToString(attachment)
-            expected = sampleData
-            # note: 60 is less than len(expected); trimming is just to make
-            # the error message look sane when the test fails.
-            actual = actual[:60]
-            self.assertEquals(actual, expected)
-        yield checkOriginal()
-        yield self.commit()
-        # Make sure that things go back to normal after a commit of that
-        # transaction.
-        obj = yield self.calendarObjectUnderTest()
-        attachment = yield get()
-        yield checkOriginal()
-
-
-    def test_removeAttachmentWithName(self, refresh=lambda x: x):
-        """
-        L{ICalendarObject.removeAttachmentWithName} will remove the calendar
-        object with the given name.
-        """
-        @inlineCallbacks
-        def deleteIt(ignored):
-            obj = yield self.calendarObjectUnderTest()
-            yield obj.removeAttachmentWithName("new.attachment")
-            obj = yield refresh(obj)
-            self.assertIdentical(
-                None, (yield obj.attachmentWithName("new.attachment"))
-            )
-            self.assertEquals(list((yield obj.attachments())), [])
-        return self.test_createAttachmentCommit().addCallback(deleteIt)
-
-
-    def test_removeAttachmentWithNameCommit(self):
-        """
-        L{ICalendarObject.removeAttachmentWithName} will remove the calendar
-        object with the given name.  (After commit, it will still be gone.)
-        """
-        @inlineCallbacks
-        def refresh(obj):
-            yield self.commit()
-            result = yield self.calendarObjectUnderTest()
-            returnValue(result)
-        return self.test_removeAttachmentWithName(refresh)
-
-
-    @inlineCallbacks
-    def test_noDropboxCalendar(self):
-        """
-        L{ICalendarObject.createAttachmentWithName} may create a directory
-        named 'dropbox', but this should not be seen as a calendar by
-        L{ICalendarHome.calendarWithName} or L{ICalendarHome.calendars}.
-        """
-        obj = yield self.calendarObjectUnderTest()
-        attachment = yield obj.createAttachmentWithName(
-            "new.attachment",
-        )
-        t = attachment.store(MimeType("text", "plain"), "")
-        t.write("new attachment text")
-        yield t.loseConnection()
-        yield self.commit()
-        home = (yield self.homeUnderTest())
-        calendars = (yield home.calendars())
-        self.assertEquals((yield home.calendarWithName("dropbox")), None)
-        self.assertEquals(
-            set([n.name() for n in calendars]),
-            set(home1_calendarNames))
-
-
-    @inlineCallbacks
     def test_finishedOnCommit(self):
         """
         Calling L{ITransaction.abort} or L{ITransaction.commit} after

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py	2013-02-12 01:07:09 UTC (rev 10695)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py	2013-02-12 15:46:33 UTC (rev 10696)
@@ -14,26 +14,1245 @@
 # limitations under the License.
 ##
 
+from calendarserver.tap.util import getRootResource
+
+from pycalendar.datetime import PyCalendarDateTime
+from pycalendar.value import PyCalendarValue
+
+from twext.enterprise.dal.syntax import Delete
+from twext.python.clsprop import classproperty
+from twext.web2.http_headers import MimeType
+from twext.web2.stream import MemoryStream
+
+from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.python.filepath import FilePath
 from twisted.trial import unittest
-from txdav.common.datastore.test.util import CommonCommonTests, buildStore, \
-    populateCalendarsFrom
-from twisted.internet.defer import inlineCallbacks, returnValue
+
 from twistedcaldav.config import config
-import os
-from calendarserver.tap.util import getRootResource
-from twext.enterprise.dal.syntax import Delete
-from txdav.common.datastore.sql_tables import schema
-from pycalendar.datetime import PyCalendarDateTime
+from twistedcaldav.ical import Property, Component
+
 from txdav.caldav.datastore.sql import CalendarStoreFeatures, DropBoxAttachment, \
     ManagedAttachment
-from twext.web2.http_headers import MimeType
-from twistedcaldav.ical import Property
-from pycalendar.value import PyCalendarValue
+from txdav.caldav.datastore.test.common import CaptureProtocol
+from txdav.caldav.icalendarstore import IAttachmentStorageTransport, IAttachment, \
+    QuotaExceeded
+from txdav.common.datastore.sql_tables import schema
+from txdav.common.datastore.test.util import CommonCommonTests, buildStore, \
+    populateCalendarsFrom, deriveQuota, withSpecialQuota
 
+import hashlib
+import os
+import uuid
+
 """
 Tests for txdav.caldav.datastore.sql attachment handling.
 """
 
+storePath = FilePath(__file__).parent().child("calendar_store")
+homeRoot = storePath.child("ho").child("me").child("home1")
+cal1Root = homeRoot.child("calendar_1")
+
+calendar1_objectNames = [
+    "1.ics",
+    "2.ics",
+    "3.ics",
+    "4.ics",
+]
+
+home1_calendarNames = [
+    "calendar_1",
+]
+
+
+class AttachmentTests(CommonCommonTests, unittest.TestCase):
+
+    metadata1 = {
+        "accessMode": "PUBLIC",
+        "isScheduleObject": True,
+        "scheduleTag": "abc",
+        "scheduleEtags": (),
+        "hasPrivateComment": False,
+    }
+    metadata2 = {
+        "accessMode": "PRIVATE",
+        "isScheduleObject": False,
+        "scheduleTag": "",
+        "scheduleEtags": (),
+        "hasPrivateComment": False,
+    }
+    metadata3 = {
+        "accessMode": "PUBLIC",
+        "isScheduleObject": None,
+        "scheduleTag": "abc",
+        "scheduleEtags": (),
+        "hasPrivateComment": True,
+    }
+    metadata4 = {
+        "accessMode": "PUBLIC",
+        "isScheduleObject": True,
+        "scheduleTag": "abc4",
+        "scheduleEtags": (),
+        "hasPrivateComment": False,
+    }
+
+
+    @inlineCallbacks
+    def setUp(self):
+        yield super(AttachmentTests, self).setUp()
+        self._sqlCalendarStore = yield buildStore(self, self.notifierFactory)
+        yield self.populate()
+
+
+    @inlineCallbacks
+    def populate(self):
+        yield populateCalendarsFrom(self.requirements, self.storeUnderTest())
+        self.notifierFactory.reset()
+
+
+    @classproperty(cache=False)
+    def requirements(cls): #@NoSelf
+        metadata1 = cls.metadata1.copy()
+        metadata2 = cls.metadata2.copy()
+        metadata3 = cls.metadata3.copy()
+        metadata4 = cls.metadata4.copy()
+        return {
+        "home1": {
+            "calendar_1": {
+                "1.ics": (cal1Root.child("1.ics").getContent(), metadata1),
+                "2.ics": (cal1Root.child("2.ics").getContent(), metadata2),
+                "3.ics": (cal1Root.child("3.ics").getContent(), metadata3),
+                "4.ics": (cal1Root.child("4.ics").getContent(), metadata4),
+            },
+        },
+    }
+
+
+    def storeUnderTest(self):
+        """
+        Create and return a L{CalendarStore} for testing.
+        """
+        return self._sqlCalendarStore
+
+
+
+class DropBoxAttachmentTests(AttachmentTests):
+
+    eventWithDropbox = "\r\n".join("""
+BEGIN:VCALENDAR
+CALSCALE:GREGORIAN
+PRODID:-//Example Inc.//Example Calendar//EN
+VERSION:2.0
+BEGIN:VTIMEZONE
+LAST-MODIFIED:20040110T032845Z
+TZID:US/Eastern
+BEGIN:DAYLIGHT
+DTSTART:20000404T020000
+RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=4
+TZNAME:EDT
+TZOFFSETFROM:-0500
+TZOFFSETTO:-0400
+END:DAYLIGHT
+BEGIN:STANDARD
+DTSTART:20001026T020000
+RRULE:FREQ=YEARLY;BYDAY=-1SU;BYMONTH=10
+TZNAME:EST
+TZOFFSETFROM:-0400
+TZOFFSETTO:-0500
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+DTSTAMP:20051222T205953Z
+CREATED:20060101T150000Z
+DTSTART;TZID=US/Eastern:20060101T100000
+DURATION:PT1H
+SUMMARY:event 1
+UID:event1 at ninevah.local
+ORGANIZER:user01
+ATTENDEE;PARTSTAT=ACCEPTED:user01
+ATTACH;VALUE=URI:/calendars/users/home1/some-dropbox-id/some-dropbox-id/caldavd.plist
+X-APPLE-DROPBOX:/calendars/users/home1/dropbox/some-dropbox-id
+END:VEVENT
+END:VCALENDAR
+    """.strip().split("\n"))
+
+
+    @inlineCallbacks
+    def setUp(self):
+        yield super(DropBoxAttachmentTests, self).setUp()
+
+        # Need to tweak config and settings to setup dropbox to work
+        self.patch(config, "EnableDropBox", True)
+        self.patch(config, "EnableManagedAttachments", False)
+        self._sqlCalendarStore.enableManagedAttachments = False
+
+        txn = self._sqlCalendarStore.newTransaction()
+        cs = schema.CALENDARSERVER
+        yield Delete(
+            From=cs,
+            Where=cs.NAME == "MANAGED-ATTACHMENTS"
+        ).on(txn)
+        yield txn.commit()
+
+
+    @inlineCallbacks
+    def createAttachmentTest(self, refresh):
+        """
+        Common logic for attachment-creation tests.
+        """
+        obj = yield self.calendarObjectUnderTest()
+        attachment = yield obj.createAttachmentWithName(
+            "new.attachment",
+        )
+        t = attachment.store(MimeType("text", "x-fixture"), "")
+        self.assertProvides(IAttachmentStorageTransport, t)
+        t.write("new attachment")
+        t.write(" text")
+        yield t.loseConnection()
+        obj = yield refresh(obj)
+        attachment = yield obj.attachmentWithName("new.attachment")
+        self.assertProvides(IAttachment, attachment)
+        data = yield self.attachmentToString(attachment)
+        self.assertEquals(data, "new attachment text")
+        contentType = attachment.contentType()
+        self.assertIsInstance(contentType, MimeType)
+        self.assertEquals(contentType, MimeType("text", "x-fixture"))
+        self.assertEquals(attachment.md5(), '50a9f27aeed9247a0833f30a631f1858')
+        self.assertEquals(
+            [attachment.name() for attachment in (yield obj.attachments())],
+            ['new.attachment']
+        )
+
+
+    @inlineCallbacks
+    def stringToAttachment(self, obj, name, contents,
+                           mimeType=MimeType("text", "x-fixture")):
+        """
+        Convenience for producing an attachment from a calendar object.
+
+        @param obj: the calendar object which owns the dropbox associated with
+            the to-be-created attachment.
+
+        @param name: the (utf-8 encoded) name to create the attachment with.
+
+        @type name: C{bytes}
+
+        @param contents: the desired contents of the new attachment.
+
+        @type contents: C{bytes}
+
+        @param mimeType: the mime type of the incoming bytes.
+
+        @return: a L{Deferred} that fires with the L{IAttachment} that is
+            created, once all the bytes have been stored.
+        """
+        att = yield obj.createAttachmentWithName(name)
+        t = att.store(mimeType, "")
+        t.write(contents)
+        yield t.loseConnection()
+        returnValue(att)
+
+
+    def attachmentToString(self, attachment):
+        """
+        Convenience to convert an L{IAttachment} to a string.
+
+        @param attachment: an L{IAttachment} provider to convert into a string.
+
+        @return: a L{Deferred} that fires with the contents of the attachment.
+
+        @rtype: L{Deferred} firing C{bytes}
+        """
+        capture = CaptureProtocol()
+        attachment.retrieve(capture)
+        return capture.deferred
+
+
+    @inlineCallbacks
+    def test_attachmentPath(self):
+        """
+        L{ICalendarObject.createAttachmentWithName} will store an
+        L{IAttachment} object that can be retrieved by
+        L{ICalendarObject.attachmentWithName}.
+        """
+        yield self.createAttachmentTest(lambda x: x)
+        attachmentRoot = (
+            yield self.calendarObjectUnderTest()
+        )._txn._store.attachmentsPath
+        obj = yield self.calendarObjectUnderTest()
+        hasheduid = hashlib.md5(obj._dropboxID).hexdigest()
+        attachmentPath = attachmentRoot.child(
+            hasheduid[0:2]).child(hasheduid[2:4]).child(hasheduid).child(
+                "new.attachment")
+        self.assertTrue(attachmentPath.isfile())
+
+
+    @inlineCallbacks
+    def test_dropboxID(self):
+        """
+        L{ICalendarObject.dropboxID} should synthesize its dropbox from the X
+        -APPLE-DROPBOX property, if available.
+        """
+        cal = yield self.calendarUnderTest()
+        yield cal.createCalendarObjectWithName("drop.ics", Component.fromString(
+                self.eventWithDropbox
+            )
+        )
+        obj = yield cal.calendarObjectWithName("drop.ics")
+        self.assertEquals((yield obj.dropboxID()), "some-dropbox-id")
+
+
+    @inlineCallbacks
+    def test_dropboxIDs(self):
+        """
+        L{ICalendarObject.getAllDropboxIDs} returns a L{Deferred} that fires
+        with a C{list} of all Dropbox IDs.
+        """
+        home = yield self.homeUnderTest()
+        # The only item in the home which has an ATTACH or X-APPLE-DROPBOX
+        # property.
+        allDropboxIDs = set([
+            u'FE5CDC6F-7776-4607-83A9-B90FF7ACC8D0.dropbox',
+        ])
+        self.assertEquals(set((yield home.getAllDropboxIDs())),
+                          allDropboxIDs)
+
+
+    @inlineCallbacks
+    def test_indexByDropboxProperty(self):
+        """
+        L{ICalendarHome.calendarObjectWithDropboxID} will return a calendar
+        object in the calendar home with the given final segment in its C{X
+        -APPLE-DROPBOX} property URI.
+        """
+        objName = "with-dropbox.ics"
+        cal = yield self.calendarUnderTest()
+        yield cal.createCalendarObjectWithName(
+            objName, Component.fromString(
+                self.eventWithDropbox
+            )
+        )
+        yield self.commit()
+        home = yield self.homeUnderTest()
+        cal = yield self.calendarUnderTest()
+        fromName = yield cal.calendarObjectWithName(objName)
+        fromDropbox = yield home.calendarObjectWithDropboxID("some-dropbox-id")
+        self.assertEquals(fromName, fromDropbox)
+
+
+    @inlineCallbacks
+    def test_twoAttachmentsWithTheSameName(self):
+        """
+        Attachments are uniquely identified by their associated object and path;
+        two attachments with the same name won't overwrite each other.
+        """
+        obj = yield self.calendarObjectUnderTest()
+        obj2 = yield self.calendarObjectUnderTest("2.ics")
+        att1 = yield self.stringToAttachment(obj, "sample.attachment",
+                                             "test data 1")
+        att2 = yield self.stringToAttachment(obj2, "sample.attachment",
+                                             "test data 2")
+        data1 = yield self.attachmentToString(att1)
+        data2 = yield self.attachmentToString(att2)
+        self.assertEquals(data1, "test data 1")
+        self.assertEquals(data2, "test data 2")
+
+
+    def test_createAttachment(self):
+        """
+        L{ICalendarObject.createAttachmentWithName} will store an
+        L{IAttachment} object that can be retrieved by
+        L{ICalendarObject.attachmentWithName}.
+        """
+        return self.createAttachmentTest(lambda x: x)
+
+
+    def test_createAttachmentCommit(self):
+        """
+        L{ICalendarObject.createAttachmentWithName} will store an
+        L{IAttachment} object that can be retrieved by
+        L{ICalendarObject.attachmentWithName} in subsequent transactions.
+        """
+        @inlineCallbacks
+        def refresh(obj):
+            yield self.commit()
+            result = yield self.calendarObjectUnderTest()
+            returnValue(result)
+        return self.createAttachmentTest(refresh)
+
+
+    @inlineCallbacks
+    def test_attachmentTemporaryFileCleanup(self):
+        """
+        L{IAttachmentStream} object cleans-up its temporary file on txn abort.
+        """
+        obj = yield self.calendarObjectUnderTest()
+        attachment = yield obj.createAttachmentWithName(
+            "new.attachment",
+        )
+        t = attachment.store(MimeType("text", "x-fixture"))
+
+        temp = t._path.path
+
+        yield self.abort()
+
+        self.assertFalse(os.path.exists(temp))
+
+        obj = yield self.calendarObjectUnderTest()
+        attachment = yield obj.createAttachmentWithName(
+            "new.attachment",
+        )
+        t = attachment.store(MimeType("text", "x-fixture"))
+
+        temp = t._path.path
+        os.remove(temp)
+
+        yield self.abort()
+
+        self.assertFalse(os.path.exists(temp))
+
+
+    @inlineCallbacks
+    def test_quotaAllowedBytes(self):
+        """
+        L{ICalendarHome.quotaAllowedBytes} should return the configuration value
+        passed to the calendar store's constructor.
+        """
+        expected = deriveQuota(self)
+        home = yield self.homeUnderTest()
+        actual = home.quotaAllowedBytes()
+        self.assertEquals(expected, actual)
+
+
+    @withSpecialQuota(None)
+    @inlineCallbacks
+    def test_quotaUnlimited(self):
+        """
+        When L{ICalendarHome.quotaAllowedBytes} returns C{None}, quota is
+        unlimited; any sized attachment can be stored.
+        """
+        home = yield self.homeUnderTest()
+        allowed = home.quotaAllowedBytes()
+        self.assertIdentical(allowed, None)
+        yield self.test_createAttachment()
+
+
+    @inlineCallbacks
+    def test_quotaTransportAddress(self):
+        """
+        Since L{IAttachmentStorageTransport} is a subinterface of L{ITransport},
+        it must provide peer and host addresses.
+        """
+        obj = yield self.calendarObjectUnderTest()
+        name = 'a-fun-attachment'
+        attachment = yield obj.createAttachmentWithName(name)
+        transport = attachment.store(MimeType("test", "x-something"), "")
+        peer = transport.getPeer()
+        host = transport.getHost()
+        self.assertIdentical(peer.attachment, attachment)
+        self.assertIdentical(host.attachment, attachment)
+        self.assertIn(name, repr(peer))
+        self.assertIn(name, repr(host))
+
+
+    @inlineCallbacks
+    def exceedQuotaTest(self, getit):
+        """
+        If too many bytes are passed to the transport returned by
+        L{ICalendarObject.createAttachmentWithName},
+        L{IAttachmentStorageTransport.loseConnection} will return a L{Deferred}
+        that fails with L{QuotaExceeded}.
+        """
+        home = yield self.homeUnderTest()
+        attachment = yield getit()
+        t = attachment.store(MimeType("text", "x-fixture"), "")
+        sample = "all work and no play makes jack a dull boy"
+        chunk = (sample * (home.quotaAllowedBytes() / len(sample)))
+
+        t.write(chunk)
+        t.writeSequence([chunk, chunk])
+
+        d = t.loseConnection()
+        yield self.failUnlessFailure(d, QuotaExceeded)
+
+
+    @inlineCallbacks
+    def test_exceedQuotaNew(self):
+        """
+        When quota is exceeded on a new attachment, that attachment will no
+        longer exist.
+        """
+        obj = yield self.calendarObjectUnderTest()
+        yield self.exceedQuotaTest(
+            lambda: obj.createAttachmentWithName("too-big.attachment")
+        )
+        self.assertEquals((yield obj.attachments()), [])
+        yield self.commit()
+        obj = yield self.calendarObjectUnderTest()
+        self.assertEquals((yield obj.attachments()), [])
+
+
+    @inlineCallbacks
+    def test_exceedQuotaReplace(self):
+        """
+        When quota is exceeded while replacing an attachment, that attachment's
+        contents will not be replaced.
+        """
+        obj = yield self.calendarObjectUnderTest()
+        create = lambda: obj.createAttachmentWithName("exists.attachment")
+        get = lambda: obj.attachmentWithName("exists.attachment")
+        attachment = yield create()
+        t = attachment.store(MimeType("text", "x-fixture"), "")
+        sampleData = "a reasonably sized attachment"
+        t.write(sampleData)
+        yield t.loseConnection()
+        yield self.exceedQuotaTest(get)
+        @inlineCallbacks
+        def checkOriginal():
+            actual = yield self.attachmentToString(attachment)
+            expected = sampleData
+            # note: 60 is less than len(expected); trimming is just to make
+            # the error message look sane when the test fails.
+            actual = actual[:60]
+            self.assertEquals(actual, expected)
+        yield checkOriginal()
+        yield self.commit()
+        # Make sure that things go back to normal after a commit of that
+        # transaction.
+        obj = yield self.calendarObjectUnderTest()
+        attachment = yield get()
+        yield checkOriginal()
+
+
+    def test_removeAttachmentWithName(self, refresh=lambda x: x):
+        """
+        L{ICalendarObject.removeAttachmentWithName} will remove the calendar
+        object with the given name.
+        """
+        @inlineCallbacks
+        def deleteIt(ignored):
+            obj = yield self.calendarObjectUnderTest()
+            yield obj.removeAttachmentWithName("new.attachment")
+            obj = yield refresh(obj)
+            self.assertIdentical(
+                None, (yield obj.attachmentWithName("new.attachment"))
+            )
+            self.assertEquals(list((yield obj.attachments())), [])
+        return self.test_createAttachmentCommit().addCallback(deleteIt)
+
+
+    def test_removeAttachmentWithNameCommit(self):
+        """
+        L{ICalendarObject.removeAttachmentWithName} will remove the calendar
+        object with the given name.  (After commit, it will still be gone.)
+        """
+        @inlineCallbacks
+        def refresh(obj):
+            yield self.commit()
+            result = yield self.calendarObjectUnderTest()
+            returnValue(result)
+        return self.test_removeAttachmentWithName(refresh)
+
+
+    @inlineCallbacks
+    def test_noDropboxCalendar(self):
+        """
+        L{ICalendarObject.createAttachmentWithName} may create a directory
+        named 'dropbox', but this should not be seen as a calendar by
+        L{ICalendarHome.calendarWithName} or L{ICalendarHome.calendars}.
+        """
+        obj = yield self.calendarObjectUnderTest()
+        attachment = yield obj.createAttachmentWithName(
+            "new.attachment",
+        )
+        t = attachment.store(MimeType("text", "plain"), "")
+        t.write("new attachment text")
+        yield t.loseConnection()
+        yield self.commit()
+        home = (yield self.homeUnderTest())
+        calendars = (yield home.calendars())
+        self.assertEquals((yield home.calendarWithName("dropbox")), None)
+        self.assertEquals(
+            set([n.name() for n in calendars]),
+            set(home1_calendarNames))
+
+
+    @inlineCallbacks
+    def test_cleanupAttachments(self):
+        """
+        L{ICalendarObject.remove} will remove an associated calendar
+        attachment.
+        """
+
+        # Create attachment
+        obj = yield self.calendarObjectUnderTest()
+        attachment = yield obj.createAttachmentWithName(
+            "new.attachment",
+        )
+        t = attachment.store(MimeType("text", "x-fixture"))
+        t.write("new attachment")
+        t.write(" text")
+        yield t.loseConnection()
+        apath = attachment._path.path
+        yield self.commit()
+
+        self.assertTrue(os.path.exists(apath))
+
+        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
+        quota = (yield home.quotaUsedBytes())
+        yield self.commit()
+        self.assertNotEqual(quota, 0)
+
+        # Remove resource
+        obj = yield self.calendarObjectUnderTest()
+        yield obj.remove()
+        yield self.commit()
+
+        self.assertFalse(os.path.exists(apath))
+
+        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
+        quota = (yield home.quotaUsedBytes())
+        yield self.commit()
+        self.assertEqual(quota, 0)
+
+
+    @inlineCallbacks
+    def test_cleanupMultipleAttachments(self):
+        """
+        L{ICalendarObject.remove} will remove all associated calendar
+        attachments.
+        """
+
+        # Create attachment
+        obj = yield self.calendarObjectUnderTest()
+
+        attachment = yield obj.createAttachmentWithName(
+            "new.attachment",
+        )
+        t = attachment.store(MimeType("text", "x-fixture"))
+        t.write("new attachment")
+        t.write(" text")
+        yield t.loseConnection()
+        apath1 = attachment._path.path
+
+        attachment = yield obj.createAttachmentWithName(
+            "new.attachment2",
+        )
+        t = attachment.store(MimeType("text", "x-fixture"))
+        t.write("new attachment 2")
+        t.write(" text")
+        yield t.loseConnection()
+        apath2 = attachment._path.path
+
+        yield self.commit()
+
+        self.assertTrue(os.path.exists(apath1))
+        self.assertTrue(os.path.exists(apath2))
+
+        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
+        quota = (yield home.quotaUsedBytes())
+        yield self.commit()
+        self.assertNotEqual(quota, 0)
+
+        # Remove resource
+        obj = yield self.calendarObjectUnderTest()
+        yield obj.remove()
+        yield self.commit()
+
+        self.assertFalse(os.path.exists(apath1))
+        self.assertFalse(os.path.exists(apath2))
+
+        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
+        quota = (yield home.quotaUsedBytes())
+        yield self.commit()
+        self.assertEqual(quota, 0)
+
+
+    @inlineCallbacks
+    def test_cleanupAttachmentsOnMultipleResources(self):
+        """
+        L{ICalendarObject.remove} will remove all associated calendar
+        attachments unless used in another resource.
+        """
+
+        # Create attachment
+        obj = yield self.calendarObjectUnderTest()
+
+        attachment = yield obj.createAttachmentWithName(
+            "new.attachment",
+        )
+        t = attachment.store(MimeType("text", "x-fixture"))
+        t.write("new attachment")
+        t.write(" text")
+        yield t.loseConnection()
+        apath = attachment._path.path
+
+        new_component = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VTIMEZONE
+TZID:US/Pacific
+BEGIN:DAYLIGHT
+TZOFFSETFROM:-0800
+RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
+DTSTART:20070311T020000
+TZNAME:PDT
+TZOFFSETTO:-0700
+END:DAYLIGHT
+BEGIN:STANDARD
+TZOFFSETFROM:-0700
+RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
+DTSTART:20071104T020000
+TZNAME:PST
+TZOFFSETTO:-0800
+END:STANDARD
+END:VTIMEZONE
+BEGIN:VEVENT
+ATTENDEE;CN="Wilfredo Sanchez";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:mailt
+ o:wsanchez at example.com
+ATTENDEE;CN="Cyrus Daboo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:mailto:cda
+ boo at example.com
+DTEND;TZID=US/Pacific:%(now)s0324T124500
+TRANSP:OPAQUE
+ORGANIZER;CN="Wilfredo Sanchez":mailto:wsanchez at example.com
+UID:uid1-attachmenttest
+DTSTAMP:20090326T145447Z
+LOCATION:Wilfredo's Office
+SEQUENCE:2
+X-APPLE-EWS-BUSYSTATUS:BUSY
+X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/FE5CDC6F-7776-4607-83
+ A9-B90FF7ACC8D0.dropbox
+SUMMARY:CalDAV protocol updates
+DTSTART;TZID=US/Pacific:%(now)s0324T121500
+CREATED:20090326T145440Z
+BEGIN:VALARM
+X-WR-ALARMUID:DB39AB67-449C-441C-89D2-D740B5F41A73
+TRIGGER;VALUE=DATE-TIME:%(now)s0324T180009Z
+ACTION:AUDIO
+END:VALARM
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n") % {"now": 2012}
+
+        calendar = yield self.calendarUnderTest()
+        yield calendar.createCalendarObjectWithName(
+            "test.ics", Component.fromString(new_component)
+        )
+
+        yield self.commit()
+
+        self.assertTrue(os.path.exists(apath))
+
+        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
+        quota = (yield home.quotaUsedBytes())
+        yield self.commit()
+        self.assertNotEqual(quota, 0)
+
+        # Remove resource
+        obj = yield self.calendarObjectUnderTest()
+        yield obj.remove()
+        yield self.commit()
+
+        self.assertTrue(os.path.exists(apath))
+
+        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
+        quota = (yield home.quotaUsedBytes())
+        yield self.commit()
+        self.assertNotEqual(quota, 0)
+
+        # Remove resource
+        obj = yield self.calendarObjectUnderTest("test.ics")
+        yield obj.remove()
+        yield self.commit()
+
+        self.assertFalse(os.path.exists(apath))
+
+        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
+        quota = (yield home.quotaUsedBytes())
+        yield self.commit()
+        self.assertEqual(quota, 0)
+
+
+
+class ManagedAttachmentTests(AttachmentTests):
+
+    @inlineCallbacks
+    def setUp(self):
+        yield super(ManagedAttachmentTests, self).setUp()
+
+        # Need to tweak config and settings to setup dropbox to work
+        self.patch(config, "EnableDropBox", False)
+        self.patch(config, "EnableManagedAttachments", True)
+        self._sqlCalendarStore.enableManagedAttachments = True
+
+
+    @inlineCallbacks
+    def createAttachmentTest(self, refresh):
+        """
+        Common logic for attachment-creation tests.
+        """
+        obj = yield self.calendarObjectUnderTest()
+        attachment = yield obj.createManagedAttachment()
+        mid = attachment.managedID()
+        t = attachment.store(MimeType("text", "x-fixture"), "new.attachment")
+        self.assertProvides(IAttachmentStorageTransport, t)
+        t.write("new attachment")
+        t.write(" text")
+        yield t.loseConnection()
+        obj = yield refresh(obj)
+        attachment = yield obj.attachmentWithManagedID(mid)
+        self.assertProvides(IAttachment, attachment)
+        data = yield self.attachmentToString(attachment)
+        self.assertEquals(data, "new attachment text")
+        contentType = attachment.contentType()
+        self.assertIsInstance(contentType, MimeType)
+        self.assertEquals(contentType, MimeType("text", "x-fixture"))
+        self.assertEquals(attachment.md5(), '50a9f27aeed9247a0833f30a631f1858')
+        self.assertEquals(
+            (yield obj.managedAttachmentList()),
+            ['new-%s.attachment' % (mid[:8],)]
+        )
+
+        returnValue(mid)
+
+
+    @inlineCallbacks
+    def stringToAttachment(self, obj, name, contents,
+                           mimeType=MimeType("text", "x-fixture")):
+        """
+        Convenience for producing an attachment from a calendar object.
+
+        @param obj: the calendar object which owns the dropbox associated with
+            the to-be-created attachment.
+
+        @param name: the (utf-8 encoded) name to create the attachment with.
+
+        @type name: C{bytes}
+
+        @param contents: the desired contents of the new attachment.
+
+        @type contents: C{bytes}
+
+        @param mimeType: the mime type of the incoming bytes.
+
+        @return: a L{Deferred} that fires with the L{IAttachment} that is
+            created, once all the bytes have been stored.
+        """
+        att = yield obj.createManagedAttachment()
+        t = att.store(mimeType, name)
+        t.write(contents)
+        yield t.loseConnection()
+        returnValue(att)
+
+
+    def attachmentToString(self, attachment):
+        """
+        Convenience to convert an L{IAttachment} to a string.
+
+        @param attachment: an L{IAttachment} provider to convert into a string.
+
+        @return: a L{Deferred} that fires with the contents of the attachment.
+
+        @rtype: L{Deferred} firing C{bytes}
+        """
+        capture = CaptureProtocol()
+        attachment.retrieve(capture)
+        return capture.deferred
+
+
+    @inlineCallbacks
+    def test_attachmentPath(self):
+        """
+        L{ICalendarObject.createManagedAttachment} will store an
+        L{IAttachment} object that can be retrieved by
+        L{ICalendarObject.attachmentWithManagedID}.
+        """
+
+        mid = yield self.createAttachmentTest(lambda x: x)
+        obj = yield self.calendarObjectUnderTest()
+        attachment = yield obj.attachmentWithManagedID(mid)
+        hasheduid = hashlib.md5(str(attachment._attachmentID)).hexdigest()
+
+        attachmentRoot = (
+            yield self.calendarObjectUnderTest()
+        )._txn._store.attachmentsPath
+        attachmentPath = attachmentRoot.child(
+            hasheduid[0:2]).child(hasheduid[2:4]).child(hasheduid)
+        self.assertTrue(attachmentPath.isfile())
+
+
+    @inlineCallbacks
+    def test_twoAttachmentsWithTheSameName(self):
+        """
+        Attachments are uniquely identified by their associated object and path;
+        two attachments with the same name won't overwrite each other.
+        """
+        obj = yield self.calendarObjectUnderTest()
+        obj2 = yield self.calendarObjectUnderTest("2.ics")
+        att1 = yield self.stringToAttachment(obj, "sample.attachment",
+                                             "test data 1")
+        att2 = yield self.stringToAttachment(obj2, "sample.attachment",
+                                             "test data 2")
+        data1 = yield self.attachmentToString(att1)
+        data2 = yield self.attachmentToString(att2)
+        self.assertEquals(data1, "test data 1")
+        self.assertEquals(data2, "test data 2")
+
+
+    def test_createAttachment(self):
+        """
+        L{ICalendarObject.createManagedAttachment} will store an
+        L{IAttachment} object that can be retrieved by
+        L{ICalendarObject.attachmentWithManagedID}.
+        """
+        return self.createAttachmentTest(lambda x: x)
+
+
+    def test_createAttachmentCommit(self):
+        """
+        L{ICalendarObject.createManagedAttachment} will store an
+        L{IAttachment} object that can be retrieved by
+        L{ICalendarObject.attachmentWithManagedID} in subsequent transactions.
+        """
+        @inlineCallbacks
+        def refresh(obj):
+            yield self.commit()
+            result = yield self.calendarObjectUnderTest()
+            returnValue(result)
+        return self.createAttachmentTest(refresh)
+
+
+    @inlineCallbacks
+    def test_attachmentTemporaryFileCleanup(self):
+        """
+        L{IAttachmentStream} object cleans-up its temporary file on txn abort.
+        """
+        obj = yield self.calendarObjectUnderTest()
+        attachment = yield obj.createManagedAttachment()
+        t = attachment.store(MimeType("text", "x-fixture"), "new.attachment")
+
+        temp = t._path.path
+
+        yield self.abort()
+
+        self.assertFalse(os.path.exists(temp))
+
+        obj = yield self.calendarObjectUnderTest()
+        attachment = yield obj.createManagedAttachment()
+        t = attachment.store(MimeType("text", "x-fixture"), "new.attachment")
+
+        temp = t._path.path
+        os.remove(temp)
+
+        yield self.abort()
+
+        self.assertFalse(os.path.exists(temp))
+
+
+    @inlineCallbacks
+    def test_quotaAllowedBytes(self):
+        """
+        L{ICalendarHome.quotaAllowedBytes} should return the configuration value
+        passed to the calendar store's constructor.
+        """
+        expected = deriveQuota(self)
+        home = yield self.homeUnderTest()
+        actual = home.quotaAllowedBytes()
+        self.assertEquals(expected, actual)
+
+
+    @withSpecialQuota(None)
+    @inlineCallbacks
+    def test_quotaUnlimited(self):
+        """
+        When L{ICalendarHome.quotaAllowedBytes} returns C{None}, quota is
+        unlimited; any sized attachment can be stored.
+        """
+        home = yield self.homeUnderTest()
+        allowed = home.quotaAllowedBytes()
+        self.assertIdentical(allowed, None)
+        yield self.test_createAttachment()
+
+
+    @inlineCallbacks
+    def test_quotaTransportAddress(self):
+        """
+        Since L{IAttachmentStorageTransport} is a subinterface of L{ITransport},
+        it must provide peer and host addresses.
+        """
+        obj = yield self.calendarObjectUnderTest()
+        name = 'a-fun-attachment'
+        attachment = yield obj.createManagedAttachment()
+        transport = attachment.store(MimeType("test", "x-something"), name)
+        peer = transport.getPeer()
+        host = transport.getHost()
+        self.assertIdentical(peer.attachment, attachment)
+        self.assertIdentical(host.attachment, attachment)
+        self.assertIn(name, repr(peer))
+        self.assertIn(name, repr(host))
+
+
+    @inlineCallbacks
+    def exceedQuotaTest(self, getit, name):
+        """
+        If too many bytes are passed to the transport returned by
+        L{ICalendarObject.createManagedAttachment},
+        L{IAttachmentStorageTransport.loseConnection} will return a L{Deferred}
+        that fails with L{QuotaExceeded}.
+        """
+        home = yield self.homeUnderTest()
+        attachment = yield getit()
+        t = attachment.store(MimeType("text", "x-fixture"), name)
+        sample = "all work and no play makes jack a dull boy"
+        chunk = (sample * (home.quotaAllowedBytes() / len(sample)))
+
+        t.write(chunk)
+        t.writeSequence([chunk, chunk])
+
+        d = t.loseConnection()
+        yield self.failUnlessFailure(d, QuotaExceeded)
+
+
+    @inlineCallbacks
+    def test_exceedQuotaNew(self):
+        """
+        When quota is exceeded on a new attachment, that attachment will no
+        longer exist.
+        """
+        obj = yield self.calendarObjectUnderTest()
+        yield self.exceedQuotaTest(
+            lambda: obj.createManagedAttachment(), "too-big.attachment"
+        )
+        self.assertEquals((yield obj.managedAttachmentList()), [])
+        yield self.commit()
+        obj = yield self.calendarObjectUnderTest()
+        self.assertEquals((yield obj.managedAttachmentList()), [])
+
+
+    @inlineCallbacks
+    def test_exceedQuotaReplace(self):
+        """
+        When quota is exceeded while replacing an attachment, that attachment's
+        contents will not be replaced.
+        """
+        obj = yield self.calendarObjectUnderTest()
+        create = lambda: obj.createManagedAttachment()
+        attachment = yield create()
+        get = lambda: obj.attachmentWithManagedID(attachment.managedID())
+        t = attachment.store(MimeType("text", "x-fixture"), "new.attachment")
+        sampleData = "a reasonably sized attachment"
+        t.write(sampleData)
+        yield t.loseConnection()
+        yield self.exceedQuotaTest(get, "exists.attachment")
+        @inlineCallbacks
+        def checkOriginal():
+            actual = yield self.attachmentToString(attachment)
+            expected = sampleData
+            # note: 60 is less than len(expected); trimming is just to make
+            # the error message look sane when the test fails.
+            actual = actual[:60]
+            self.assertEquals(actual, expected)
+        yield checkOriginal()
+        yield self.commit()
+        # Make sure that things go back to normal after a commit of that
+        # transaction.
+        obj = yield self.calendarObjectUnderTest()
+        attachment = yield get()
+        yield checkOriginal()
+
+
+    def test_removeManagedAttachmentWithID(self, refresh=lambda x: x):
+        """
+        L{ICalendarObject.removeManagedAttachmentWithID} will remove the calendar
+        object with the given managed-id.
+        """
+        @inlineCallbacks
+        def deleteIt(mid):
+            obj = yield self.calendarObjectUnderTest()
+            yield obj.removeManagedAttachmentWithID(mid)
+            obj = yield refresh(obj)
+            self.assertIdentical(
+                None, (yield obj.attachmentWithManagedID(mid))
+            )
+            self.assertEquals(list((yield obj.managedAttachmentList())), [])
+        return self.test_createAttachmentCommit().addCallback(deleteIt)
+
+
+    def test_removeManagedAttachmentWithIDCommit(self):
+        """
+        L{ICalendarObject.removeManagedAttachmentWithID} will remove the calendar
+        object with the given managed-id.  (After commit, it will still be gone.)
+        """
+        @inlineCallbacks
+        def refresh(obj):
+            yield self.commit()
+            result = yield self.calendarObjectUnderTest()
+            returnValue(result)
+        return self.test_removeManagedAttachmentWithID(refresh)
+
+
+    @inlineCallbacks
+    def test_noDropboxCalendar(self):
+        """
+        L{ICalendarObject.createManagedAttachment} may create a directory
+        named 'dropbox', but this should not be seen as a calendar by
+        L{ICalendarHome.calendarWithName} or L{ICalendarHome.calendars}.
+        """
+        obj = yield self.calendarObjectUnderTest()
+        attachment = yield obj.createManagedAttachment()
+        t = attachment.store(MimeType("text", "plain"), "new.attachment")
+        t.write("new attachment text")
+        yield t.loseConnection()
+        yield self.commit()
+        home = (yield self.homeUnderTest())
+        calendars = (yield home.calendars())
+        self.assertEquals((yield home.calendarWithName("dropbox")), None)
+        self.assertEquals(
+            set([n.name() for n in calendars]),
+            set(home1_calendarNames))
+
+
+    @inlineCallbacks
+    def test_cleanupAttachments(self):
+        """
+        L{ICalendarObject.remove} will remove an associated calendar
+        attachment.
+        """
+
+        # Create attachment
+        obj = yield self.calendarObjectUnderTest()
+        attachment = yield obj.createManagedAttachment()
+        t = attachment.store(MimeType("text", "x-fixture"), "new.attachment")
+        t.write("new attachment")
+        t.write(" text")
+        yield t.loseConnection()
+        apath = attachment._path.path
+        yield self.commit()
+
+        self.assertTrue(os.path.exists(apath))
+
+        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
+        quota = (yield home.quotaUsedBytes())
+        yield self.commit()
+        self.assertNotEqual(quota, 0)
+
+        # Remove resource
+        obj = yield self.calendarObjectUnderTest()
+        yield obj.remove()
+        yield self.commit()
+
+        self.assertFalse(os.path.exists(apath))
+
+        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
+        quota = (yield home.quotaUsedBytes())
+        yield self.commit()
+        self.assertEqual(quota, 0)
+
+
+    @inlineCallbacks
+    def test_cleanupMultipleAttachments(self):
+        """
+        L{ICalendarObject.remove} will remove all associated calendar
+        attachments.
+        """
+
+        # Create attachment
+        obj = yield self.calendarObjectUnderTest()
+
+        attachment = yield obj.createManagedAttachment()
+        t = attachment.store(MimeType("text", "x-fixture"), "new.attachment")
+        t.write("new attachment")
+        t.write(" text")
+        yield t.loseConnection()
+        apath1 = attachment._path.path
+
+        attachment = yield obj.createManagedAttachment()
+        t = attachment.store(MimeType("text", "x-fixture"), "new.attachment2")
+        t.write("new attachment 2")
+        t.write(" text")
+        yield t.loseConnection()
+        apath2 = attachment._path.path
+
+        yield self.commit()
+
+        self.assertTrue(os.path.exists(apath1))
+        self.assertTrue(os.path.exists(apath2))
+
+        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
+        quota = (yield home.quotaUsedBytes())
+        yield self.commit()
+        self.assertNotEqual(quota, 0)
+
+        # Remove resource
+        obj = yield self.calendarObjectUnderTest()
+        yield obj.remove()
+        yield self.commit()
+
+        self.assertFalse(os.path.exists(apath1))
+        self.assertFalse(os.path.exists(apath2))
+
+        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
+        quota = (yield home.quotaUsedBytes())
+        yield self.commit()
+        self.assertEqual(quota, 0)
+
+
+    @inlineCallbacks
+    def test_cleanupAttachmentsOnMultipleResources(self):
+        """
+        L{ICalendarObject.remove} will remove all associated calendar
+        attachments unless used in another resource.
+        """
+
+        # Create attachment
+        obj = yield self.calendarObjectUnderTest()
+        cdata = yield obj.component()
+
+        attachment, _ignore_location = yield obj.addAttachment(None, MimeType("text", "x-fixture"), "new.attachment", MemoryStream("new attachment text"), cdata)
+        mid = attachment.managedID()
+        apath = attachment._path.path
+
+        newcdata = Component.fromString(str(cdata).replace("uid1", "uid1-attached"))
+        calendar = yield self.calendarUnderTest()
+        cobj = yield calendar.createCalendarObjectWithName(
+            "test.ics", newcdata
+        )
+        yield cobj.copyResourceAttachments(((mid, str(uuid.uuid4()),),))
+        yield self.commit()
+
+        self.assertTrue(os.path.exists(apath))
+
+        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
+        quota = (yield home.quotaUsedBytes())
+        yield self.commit()
+        self.assertNotEqual(quota, 0)
+
+        # Remove resource
+        obj = yield self.calendarObjectUnderTest()
+        yield obj.remove()
+        yield self.commit()
+
+        self.assertTrue(os.path.exists(apath))
+
+        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
+        quota = (yield home.quotaUsedBytes())
+        yield self.commit()
+        self.assertNotEqual(quota, 0)
+
+        # Remove resource
+        obj = yield self.calendarObjectUnderTest("test.ics")
+        yield obj.remove()
+        yield self.commit()
+
+        self.assertFalse(os.path.exists(apath))
+
+        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
+        quota = (yield home.quotaUsedBytes())
+        yield self.commit()
+        self.assertEqual(quota, 0)
+
+
+
 now = PyCalendarDateTime.getToday().getYear()
 
 PLAIN_ICS = """BEGIN:VCALENDAR

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py	2013-02-12 01:07:09 UTC (rev 10695)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py	2013-02-12 15:46:33 UTC (rev 10696)
@@ -22,14 +22,12 @@
 from pycalendar.datetime import PyCalendarDateTime
 from pycalendar.timezone import PyCalendarTimezone
 
-from twext.enterprise.dal.syntax import Select, Parameter, Insert
+from twext.enterprise.dal.syntax import Select, Parameter, Insert, Delete
 from twext.python.vcomponent import VComponent
-from twext.web2.http_headers import MimeType
 
 from twisted.internet import reactor
 from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.internet.task import deferLater
-from twisted.python import hashlib
 from twisted.trial import unittest
 
 from twistedcaldav import caldavxml
@@ -53,7 +51,6 @@
 from txdav.xml.rfc2518 import GETContentLanguage, ResourceType
 
 import datetime
-import os
 
 class CalendarSQLStorageTests(CalendarCommonTests, unittest.TestCase):
     """
@@ -130,25 +127,6 @@
 
 
     @inlineCallbacks
-    def test_attachmentPath(self):
-        """
-        L{ICalendarObject.createAttachmentWithName} will store an
-        L{IAttachment} object that can be retrieved by
-        L{ICalendarObject.attachmentWithName}.
-        """
-        yield self.createAttachmentTest(lambda x: x)
-        attachmentRoot = (
-            yield self.calendarObjectUnderTest()
-        )._txn._store.attachmentsPath
-        obj = yield self.calendarObjectUnderTest()
-        hasheduid = hashlib.md5(obj._dropboxID).hexdigest()
-        attachmentPath = attachmentRoot.child(
-            hasheduid[0:2]).child(hasheduid[2:4]).child(hasheduid).child(
-                "new.attachment")
-        self.assertTrue(attachmentPath.isfile())
-
-
-    @inlineCallbacks
     def test_migrateCalendarFromFile(self):
         """
         C{_migrateCalendar()} can migrate a file-backed calendar to a database-
@@ -366,6 +344,19 @@
         backed calendar. Test that migrating a calendar containing duplicate attachments
         will de-duplicate those attachments and proceed without error.
         """
+        # Need to tweak config and settings to setup dropbox to work
+        self.patch(config, "EnableDropBox", True)
+        self.patch(config, "EnableManagedAttachments", False)
+        self._sqlCalendarStore.enableManagedAttachments = False
+
+        txn = self._sqlCalendarStore.newTransaction()
+        cs = schema.CALENDARSERVER
+        yield Delete(
+            From=cs,
+            Where=cs.NAME == "MANAGED-ATTACHMENTS"
+        ).on(txn)
+        yield txn.commit()
+
         fromCalendar = yield (yield self.fileTransaction().calendarHomeWithUID(
             "home_attachments")).calendarWithName("calendar_1")
         toHome = yield self.transactionUnderTest().calendarHomeWithUID(
@@ -1436,200 +1427,3 @@
         obj = (yield self.calendarObjectUnderTest())
         calendarObject = (yield home.objectResourceWithID(obj._resourceID))
         self.assertNotEquals(calendarObject, None)
-
-
-    @inlineCallbacks
-    def test_cleanupAttachments(self):
-        """
-        L{ICalendarObject.remove} will remove an associated calendar
-        attachment.
-        """
-
-        # Create attachment
-        obj = yield self.calendarObjectUnderTest()
-        attachment = yield obj.createAttachmentWithName(
-            "new.attachment",
-        )
-        t = attachment.store(MimeType("text", "x-fixture"))
-        t.write("new attachment")
-        t.write(" text")
-        yield t.loseConnection()
-        apath = attachment._path.path
-        yield self.commit()
-
-        self.assertTrue(os.path.exists(apath))
-
-        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
-        quota = (yield home.quotaUsedBytes())
-        yield self.commit()
-        self.assertNotEqual(quota, 0)
-
-        # Remove resource
-        obj = yield self.calendarObjectUnderTest()
-        yield obj.remove()
-        yield self.commit()
-
-        self.assertFalse(os.path.exists(apath))
-
-        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
-        quota = (yield home.quotaUsedBytes())
-        yield self.commit()
-        self.assertEqual(quota, 0)
-
-
-    @inlineCallbacks
-    def test_cleanupMultipleAttachments(self):
-        """
-        L{ICalendarObject.remove} will remove all associated calendar
-        attachments.
-        """
-
-        # Create attachment
-        obj = yield self.calendarObjectUnderTest()
-
-        attachment = yield obj.createAttachmentWithName(
-            "new.attachment",
-        )
-        t = attachment.store(MimeType("text", "x-fixture"))
-        t.write("new attachment")
-        t.write(" text")
-        yield t.loseConnection()
-        apath1 = attachment._path.path
-
-        attachment = yield obj.createAttachmentWithName(
-            "new.attachment2",
-        )
-        t = attachment.store(MimeType("text", "x-fixture"))
-        t.write("new attachment 2")
-        t.write(" text")
-        yield t.loseConnection()
-        apath2 = attachment._path.path
-
-        yield self.commit()
-
-        self.assertTrue(os.path.exists(apath1))
-        self.assertTrue(os.path.exists(apath2))
-
-        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
-        quota = (yield home.quotaUsedBytes())
-        yield self.commit()
-        self.assertNotEqual(quota, 0)
-
-        # Remove resource
-        obj = yield self.calendarObjectUnderTest()
-        yield obj.remove()
-        yield self.commit()
-
-        self.assertFalse(os.path.exists(apath1))
-        self.assertFalse(os.path.exists(apath2))
-
-        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
-        quota = (yield home.quotaUsedBytes())
-        yield self.commit()
-        self.assertEqual(quota, 0)
-
-
-    @inlineCallbacks
-    def test_cleanupAttachmentsOnMultipleResources(self):
-        """
-        L{ICalendarObject.remove} will remove all associated calendar
-        attachments unless used in another resource.
-        """
-
-        # Create attachment
-        obj = yield self.calendarObjectUnderTest()
-
-        attachment = yield obj.createAttachmentWithName(
-            "new.attachment",
-        )
-        t = attachment.store(MimeType("text", "x-fixture"))
-        t.write("new attachment")
-        t.write(" text")
-        yield t.loseConnection()
-        apath = attachment._path.path
-
-        new_component = """BEGIN:VCALENDAR
-VERSION:2.0
-PRODID:-//Apple Inc.//iCal 4.0.1//EN
-CALSCALE:GREGORIAN
-BEGIN:VTIMEZONE
-TZID:US/Pacific
-BEGIN:DAYLIGHT
-TZOFFSETFROM:-0800
-RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU
-DTSTART:20070311T020000
-TZNAME:PDT
-TZOFFSETTO:-0700
-END:DAYLIGHT
-BEGIN:STANDARD
-TZOFFSETFROM:-0700
-RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU
-DTSTART:20071104T020000
-TZNAME:PST
-TZOFFSETTO:-0800
-END:STANDARD
-END:VTIMEZONE
-BEGIN:VEVENT
-ATTENDEE;CN="Wilfredo Sanchez";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:mailt
- o:wsanchez at example.com
-ATTENDEE;CN="Cyrus Daboo";CUTYPE=INDIVIDUAL;PARTSTAT=ACCEPTED:mailto:cda
- boo at example.com
-DTEND;TZID=US/Pacific:%(now)s0324T124500
-TRANSP:OPAQUE
-ORGANIZER;CN="Wilfredo Sanchez":mailto:wsanchez at example.com
-UID:uid1-attachmenttest
-DTSTAMP:20090326T145447Z
-LOCATION:Wilfredo's Office
-SEQUENCE:2
-X-APPLE-EWS-BUSYSTATUS:BUSY
-X-APPLE-DROPBOX:/calendars/__uids__/user01/dropbox/FE5CDC6F-7776-4607-83
- A9-B90FF7ACC8D0.dropbox
-SUMMARY:CalDAV protocol updates
-DTSTART;TZID=US/Pacific:%(now)s0324T121500
-CREATED:20090326T145440Z
-BEGIN:VALARM
-X-WR-ALARMUID:DB39AB67-449C-441C-89D2-D740B5F41A73
-TRIGGER;VALUE=DATE-TIME:%(now)s0324T180009Z
-ACTION:AUDIO
-END:VALARM
-END:VEVENT
-END:VCALENDAR
-""".replace("\n", "\r\n") % {"now": 2012}
-
-        calendar = yield self.calendarUnderTest()
-        yield calendar.createCalendarObjectWithName(
-            "test.ics", VComponent.fromString(new_component)
-        )
-
-        yield self.commit()
-
-        self.assertTrue(os.path.exists(apath))
-
-        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
-        quota = (yield home.quotaUsedBytes())
-        yield self.commit()
-        self.assertNotEqual(quota, 0)
-
-        # Remove resource
-        obj = yield self.calendarObjectUnderTest()
-        yield obj.remove()
-        yield self.commit()
-
-        self.assertTrue(os.path.exists(apath))
-
-        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
-        quota = (yield home.quotaUsedBytes())
-        yield self.commit()
-        self.assertNotEqual(quota, 0)
-
-        # Remove resource
-        obj = yield self.calendarObjectUnderTest("test.ics")
-        yield obj.remove()
-        yield self.commit()
-
-        self.assertFalse(os.path.exists(apath))
-
-        home = (yield self.transactionUnderTest().calendarHomeWithUID("home1"))
-        quota = (yield home.quotaUsedBytes())
-        yield self.commit()
-        self.assertEqual(quota, 0)

Modified: CalendarServer/trunk/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/util.py	2013-02-12 01:07:09 UTC (rev 10695)
+++ CalendarServer/trunk/txdav/common/datastore/test/util.py	2013-02-12 15:46:33 UTC (rev 10696)
@@ -615,7 +615,37 @@
         raise NotImplementedError("CommonCommonTests subclasses must implement.")
 
 
+    @inlineCallbacks
+    def homeUnderTest(self, txn=None, name="home1"):
+        """
+        Get the calendar home detailed by C{requirements['home1']}.
+        """
+        if txn is None:
+            txn = self.transactionUnderTest()
+        returnValue((yield txn.calendarHomeWithUID(name)))
 
+
+    @inlineCallbacks
+    def calendarUnderTest(self, txn=None, name="calendar_1", home="home1"):
+        """
+        Get the calendar detailed by C{requirements['home1']['calendar_1']}.
+        """
+        returnValue((yield
+            (yield self.homeUnderTest(txn, home)).calendarWithName(name))
+        )
+
+
+    @inlineCallbacks
+    def calendarObjectUnderTest(self, name="1.ics", txn=None):
+        """
+        Get the calendar detailed by
+        C{requirements['home1']['calendar_1'][name]}.
+        """
+        returnValue((yield (yield self.calendarUnderTest(txn))
+                     .calendarObjectWithName(name)))
+
+
+
 class StubNodeCacher(object):
 
     def waitForNode(self, notifier, nodeName):

Modified: CalendarServer/trunk/txdav/common/datastore/upgrade/test/test_migrate.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/upgrade/test/test_migrate.py	2013-02-12 01:07:09 UTC (rev 10695)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/test/test_migrate.py	2013-02-12 15:46:33 UTC (rev 10696)
@@ -18,36 +18,37 @@
 Tests for L{txdav.common.datastore.upgrade.migrate}.
 """
 
-import copy
-
+from twext.enterprise.adbapi2 import Pickle
+from twext.enterprise.dal.syntax import Delete
 from twext.python.filepath import CachingFilePath
 from twext.web2.http_headers import MimeType
-from twext.enterprise.adbapi2 import Pickle
 
-from twisted.python.modules import getModule
 from twisted.application.service import Service, MultiService
 from twisted.internet.defer import inlineCallbacks, Deferred, returnValue
 from twisted.internet.protocol import Protocol
 from twisted.protocols.amp import AMP, Command, String
+from twisted.python.modules import getModule
 from twisted.python.reflect import qual, namedAny
 from twisted.trial.unittest import TestCase
 
+from twistedcaldav.config import config
 
 from txdav.caldav.datastore.test.common import CommonTests
 from txdav.carddav.datastore.test.common import CommonTests as ABCommonTests
 from txdav.common.datastore.file import CommonDataStore
-
+from txdav.common.datastore.sql_tables import schema
+from txdav.common.datastore.test.util import SQLStoreBuilder
 from txdav.common.datastore.test.util import theStoreBuilder, \
     populateCalendarsFrom, StubNotifierFactory, resetCalendarMD5s, \
     populateAddressBooksFrom, resetAddressBookMD5s, deriveValue, \
     withSpecialValue
-
-from txdav.common.datastore.test.util import SQLStoreBuilder
 from txdav.common.datastore.upgrade.migrate import UpgradeToDatabaseService, \
     StoreSpawnerService, swapAMP
 
+import copy
 
 
+
 class CreateStore(Command):
     """
     Create a store in a subprocess.
@@ -55,6 +56,7 @@
     arguments = [('delegateTo', String())]
 
 
+
 class PickleConfig(Command):
     """
     Unpickle some configuration in a subprocess.
@@ -317,6 +319,19 @@
         as well.
         """
 
+        # Need to tweak config and settings to setup dropbox to work
+        self.patch(config, "EnableDropBox", True)
+        self.patch(config, "EnableManagedAttachments", False)
+        self.sqlStore.enableManagedAttachments = False
+
+        txn = self.sqlStore.newTransaction()
+        cs = schema.CALENDARSERVER
+        yield Delete(
+            From=cs,
+            Where=cs.NAME == "MANAGED-ATTACHMENTS"
+        ).on(txn)
+        yield txn.commit()
+
         txn = self.fileStore.newTransaction()
         committed = []
         def maybeCommit():
@@ -409,4 +424,3 @@
             self.fileStore, self.sqlStore, self.stubService,
             parallel=2, spawner=StubSpawner()
         )
-
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130212/db7ff6f5/attachment-0001.html>


More information about the calendarserver-changes mailing list