[CalendarServer-changes] [11003] CalendarServer/trunk/calendarserver/tools
source_changes at macosforge.org
source_changes at macosforge.org
Fri Apr 5 11:33:30 PDT 2013
Revision: 11003
http://trac.calendarserver.org//changeset/11003
Author: cdaboo at apple.com
Date: 2013-04-05 11:33:30 -0700 (Fri, 05 Apr 2013)
Log Message:
-----------
Add option to detect location/resource calendar data with an invalid organizer.
Modified Paths:
--------------
CalendarServer/trunk/calendarserver/tools/calverify.py
CalendarServer/trunk/calendarserver/tools/test/test_calverify.py
Modified: CalendarServer/trunk/calendarserver/tools/calverify.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/calverify.py 2013-04-05 00:36:48 UTC (rev 11002)
+++ CalendarServer/trunk/calendarserver/tools/calverify.py 2013-04-05 18:33:30 UTC (rev 11003)
@@ -17,8 +17,6 @@
##
from __future__ import print_function
-from twistedcaldav.directory.directory import DirectoryService
-from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
"""
This tool scans the calendar store to analyze organizer/attendee event
@@ -42,30 +40,39 @@
"""
+from calendarserver.tools.cmdline import utilityMain
+
from calendarserver.tools import tables
-from calendarserver.tools.cmdline import utilityMain
from calendarserver.tools.util import getDirectory
+
from pycalendar import definitions
from pycalendar.calendar import PyCalendar
from pycalendar.datetime import PyCalendarDateTime
from pycalendar.exceptions import PyCalendarError
from pycalendar.period import PyCalendarPeriod
from pycalendar.timezone import PyCalendarTimezone
+
from twext.enterprise.dal.syntax import Select, Parameter, Count
+
from twisted.application.service import Service
from twisted.internet.defer import inlineCallbacks, returnValue
from twisted.python import log, usage
from twisted.python.usage import Options
+
from twistedcaldav import caldavxml
+from twistedcaldav.datafilters.peruserdata import PerUserDataFilter
from twistedcaldav.dateops import pyCalendarTodatetime
+from twistedcaldav.directory.directory import DirectoryService
from twistedcaldav.ical import Component, ignoredComponents, \
InvalidICalendarDataError, Property
from twistedcaldav.scheduling.itip import iTipGenerator
from twistedcaldav.stdconfig import DEFAULT_CONFIG_FILE
from twistedcaldav.util import normalizationLookup
+
from txdav.base.propertystore.base import PropertyName
from txdav.common.datastore.sql_tables import schema, _BIND_MODE_OWN
from txdav.common.icommondatastore import InternalDataStoreError
+
import base64
import collections
import sys
@@ -195,7 +202,7 @@
if not hasattr(Component, "maxAlarmCounts"):
Component.hasDuplicateAlarms = new_hasDuplicateAlarms
-VERSION = "9"
+VERSION = "10"
def printusage(e=None):
if e:
@@ -228,6 +235,7 @@
--missing : display orphaned calendar homes - can be used.
with either --ical or --mismatch.
--double : detect double-bookings.
+--dark-purge : purge room/resource events with invalid organizer
--nuke PATH|RID : remove specific calendar resources - can
only be used by itself. PATH is the full
@@ -293,6 +301,7 @@
['mismatch', 's', "Detect organizer/attendee mismatches."],
['missing', 'm', "Show 'orphaned' homes."],
['double', 'd', "Detect double-bookings."],
+ ['dark-purge', 'p', "Purge room/resource events with invalid organizer."],
['fix', 'x', "Fix problems."],
['verbose', 'v', "Verbose logging."],
['details', 'V', "Detailed logging."],
@@ -564,7 +573,32 @@
cb = schema.CALENDAR_BIND
ch = schema.CALENDAR_HOME
tr = schema.TIME_RANGE
+ kwds = {
+ "Start" : pyCalendarTodatetime(start),
+ "Max" : pyCalendarTodatetime(PyCalendarDateTime(1900, 1, 1, 0, 0, 0)),
+ "UUID" : uuid,
+ }
+ rows = (yield Select(
+ [ch.OWNER_UID, co.RESOURCE_ID, co.ICALENDAR_UID, cb.CALENDAR_RESOURCE_NAME, co.MD5, co.ORGANIZER, co.CREATED, co.MODIFIED],
+ From=ch.join(
+ cb, type="inner", on=(ch.RESOURCE_ID == cb.CALENDAR_HOME_RESOURCE_ID)).join(
+ co, type="inner", on=(cb.CALENDAR_RESOURCE_ID == co.CALENDAR_RESOURCE_ID).And(
+ cb.BIND_MODE == _BIND_MODE_OWN).And(
+ cb.CALENDAR_RESOURCE_NAME != "inbox")).join(
+ tr, type="left", on=(co.RESOURCE_ID == tr.CALENDAR_OBJECT_RESOURCE_ID)),
+ Where=(ch.OWNER_UID == Parameter("UUID")).And((tr.START_DATE >= Parameter("Start")).Or(co.RECURRANCE_MAX <= Parameter("Start"))),
+ GroupBy=(ch.OWNER_UID, co.RESOURCE_ID, co.ICALENDAR_UID, cb.CALENDAR_RESOURCE_NAME, co.MD5, co.ORGANIZER, co.CREATED, co.MODIFIED,),
+ ).on(self.txn, **kwds))
+ returnValue(tuple(rows))
+
+ @inlineCallbacks
+ def getAllResourceInfoTimeRangeWithUUIDForAllUID(self, start, uuid):
+ co = schema.CALENDAR_OBJECT
+ cb = schema.CALENDAR_BIND
+ ch = schema.CALENDAR_HOME
+ tr = schema.TIME_RANGE
+
cojoin = (cb.CALENDAR_RESOURCE_ID == co.CALENDAR_RESOURCE_ID).And(
cb.BIND_MODE == _BIND_MODE_OWN).And(
cb.CALENDAR_RESOURCE_NAME != "inbox")
@@ -1370,8 +1404,8 @@
rows = yield self.getAllResourceInfoWithUID(self.options["uid"])
descriptor = "getAllResourceInfoWithUID"
elif self.options["uuid"]:
- rows = yield self.getAllResourceInfoTimeRangeWithUUID(self.start, self.options["uuid"])
- descriptor = "getAllResourceInfoTimeRangeWithUUID"
+ rows = yield self.getAllResourceInfoTimeRangeWithUUIDForAllUID(self.start, self.options["uuid"])
+ descriptor = "getAllResourceInfoTimeRangeWithUUIDForAllUID"
self.options["uuid"] = None
else:
rows = yield self.getAllResourceInfoTimeRange(self.start)
@@ -2357,6 +2391,227 @@
+class DarkPurgeService(CalVerifyService):
+ """
+ Service which detects room/resource events that have an invalid organizer.
+ """
+
+ def title(self):
+ return "Dark Purge Service"
+
+
+ @inlineCallbacks
+ def doAction(self):
+
+ self.output.write("\n---- Scanning calendar data ----\n")
+
+ self.tzid = PyCalendarTimezone(tzid=self.options["tzid"] if self.options["tzid"] else "America/Los_Angeles")
+ self.now = PyCalendarDateTime.getNowUTC()
+ self.start = self.options["start"] if "start" in self.options else PyCalendarDateTime.getToday()
+ self.start.setDateOnly(False)
+ self.start.setTimezone(self.tzid)
+ self.fix = self.options["fix"]
+
+ if self.options["verbose"] and self.options["summary"]:
+ ot = time.time()
+
+ # Check loop over uuid
+ UUIDDetails = collections.namedtuple("UUIDDetails", ("uuid", "rname", "purged",))
+ self.uuid_details = []
+ if len(self.options["uuid"]) != 36:
+ self.txn = self.store.newTransaction()
+ if self.options["uuid"]:
+ homes = yield self.getMatchingHomeUIDs(self.options["uuid"])
+ else:
+ homes = yield self.getAllHomeUIDs()
+ yield self.txn.commit()
+ self.txn = None
+ uuids = []
+ if self.options["verbose"]:
+ self.output.write("%d uuids to check\n" % (len(homes,)))
+ for uuid in sorted(homes):
+ record = self.directoryService().recordWithGUID(uuid)
+ if record is not None and record.recordType in (DirectoryService.recordType_locations, DirectoryService.recordType_resources):
+ uuids.append(uuid)
+ else:
+ uuids = [self.options["uuid"], ]
+ if self.options["verbose"]:
+ self.output.write("%d uuids to scan\n" % (len(uuids,)))
+
+ count = 0
+ for uuid in uuids:
+ self.results = {}
+ self.summary = []
+ self.total = 0
+ count += 1
+
+ record = self.directoryService().recordWithGUID(uuid)
+ if record is None:
+ continue
+ if not record.thisServer() or not record.enabledForCalendaring:
+ continue
+
+ rname = record.fullName
+
+ if len(uuids) > 1 and not self.options["summary"]:
+ self.output.write("\n\n-----------------------------\n")
+
+ self.txn = self.store.newTransaction()
+
+ if self.options["verbose"]:
+ t = time.time()
+ rows = yield self.getAllResourceInfoTimeRangeWithUUID(self.start, uuid)
+ descriptor = "getAllResourceInfoTimeRangeWithUUID"
+
+ yield self.txn.commit()
+ self.txn = None
+
+ if self.options["verbose"]:
+ if not self.options["summary"]:
+ self.output.write("%s time: %.1fs\n" % (descriptor, time.time() - t,))
+ else:
+ self.output.write("%s (%d/%d)" % (uuid, count, len(uuids),))
+ self.output.flush()
+
+ self.total = len(rows)
+ if not self.options["summary"]:
+ self.logResult("UUID to process", uuid)
+ self.logResult("Record name", rname)
+ self.addSummaryBreak()
+ self.logResult("Number of events to process", self.total)
+
+ if rows:
+ if not self.options["summary"]:
+ self.addSummaryBreak()
+ purged = yield self.darkPurge(rows, uuid)
+ else:
+ purged = False
+
+ self.uuid_details.append(UUIDDetails(uuid, rname, purged))
+
+ if not self.options["summary"]:
+ self.printSummary()
+ else:
+ self.output.write(" - %s\n" % ("Dark Events" if purged else "OK",))
+ self.output.flush()
+
+ if count == 0:
+ self.output.write("Nothing to scan\n")
+
+ if self.options["summary"]:
+ table = tables.Table()
+ table.addHeader(("GUID", "Name", "RID", "UID", "Organizer",))
+ purged = 0
+ for item in sorted(self.uuid_details):
+ if not item.purged:
+ continue
+ uuid = item.uuid
+ rname = item.rname
+ for detail in item.purged:
+ table.addRow((
+ uuid,
+ rname,
+ detail.resid,
+ detail.uid,
+ detail.organizer,
+ ))
+ uuid = ""
+ rname = ""
+ purged += 1
+ table.addFooter(("Total", "%d" % (purged,), "", "", "",))
+ self.output.write("\n")
+ table.printTable(os=self.output)
+
+ if self.options["verbose"]:
+ self.output.write("%s time: %.1fs\n" % ("Summary", time.time() - ot,))
+
+
+ @inlineCallbacks
+ def darkPurge(self, rows, uuid):
+ """
+ Check each calendar resource by looking at any ORGANIER property value and verifying it is valid.
+ """
+
+ if not self.options["summary"]:
+ self.output.write("\n---- Checking for dark events ----\n")
+ self.txn = self.store.newTransaction()
+
+ if self.options["verbose"]:
+ t = time.time()
+
+ Details = collections.namedtuple("Details", ("resid", "uid", "organizer",))
+
+ count = 0
+ total = len(rows)
+ details = []
+ fixed = 0
+ rjust = 10
+ for resid in rows:
+ resid = resid[1]
+ caldata = yield self.getCalendar(resid, self.fix)
+ if caldata is None:
+ if self.parseError:
+ returnValue((False, self.parseError))
+ else:
+ returnValue((True, "Nothing to scan"))
+
+ cal = Component(None, pycalendar=caldata)
+ uid = cal.resourceUID()
+ organizer = cal.getOrganizer()
+ if organizer is not None:
+ principal = self.directoryService().principalForCalendarUserAddress(organizer)
+ if principal is None or not principal.calendarsEnabled():
+ details.append(Details(resid, uid, organizer,))
+ if self.fix:
+ yield self.removeEvent(resid)
+ fixed += 1
+
+ if self.options["verbose"] and not self.options["summary"]:
+ if count == 1:
+ self.output.write("Current".rjust(rjust) + "Total".rjust(rjust) + "Complete".rjust(rjust) + "\n")
+ if divmod(count, 100)[1] == 0:
+ self.output.write((
+ "\r" +
+ ("%s" % count).rjust(rjust) +
+ ("%s" % total).rjust(rjust) +
+ ("%d%%" % safePercent(count, total)).rjust(rjust)
+ ).ljust(80))
+ self.output.flush()
+
+ # To avoid holding locks on all the rows scanned, commit every 100 resources
+ if divmod(count, 100)[1] == 0:
+ yield self.txn.commit()
+ self.txn = self.store.newTransaction()
+
+ yield self.txn.commit()
+ self.txn = None
+ if self.options["verbose"] and not self.options["summary"]:
+ self.output.write((
+ "\r" +
+ ("%s" % count).rjust(rjust) +
+ ("%s" % total).rjust(rjust) +
+ ("%d%%" % safePercent(count, total)).rjust(rjust)
+ ).ljust(80) + "\n")
+
+ # Print table of results
+ if not self.options["summary"]:
+ self.logResult("Number of dark events", len(details))
+
+ self.results["Dark Events"] = details
+ if self.fix:
+ self.results["Fix dark events"] = fixed
+
+ if self.options["verbose"] and not self.options["summary"]:
+ diff_time = time.time() - t
+ self.output.write("Time: %.2f s Average: %.1f ms/resource\n" % (
+ diff_time,
+ safePercent(diff_time, total, 1000.0),
+ ))
+
+ returnValue(details)
+
+
+
def main(argv=sys.argv, stderr=sys.stderr, reactor=None):
if reactor is None:
@@ -2387,6 +2642,11 @@
return SchedulingMismatchService(store, options, output, reactor, config)
elif options["double"]:
return DoubleBookingService(store, options, output, reactor, config)
+ elif options["dark-purge"]:
+ return DarkPurgeService(store, options, output, reactor, config)
+ else:
+ printusage("Invalid operation")
+ sys.exit(1)
utilityMain(options['config'], makeService, reactor)
Modified: CalendarServer/trunk/calendarserver/tools/test/test_calverify.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_calverify.py 2013-04-05 00:36:48 UTC (rev 11002)
+++ CalendarServer/trunk/calendarserver/tools/test/test_calverify.py 2013-04-05 18:33:30 UTC (rev 11003)
@@ -19,10 +19,11 @@
Tests for calendarserver.tools.calverify
"""
+from calendarserver.tools.calverify import BadDataService, \
+ SchedulingMismatchService, DoubleBookingService, DarkPurgeService
+
from StringIO import StringIO
from calendarserver.tap.util import getRootResource
-from calendarserver.tools.calverify import BadDataService, \
- SchedulingMismatchService, DoubleBookingService
from pycalendar.datetime import PyCalendarDateTime
from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks, returnValue
@@ -2525,67 +2526,214 @@
self.assertEqual(sync_token_oldl1, sync_token_newl1)
- def test_instance(self):
- """
- CalVerifyService.doScan without fix for mismatches. Make sure it detects
- as much as it can. Make sure sync-token is not changed.
- """
- s = """BEGIN:VCALENDAR
+class CalVerifyDarkPurge(CalVerifyMismatchTestsBase):
+ """
+ Tests calverify for events.
+ """
+
+ # No organizer
+ INVITE_NO_ORGANIZER_ICS = """BEGIN:VCALENDAR
VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
CALSCALE:GREGORIAN
-PRODID:-//CALENDARSERVER.ORG//NONSGML Version 1//EN
-BEGIN:VTIMEZONE
-TZID:America/Los_Angeles
-BEGIN:DAYLIGHT
-DTSTART:20070311T020000
-RRULE:FREQ=YEARLY;BYDAY=2SU;BYMONTH=3
-TZNAME:PDT
-TZOFFSETFROM:-0800
-TZOFFSETTO:-0700
-END:DAYLIGHT
-BEGIN:STANDARD
-DTSTART:20071104T020000
-RRULE:FREQ=YEARLY;BYDAY=1SU;BYMONTH=11
-TZNAME:PST
-TZOFFSETFROM:-0700
-TZOFFSETTO:-0800
-END:STANDARD
-END:VTIMEZONE
BEGIN:VEVENT
-UID:4760FF93-C7F8-4EB0-B3E8-0B22A96DB1BC
-DTSTART;TZID=America/Los_Angeles:20130221T170000
-DTEND;TZID=America/Los_Angeles:20130221T180000
-ATTENDEE;CN=Casa Blanca APPLE EMP ONLY (12) DA03 4th;CUTYPE=ROOM;PARTSTAT=
- ACCEPTED;ROLE=REQ-PARTICIPANT:urn:uuid:366CC7BE-FEF7-4FFF-B713-6B883538A24
- 9
-ATTENDEE;CN=Mark Chu;CUTYPE=INDIVIDUAL;EMAIL=markchu at apple.com;PARTSTAT=AC
- CEPTED;ROLE=REQ-PARTICIPANT:urn:uuid:46F9D5D9-08E8-4987-9636-CC796F4093C6
-ATTENDEE;CN=Kristie Phan;CUTYPE=INDIVIDUAL;EMAIL=kristie_phan at apple.com;PA
- RTSTAT=ACCEPTED:urn:uuid:97E8720F-4364-DBEC-6721-123E9A92B980
-CREATED:20130220T200530Z
-DTSTAMP:20130222T002246Z
-EXDATE:20130228T010000Z
-EXDATE:20130314T000000Z
-EXDATE:20130321T000000Z
-EXDATE:20130327T000000Z
-EXDATE:20130328T000000Z
-EXDATE:20130403T000000Z
-LOCATION:Casa Blanca APPLE EMP ONLY (12) DA03 4th
-ORGANIZER;CN=Kristie Phan;EMAIL=kristie_phan at apple.com;SCHEDULE-STATUS=1.2
- :urn:uuid:97E8720F-4364-DBEC-6721-123E9A92B980
-RRULE:FREQ=WEEKLY;BYDAY=MO,TU,WE,TH,FR;WKST=SU
-SEQUENCE:13
-SUMMARY:ESD Daily Meeting
+CREATED:20100303T181216Z
+UID:INVITE_NO_ORGANIZER_ICS
+TRANSP:OPAQUE
+SUMMARY:INVITE_NO_ORGANIZER_ICS
+DTSTART:%(year)s%(month)02d07T100000Z
+DURATION:PT1H
+DTSTAMP:20100303T181220Z
+SEQUENCE:2
END:VEVENT
END:VCALENDAR
-"""
- from twistedcaldav.ical import Component
- c = Component.fromString(s)
- start = PyCalendarDateTime.getToday()
- start.setDateOnly(False)
- end = start.duplicate()
- end.offsetDay(30)
- config.MaxAllowedInstances = 3000
- i = c.expandTimeRanges(end, start, ignoreInvalidInstances=True)
- print(i)
+""".replace("\n", "\r\n") % {"year": nowYear, "month": nowMonth}
+
+ # Valid organizer
+ INVITE_VALID_ORGANIZER_ICS = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+CREATED:20100303T181216Z
+UID:INVITE_VALID_ORGANIZER_ICS
+TRANSP:OPAQUE
+SUMMARY:INVITE_VALID_ORGANIZER_ICS
+DTSTART:%(year)s%(month)02d08T100000Z
+DURATION:PT1H
+DTSTAMP:20100303T181220Z
+SEQUENCE:2
+ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
+ATTENDEE:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0
+ATTENDEE:urn:uuid:75EA36BE-F71B-40F9-81F9-CF59BF40CA8F
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n") % {"year": nowYear, "month": nowMonth}
+
+ # Invalid organizer #1
+ INVITE_INVALID_ORGANIZER_1_ICS = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+CREATED:20100303T181216Z
+UID:INVITE_INVALID_ORGANIZER_1_ICS
+TRANSP:OPAQUE
+SUMMARY:INVITE_INVALID_ORGANIZER_1_ICS
+DTSTART:%(year)s%(month)02d09T100000Z
+DURATION:PT1H
+DTSTAMP:20100303T181220Z
+SEQUENCE:2
+ORGANIZER:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0-1
+ATTENDEE:urn:uuid:D46F3D71-04B7-43C2-A7B6-6F92F92E61D0-1
+ATTENDEE:urn:uuid:75EA36BE-F71B-40F9-81F9-CF59BF40CA8F
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n") % {"year": nowYear, "month": nowMonth}
+
+ # Invalid organizer #2
+ INVITE_INVALID_ORGANIZER_2_ICS = """BEGIN:VCALENDAR
+VERSION:2.0
+PRODID:-//Apple Inc.//iCal 4.0.1//EN
+CALSCALE:GREGORIAN
+BEGIN:VEVENT
+CREATED:20100303T181216Z
+UID:INVITE_INVALID_ORGANIZER_2_ICS
+TRANSP:OPAQUE
+SUMMARY:INVITE_INVALID_ORGANIZER_2_ICS
+DTSTART:%(year)s%(month)02d10T100000Z
+DURATION:PT1H
+DTSTAMP:20100303T181220Z
+SEQUENCE:2
+ORGANIZER:mailto:foobar at example.com
+ATTENDEE:mailto:foobar at example.com
+ATTENDEE:urn:uuid:75EA36BE-F71B-40F9-81F9-CF59BF40CA8F
+END:VEVENT
+END:VCALENDAR
+""".replace("\n", "\r\n") % {"year": nowYear, "month": nowMonth}
+
+ allEvents = {
+ "invite1.ics" : (INVITE_NO_ORGANIZER_ICS, CalVerifyMismatchTestsBase.metadata,),
+ "invite2.ics" : (INVITE_VALID_ORGANIZER_ICS, CalVerifyMismatchTestsBase.metadata,),
+ "invite3.ics" : (INVITE_INVALID_ORGANIZER_1_ICS, CalVerifyMismatchTestsBase.metadata,),
+ "invite4.ics" : (INVITE_INVALID_ORGANIZER_2_ICS, CalVerifyMismatchTestsBase.metadata,),
+ }
+
+ requirements = {
+ CalVerifyMismatchTestsBase.uuid1 : {
+ "calendar" : {},
+ "inbox" : {},
+ },
+ CalVerifyMismatchTestsBase.uuid2 : {
+ "calendar" : {},
+ "inbox" : {},
+ },
+ CalVerifyMismatchTestsBase.uuid3 : {
+ "calendar" : {},
+ "inbox" : {},
+ },
+ CalVerifyMismatchTestsBase.uuidl1 : {
+ "calendar" : allEvents,
+ "inbox" : {},
+ },
+ }
+
+ @inlineCallbacks
+ def test_scanDarkEvents(self):
+ """
+ CalVerifyService.doScan without fix for dark events. Make sure it detects
+ as much as it can. Make sure sync-token is not changed.
+ """
+
+ sync_token_oldl1 = (yield (yield self.calendarUnderTest(self.uuidl1)).syncToken())
+ self.commit()
+
+ options = {
+ "ical": False,
+ "badcua": False,
+ "mismatch": False,
+ "nobase64": False,
+ "double": True,
+ "dark-purge": False,
+ "fix": False,
+ "verbose": False,
+ "details": False,
+ "summary": False,
+ "days": 365,
+ "uid": "",
+ "uuid": self.uuidl1,
+ "tzid": "utc",
+ "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+ }
+ output = StringIO()
+ calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
+ yield calverify.doAction()
+
+ self.assertEqual(calverify.results["Number of events to process"], len(self.requirements[CalVerifyMismatchTestsBase.uuidl1]["calendar"]))
+ self.assertEqual(
+ sorted([i.uid for i in calverify.results["Dark Events"]]),
+ ["INVITE_INVALID_ORGANIZER_1_ICS", "INVITE_INVALID_ORGANIZER_2_ICS", ]
+ )
+ self.assertEqual(calverify.results["Number of dark events"], 2)
+ self.assertTrue("Fix dark events" not in calverify.results)
+ self.assertTrue("Fix remove" not in calverify.results)
+
+ sync_token_newl1 = (yield (yield self.calendarUnderTest(self.uuidl1)).syncToken())
+ self.assertEqual(sync_token_oldl1, sync_token_newl1)
+
+
+ @inlineCallbacks
+ def test_fixDarkEvents(self):
+ """
+ CalVerifyService.doScan with fix for dark events. Make sure it detects
+ as much as it can. Make sure sync-token is changed.
+ """
+
+ sync_token_oldl1 = (yield (yield self.calendarUnderTest(self.uuidl1)).syncToken())
+ self.commit()
+
+ options = {
+ "ical": False,
+ "badcua": False,
+ "mismatch": False,
+ "nobase64": False,
+ "double": True,
+ "dark-purge": False,
+ "fix": True,
+ "verbose": False,
+ "details": False,
+ "summary": False,
+ "days": 365,
+ "uid": "",
+ "uuid": self.uuidl1,
+ "tzid": "utc",
+ "start": PyCalendarDateTime(nowYear, 1, 1, 0, 0, 0),
+ }
+ output = StringIO()
+ calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
+ yield calverify.doAction()
+
+ self.assertEqual(calverify.results["Number of events to process"], len(self.requirements[CalVerifyMismatchTestsBase.uuidl1]["calendar"]))
+ self.assertEqual(
+ sorted([i.uid for i in calverify.results["Dark Events"]]),
+ ["INVITE_INVALID_ORGANIZER_1_ICS", "INVITE_INVALID_ORGANIZER_2_ICS", ]
+ )
+ self.assertEqual(calverify.results["Number of dark events"], 2)
+ self.assertEqual(calverify.results["Fix dark events"], 2)
+ self.assertTrue("Fix remove" in calverify.results)
+
+ sync_token_newl1 = (yield (yield self.calendarUnderTest(self.uuidl1)).syncToken())
+ self.assertNotEqual(sync_token_oldl1, sync_token_newl1)
+
+ # Re-scan after changes to make sure there are no errors
+ self.commit()
+ options["fix"] = False
+ options["uuid"] = self.uuidl1
+ calverify = DarkPurgeService(self._sqlCalendarStore, options, output, reactor, config)
+ yield calverify.doAction()
+
+ self.assertEqual(calverify.results["Number of events to process"], 2)
+ self.assertEqual(len(calverify.results["Dark Events"]), 0)
+ self.assertTrue("Fix dark events" not in calverify.results)
+ self.assertTrue("Fix remove" not in calverify.results)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20130405/10dedd1a/attachment-0001.html>
More information about the calendarserver-changes
mailing list