[CalendarServer-changes] [13468] CalendarServer/trunk

source_changes at macosforge.org source_changes at macosforge.org
Fri May 9 16:38:16 PDT 2014


Revision: 13468
          http://trac.calendarserver.org//changeset/13468
Author:   sagen at apple.com
Date:     2014-05-09 16:38:16 -0700 (Fri, 09 May 2014)
Log Message:
-----------
Refactor the way unit tests create stores and directories; unit test directory records now come from txdav/common/datastore/test/accounts/*xml; also ServersDB is not a global, but instead lives in the directory service.

Modified Paths:
--------------
    CalendarServer/trunk/calendarserver/tap/caldav.py
    CalendarServer/trunk/calendarserver/tools/test/test_gateway.py
    CalendarServer/trunk/calendarserver/tools/test/test_resources.py
    CalendarServer/trunk/twistedcaldav/stdconfig.py
    CalendarServer/trunk/twistedcaldav/test/util.py
    CalendarServer/trunk/txdav/caldav/datastore/scheduling/caldav/test/test_scheduler.py
    CalendarServer/trunk/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py
    CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/localservers.py
    CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/scheduler.py
    CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py
    CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_resource.py
    CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_cuaddress.py
    CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_freebusy.py
    CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_implicit.py
    CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_utils.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_implicit.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_sql_sharing.py
    CalendarServer/trunk/txdav/caldav/datastore/test/test_util.py
    CalendarServer/trunk/txdav/caldav/datastore/test/util.py
    CalendarServer/trunk/txdav/carddav/datastore/test/common.py
    CalendarServer/trunk/txdav/carddav/datastore/test/test_file.py
    CalendarServer/trunk/txdav/carddav/datastore/test/test_sql.py
    CalendarServer/trunk/txdav/carddav/datastore/test/test_sql_sharing.py
    CalendarServer/trunk/txdav/common/datastore/podding/resource.py
    CalendarServer/trunk/txdav/common/datastore/podding/test/test_conduit.py
    CalendarServer/trunk/txdav/common/datastore/podding/test/test_external_home.py
    CalendarServer/trunk/txdav/common/datastore/podding/test/test_resource.py
    CalendarServer/trunk/txdav/common/datastore/podding/test/util.py
    CalendarServer/trunk/txdav/common/datastore/test/test_sql.py
    CalendarServer/trunk/txdav/common/datastore/test/util.py
    CalendarServer/trunk/txdav/common/datastore/upgrade/test/test_migrate.py
    CalendarServer/trunk/txdav/common/datastore/work/test/test_inbox_cleanup.py
    CalendarServer/trunk/txdav/common/datastore/work/test/test_revision_cleanup.py
    CalendarServer/trunk/txdav/dps/test/test_client.py
    CalendarServer/trunk/txdav/who/directory.py
    CalendarServer/trunk/txdav/who/test/test_group_attendees.py
    CalendarServer/trunk/txdav/who/test/test_util.py
    CalendarServer/trunk/txdav/who/util.py

Added Paths:
-----------
    CalendarServer/trunk/txdav/common/datastore/test/accounts/
    CalendarServer/trunk/txdav/common/datastore/test/accounts/accounts.xml
    CalendarServer/trunk/txdav/common/datastore/test/accounts/augments.xml
    CalendarServer/trunk/txdav/common/datastore/test/accounts/generate_test_accounts.py
    CalendarServer/trunk/txdav/common/datastore/test/accounts/proxies.xml
    CalendarServer/trunk/txdav/common/datastore/test/accounts/resources.xml
    CalendarServer/trunk/txdav/who/test/support.py

Removed Paths:
-------------
    CalendarServer/trunk/twistedcaldav/directory/test/accounts.xml
    CalendarServer/trunk/twistedcaldav/directory/test/augments.xml
    CalendarServer/trunk/twistedcaldav/directory/test/proxies.xml
    CalendarServer/trunk/twistedcaldav/directory/test/resources.xml

Modified: CalendarServer/trunk/calendarserver/tap/caldav.py
===================================================================
--- CalendarServer/trunk/calendarserver/tap/caldav.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/calendarserver/tap/caldav.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -79,6 +79,9 @@
 
 from txdav.caldav.datastore.scheduling.imip.inbound import MailRetriever
 from txdav.caldav.datastore.scheduling.imip.inbound import scheduleNextMailPoll
+from txdav.caldav.datastore.scheduling.ischedule.localservers import (
+    buildServersDB
+)
 from txdav.common.datastore.upgrade.migrate import UpgradeToDatabaseStep
 from txdav.common.datastore.upgrade.sql.upgrade import (
     UpgradeDatabaseCalendarDataStep, UpgradeDatabaseOtherStep,
@@ -126,7 +129,6 @@
 )
 from twisted.application.strports import service as strPortsService
 from txdav.dps.server import DirectoryProxyAMPFactory
-
 try:
     from calendarserver.version import version
 except ImportError:
@@ -870,6 +872,8 @@
         """
         pool, txnFactory = getDBPool(config)
         directory = DirectoryProxyClientService(config.DirectoryRealmName)
+        if config.Servers.Enabled:
+            directory.setServersDB(buildServersDB(config.Servers.MaxClients))
         store = storeFromConfig(config, txnFactory, directory)
         logObserver = AMPCommonAccessLoggingObserver()
         result = self.requestProcessingService(options, store, logObserver)
@@ -1478,7 +1482,15 @@
                     # FIXME: right now the store passed *to* the directory is the
                     # calendar/contacts data store, but for a multi-server deployment
                     # it will need its own separate store.
-                    store.setDirectoryService(directoryFromConfig(config, store=store))
+                    if config.Servers.Enabled:
+                        serversDB = buildServersDB(config.Servers.MaxClients)
+                    else:
+                        serversDB = None
+                    store.setDirectoryService(
+                        directoryFromConfig(
+                            config, store=store, serversDB=serversDB
+                        )
+                    )
 
                 pps = PreProcessingService(
                     createMainService, cp, store, logObserver, storageService
@@ -1897,6 +1909,8 @@
             directory = DirectoryProxyClientService(
                 config.DirectoryRealmName
             )
+            if config.Servers.Enabled:
+                directory.setServersDB(buildServersDB(config.Servers.MaxClients))
         else:
             # If the master is to act as the DPS server:
             directory = None

Modified: CalendarServer/trunk/calendarserver/tools/test/test_gateway.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_gateway.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/calendarserver/tools/test/test_gateway.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -17,26 +17,30 @@
 
 import os
 from plistlib import readPlistFromString
+import plistlib
 import xml
 
 from twext.python.filepath import CachingFilePath as FilePath
 from twisted.internet import reactor
 from twisted.internet.defer import inlineCallbacks, Deferred, returnValue
-
+from twisted.trial.unittest import TestCase
+from twistedcaldav import memcacher
 from twistedcaldav.config import config
-from twistedcaldav.test.util import StoreTestCase, CapturingProcessProtocol
-import plistlib
 from twistedcaldav.memcacheclient import ClientFactory
-from twistedcaldav import memcacher
+from twistedcaldav.test.util import CapturingProcessProtocol
+from txdav.common.datastore.test.util import (
+    theStoreBuilder, StubNotifierFactory
+)
 from txdav.who.idirectory import AutoScheduleMode
+from txdav.who.util import directoryFromConfig
 
 
-class RunCommandTestCase(StoreTestCase):
 
-    def configure(self):
-        """
-        Override the standard StoreTestCase configuration
-        """
+class RunCommandTestCase(TestCase):
+
+    @inlineCallbacks
+    def setUp(self):
+
         self.serverRoot = self.mktemp()
         os.mkdir(self.serverRoot)
         absoluteServerRoot = os.path.abspath(self.serverRoot)
@@ -62,7 +66,6 @@
             os.makedirs(runRoot)
 
         config.reset()
-        self.configInit()
 
         testRoot = os.path.join(os.path.dirname(__file__), "gateway")
         templateName = os.path.join(testRoot, "caldavd.plist")
@@ -134,11 +137,9 @@
         copyAugmentFile = FilePath(os.path.join(config.DataRoot, "augments.xml"))
         origAugmentFile.copyTo(copyAugmentFile)
 
-        # # Make sure trial puts the reactor in the right state, by letting it
-        # # run one reactor iteration.  (Ignore me, please.)
-        # d = Deferred()
-        # reactor.callLater(0, d.callback, True)
-        # return d
+        self.notifierFactory = StubNotifierFactory()
+        self.store = yield theStoreBuilder.buildStore(self, self.notifierFactory)
+        self.directory = directoryFromConfig(config, self.store)
 
 
     @inlineCallbacks

Modified: CalendarServer/trunk/calendarserver/tools/test/test_resources.py
===================================================================
--- CalendarServer/trunk/calendarserver/tools/test/test_resources.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/calendarserver/tools/test/test_resources.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -19,7 +19,7 @@
 from twisted.internet.defer import inlineCallbacks
 from calendarserver.tools.resources import migrateResources
 from twistedcaldav.test.util import StoreTestCase
-from txdav.who.util import InMemoryDirectoryService
+from txdav.who.test.support import InMemoryDirectoryService
 from twext.who.directory import DirectoryRecord
 from txdav.who.idirectory import RecordType as CalRecordType
 from txdav.who.directory import CalendarDirectoryRecordMixin

Deleted: CalendarServer/trunk/twistedcaldav/directory/test/accounts.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/accounts.xml	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/twistedcaldav/directory/test/accounts.xml	2014-05-09 23:38:16 UTC (rev 13468)
@@ -1,389 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2006-2014 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
- -->
-
-<!DOCTYPE accounts SYSTEM "../../../conf/auth/accounts.dtd">
-<directory realm="Test">
-  <record type="user">
-    <uid>D11F03A0-97EA-48AF-9A6C-FAC7F3975766</uid>
-    <short-name>admin</short-name>
-    <password>nimda</password>
-    <full-name>Administrators</full-name>
-  </record>
-  <record type="user">
-    <uid>6423F94A-6B76-4A3A-815B-D52CFD77935D</uid>
-    <guid>6423F94A-6B76-4A3A-815B-D52CFD77935D</guid>
-    <short-name>wsanchez</short-name>
-    <password>zehcnasw</password>
-    <full-name>Wilfredo Sanchez</full-name>
-    <email>wsanchez at example.com</email>
-  </record>
-  <record type="user">
-    <uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</uid>
-    <guid>5A985493-EE2C-4665-94CF-4DFEA3A89500</guid>
-    <short-name>cdaboo</short-name>
-    <password>oobadc</password>
-    <full-name>Cyrus Daboo</full-name>
-    <email>cdaboo at example.com</email>
-  </record>
-  <record type="user">
-    <uid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</uid>
-    <short-name>lecroy</short-name>
-    <password>yorcel</password>
-    <full-name>Chris Lecroy</full-name>
-    <email>lecroy at example.com</email>
-  </record>
-  <record type="user">
-    <uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
-    <short-name>dreid</short-name>
-    <password>dierd</password>
-    <full-name>David Reid</full-name>
-    <email>dreid at example.com</email>
-  </record>
-  <record type="user">
-    <uid>8E04787E-336D-41ED-A70B-D233AD0DCE6F</uid>
-    <guid>8E04787E-336D-41ED-A70B-D233AD0DCE6F</guid>
-    <short-name>doublequotes</short-name>
-    <password>setouqelbuod</password>
-    <full-name>Double "quotey" Quotes</full-name>
-    <email>doublequotes at example.com</email>
-  </record>
-  <record type="user">
-    <uid>37DB0C90-4DB1-4932-BC69-3DAB66F374F5</uid>
-    <guid>37DB0C90-4DB1-4932-BC69-3DAB66F374F5</guid>
-    <short-name>purgeuser</short-name>
-    <password>purgeuser</password>
-    <full-name>Purge User</full-name>
-    <email>purgeuser at example.com</email>
-  </record>
-  <record type="user">
-    <uid>home1</uid>
-    <short-name>home1</short-name>
-    <password>home1</password>
-    <full-name>Home One</full-name>
-    <email>home1 at example.com</email>
-  </record>
-  <record type="user">
-    <uid>home2</uid>
-    <short-name>home2</short-name>
-    <password>home2</password>
-    <full-name>Home Two</full-name>
-    <email>home2 at example.com</email>
-  </record>
-
-  <record type="user">
-    <uid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</uid>
-    <short-name>nocalendar</short-name>
-    <password>radnelacon</password>
-    <full-name>No Calendar</full-name>
-    <email>nocalendar at example.com</email>
-  </record>
-  <record type="user">
-    <uid>7423F94A-6B76-4A3A-815B-D52CFD77935D</uid>
-    <short-name>usera</short-name>
-    <password>a</password>
-    <full-name>a</full-name>
-    <email>a at example.com</email>
-  </record>
-  <record type="user">
-    <uid>8A985493-EE2C-4665-94CF-4DFEA3A89500</uid>
-    <short-name>userb</short-name>
-    <password>b</password>
-    <full-name>b</full-name>
-    <email>b at example.com</email>
-  </record>
-  <record type="user">
-    <uid>9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD2</uid>
-    <short-name>userc</short-name>
-    <password>c</password>
-    <full-name>c</full-name>
-    <email>c at example.com</email>
-  </record>
-  <record type="user">
-    <uid>9E1FFAC4-3CCD-45A1-8272-D161C92D2EEE</uid>
-    <short-name>usercalonly</short-name>
-    <password>a</password>
-    <full-name>a calonly</full-name>
-    <email>a-calonly at example.com</email>
-  </record>
-  <record type="user">
-    <uid>7678EC8A-A069-4E82-9066-7279C6718507</uid>
-    <short-name>useradbkonly</short-name>
-    <password>a</password>
-    <full-name>a adbkonly</full-name>
-    <email>a-adbkonly at example.com</email>
-  </record>
-  <record type="user">
-    <uid>320B73A1-46E2-4180-9563-782DFDBE1F63</uid>
-    <short-name>nonascii</short-name>
-    <short-name>nonascii佐藤</short-name>
-    <password>a</password>
-    <full-name>佐藤佐藤佐藤</full-name>
-    <email>nonascii at example.com</email>
-  </record>
-  <record type="user">
-    <uid>FC465590-E9E9-4746-ACE8-6C756A49FE4D</uid>
-    <short-name>delegator</short-name>
-    <password>a</password>
-    <full-name>Calendar Delegator</full-name>
-    <email>calendardelegator at example.com</email>
-  </record>
-  <record type="user">
-    <uid>EC465590-E9E9-4746-ACE8-6C756A49FE4D</uid>
-    <short-name>occasionaldelegate</short-name>
-    <password>a</password>
-    <full-name>Occasional Delegate</full-name>
-    <email>occasional at example.com</email>
-  </record>
-  <record type="user">
-    <uid>46D9D716-CBEE-490F-907A-66FA6C3767FF</uid>
-    <short-name>delegateviagroup</short-name>
-    <password>a</password>
-    <full-name>Delegate Via Group</full-name>
-    <email>delegateviagroup at example.com</email>
-  </record>
-  <record type="group">
-    <uid>00599DAF-3E75-42DD-9DB7-52617E79943F</uid>
-    <short-name>delegategroup</short-name>
-    <full-name>Delegate Group</full-name>
-    <member-uid>46D9D716-CBEE-490F-907A-66FA6C3767FF</member-uid>
-  </record>
-
-  <record type="user">
-    <uid>user01</uid>
-    <short-name>user01</short-name>
-    <password>user01</password>
-    <full-name>User 01</full-name>
-    <email>user01 at example.com</email>
-  </record>
-
-  <record type="user">
-    <uid>user02</uid>
-    <short-name>user02</short-name>
-    <password>user02</password>
-    <full-name>User 02</full-name>
-    <email>user02 at example.com</email>
-  </record>
-
-  <record type="user">
-    <uid>user03</uid>
-    <short-name>user03</short-name>
-    <password>user03</password>
-    <full-name>User 03</full-name>
-    <email>user03 at example.com</email>
-  </record>
-
-  <record type="user">
-    <uid>user04</uid>
-    <short-name>user04</short-name>
-    <password>user04</password>
-    <full-name>User 04</full-name>
-    <email>user04 at example.com</email>
-  </record>
-
-  <record type="user">
-    <uid>user05</uid>
-    <short-name>user05</short-name>
-    <password>user05</password>
-    <full-name>User 05</full-name>
-    <email>user05 at example.com</email>
-  </record>
-
-  <record type="user">
-    <uid>user06</uid>
-    <short-name>user06</short-name>
-    <password>user06</password>
-    <full-name>User 06</full-name>
-    <email>user06 at example.com</email>
-  </record>
-
-  <record type="user">
-    <uid>__wsanchez1__</uid>
-    <short-name>wsanchez1</short-name>
-    <short-name>wilfredo_sanchez</short-name>
-    <password>zehcnasw</password>
-    <full-name>Wilfredo Sanchez</full-name>
-    <email>wsanchez at bitbucket.calendarserver.org</email>
-    <email>wsanchez at devnull.twistedmatrix.com</email>
-  </record>
-
-  <record type="user">
-    <uid>__glyph1__</uid>
-    <short-name>glyph1</short-name>
-    <password>hpylg</password>
-    <full-name>Glyph Lefkowitz</full-name>
-    <email>glyph at bitbucket.calendarserver.org</email>
-    <email>glyph at devnull.twistedmatrix.com</email>
-  </record>
-
-  <record type="user">
-    <uid>__sagen1__</uid>
-    <short-name>sagen</short-name>
-    <short-name>sagen1</short-name>
-    <password>negas</password>
-    <full-name>Morgen Sagen</full-name>
-    <email>sagen at bitbucket.calendarserver.org</email>
-  </record>
-
-  <record type="user">
-    <uid>__cdaboo1__</uid>
-    <short-name>cdaboo1</short-name>
-    <password>suryc</password>
-    <full-name>Cyrus Daboo</full-name>
-    <email>cdaboo at bitbucket.calendarserver.org</email>
-  </record>
-
-  <record type="user">
-    <uid>__dre1__</uid>
-    <short-name>dre1</short-name>
-    <short-name>dre</short-name>
-    <password>erd</password>
-    <full-name>Andre LaBranche</full-name>
-    <email>dre at bitbucket.calendarserver.org</email>
-  </record>
-
-  <record type="group">
-    <uid>__top_group_1__</uid>
-    <short-name>top-group-1</short-name>
-    <full-name>Top Group 1</full-name>
-    <email>topgroup1 at example.com</email>
-    <member-uid>__wsanchez1__</member-uid>
-    <member-uid>__glyph1__</member-uid>
-    <member-uid>__sub_group_1__</member-uid>
-  </record>
-
-  <record type="group">
-    <uid>__sub_group_1__</uid>
-    <short-name>sub-group-1</short-name>
-    <full-name>Sub Group 1</full-name>
-    <email>subgroup1 at example.com</email>
-    <member-uid>__sagen1__</member-uid>
-    <member-uid>__cdaboo1__</member-uid>
-  </record>
-
-
-  <record type="group">
-    <uid>9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
-    <short-name>managers</short-name>
-    <password>managers</password>
-    <full-name>Managers</full-name>
-      <member-uid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</member-uid>
-  </record>
-  <record type="group">
-    <uid>admin</uid>
-    <short-name>admin</short-name>
-    <password>admin</password>
-    <full-name>Administrators</full-name>
-      <member-uid>9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</member-uid>
-  </record>
-  <record type="group">
-    <uid>grunts</uid>
-    <short-name>grunts</short-name>
-    <password>grunts</password>
-    <full-name>We do all the work</full-name>
-      <member-uid>6423F94A-6B76-4A3A-815B-D52CFD77935D</member-uid>
-      <member-uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</member-uid>
-      <member-uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</member-uid>
-  </record>
-  <record type="group">
-    <uid>right_coast</uid>
-    <short-name>right_coast</short-name>
-    <password>right_coast</password>
-    <full-name>East Coast</full-name>
-      <member-uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</member-uid>
-  </record>
-  <record type="group">
-    <uid>left_coast</uid>
-    <short-name>left_coast</short-name>
-    <password>left_coast</password>
-    <full-name>West Coast</full-name>
-      <member-uid>6423F94A-6B76-4A3A-815B-D52CFD77935D</member-uid>
-      <member-uid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</member-uid>
-      <member-uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</member-uid>
-  </record>
-  <record type="group">
-    <uid>both_coasts</uid>
-    <short-name>both_coasts</short-name>
-    <password>both_coasts</password>
-    <full-name>Both Coasts</full-name>
-      <member-uid>right_coast</member-uid>
-      <member-uid>left_coast</member-uid>
-  </record>
-  <record type="group">
-    <uid>recursive1_coasts</uid>
-    <short-name>recursive1_coasts</short-name>
-    <password>recursive1_coasts</password>
-    <full-name>Recursive1 Coasts</full-name>
-      <member-uid>recursive2_coasts</member-uid>
-      <member-uid>6423F94A-6B76-4A3A-815B-D52CFD77935D</member-uid>
-  </record>
-  <record type="group">
-    <uid>recursive2_coasts</uid>
-    <short-name>recursive2_coasts</short-name>
-    <password>recursive2_coasts</password>
-    <full-name>Recursive2 Coasts</full-name>
-      <member-uid>recursive1_coasts</member-uid>
-      <member-uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</member-uid>
-  </record>
-  <record type="group">
-    <uid>non_calendar_group</uid>
-    <short-name>non_calendar_group</short-name>
-    <password>non_calendar_group</password>
-    <full-name>Non-calendar group</full-name>
-      <member-uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</member-uid>
-      <member-uid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</member-uid>
-  </record>
-
-  <!-- Calverify test records -->
-
-  <record type="user">
-    <uid>D46F3D71-04B7-43C2-A7B6-6F92F92E61D0</uid>
-    <guid>D46F3D71-04B7-43C2-A7B6-6F92F92E61D0</guid>
-    <short-name>example1</short-name>
-    <password>example</password>
-    <full-name>Example User1</full-name>
-    <email>example1 at example.com</email>
-  </record>
-
-  <record type="user">
-    <uid>47B16BB4-DB5F-4BF6-85FE-A7DA54230F92</uid>
-    <guid>47B16BB4-DB5F-4BF6-85FE-A7DA54230F92</guid>
-    <short-name>example2</short-name>
-    <password>example</password>
-    <full-name>Example User2</full-name>
-    <email>example2 at example.com</email>
-  </record>
-
-  <record type="user">
-    <uid>AC478592-7783-44D1-B2AE-52359B4E8415</uid>
-    <guid>AC478592-7783-44D1-B2AE-52359B4E8415</guid>
-    <short-name>example3</short-name>
-    <password>example</password>
-    <full-name>Example User3</full-name>
-    <email>example3 at example.com</email>
-  </record>
-
-  <record type="user">
-    <uid>A89E3A97-1658-4E45-A185-479F3E49D446</uid>
-    <guid>A89E3A97-1658-4E45-A185-479F3E49D446</guid>
-    <short-name>example4</short-name>
-    <password>example</password>
-    <full-name>Example User4</full-name>
-    <email>example4 at example.com</email>
-  </record>
-
-</directory>

Deleted: CalendarServer/trunk/twistedcaldav/directory/test/augments.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/augments.xml	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/twistedcaldav/directory/test/augments.xml	2014-05-09 23:38:16 UTC (rev 13468)
@@ -1,187 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2009-2014 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
- -->
-
-<!DOCTYPE augments SYSTEM "../../../conf/auth/augments.dtd">
-
-<augments>
-  <!--
-  <record>
-    <uid>Location-Default</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-    <auto-schedule-mode>automatic</auto-schedule-mode>
-  </record>
-  <record>
-    <uid>Resource-Default</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-    <auto-schedule-mode>automatic</auto-schedule-mode>
-  </record>
-  -->
-
-  <record>
-    <uid>D11F03A0-97EA-48AF-9A6C-FAC7F3975766</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>6423F94A-6B76-4A3A-815B-D52CFD77935D</uid>
-    <server-id>00001</server-id>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</uid>
-    <server-id>00002</server-id>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</uid>
-    <enable-calendar>false</enable-calendar>
-    <enable-addressbook>false</enable-addressbook>
-  </record>
-  <record>
-    <uid>user01</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>user02</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>user03</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>user04</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>user05</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>user06</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>right_coast</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>left_coast</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>mercury</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>gemini</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>apollo</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-    <auto-accept-group>both_coasts</auto-accept-group>
-  </record>
-  <record>
-    <uid>orion</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>transporter</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>ftlcpu</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <!--
-  <record>
-    <uid>non_calendar_proxy</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
--->
-  <record>
-    <uid>7423F94A-6B76-4A3A-815B-D52CFD77935D</uid>
-    <enable-calendar>true</enable-calendar>
-  </record>
-  <record>
-    <uid>8A985493-EE2C-4665-94CF-4DFEA3A89500</uid>
-    <enable-calendar>true</enable-calendar>
-  </record>
-  <record>
-    <uid>9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD2</uid>
-    <enable-calendar>true</enable-calendar>
-  </record>
-  <record>
-    <uid>9E1FFAC4-3CCD-45A1-8272-D161C92D2EEE</uid>
-    <enable-calendar>true</enable-calendar>
-  </record>
-  <record>
-    <uid>7678EC8A-A069-4E82-9066-7279C6718507</uid>
-    <enable-addressbook>true</enable-addressbook>
-  </record>
-  <record>
-    <uid>FC465590-E9E9-4746-ACE8-6C756A49FE4D</uid>
-    <enable-calendar>true</enable-calendar>
-  </record>
-  <record>
-    <uid>EC465590-E9E9-4746-ACE8-6C756A49FE4D</uid>
-    <enable-calendar>true</enable-calendar>
-    <enable-login>true</enable-login>
-  </record>
-  <record>
-    <uid>00599DAF-3E75-42DD-9DB7-52617E79943F</uid>
-    <enable-calendar>false</enable-calendar>
-    <enable-login>false</enable-login>
-  </record>
-  <record>
-    <uid>75EA36BE-F71B-40F9-81F9-CF59BF40CA8F</uid>
-    <enable-calendar>true</enable-calendar>
-    <auto-schedule-mode>automatic</auto-schedule-mode>
-  </record>
-
-</augments>

Deleted: CalendarServer/trunk/twistedcaldav/directory/test/proxies.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/proxies.xml	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/twistedcaldav/directory/test/proxies.xml	2014-05-09 23:38:16 UTC (rev 13468)
@@ -1,69 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2006-2014 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
- -->
-
-<!DOCTYPE proxies SYSTEM "../../../conf/auth/proxies.dtd">
-
-<proxies>
-  <record>
-    <guid>mercury</guid>
-    <write-proxies>
-      <member>left_coast</member>
-    </write-proxies>
-  </record>
-  <record>
-    <guid>gemini</guid>
-    <write-proxies>
-      <member>6423F94A-6B76-4A3A-815B-D52CFD77935D</member>
-    </write-proxies>
-  </record>
-  <record>
-    <guid>apollo</guid>
-    <write-proxies>
-      <member>both_coasts</member>
-    </write-proxies>
-  </record>
-  <record>
-    <guid>orion</guid>
-    <write-proxies>
-      <member>recursive1_coasts</member>
-    </write-proxies>
-  </record>
-  <record>
-    <guid>non_calendar_proxy</guid>
-    <write-proxies>
-      <member>non_calendar_group</member>
-    </write-proxies>
-    <read-proxies>
-      <member>recursive2_coasts</member>
-    </read-proxies>
-  </record>
-  <record>
-    <guid>7423F94A-6B76-4A3A-815B-D52CFD77935D</guid>
-    <write-proxies>
-      <member>8A985493-EE2C-4665-94CF-4DFEA3A89500</member>
-      <member>9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD2</member>
-    </write-proxies>
-  </record>
-  <record>
-    <guid>FC465590-E9E9-4746-ACE8-6C756A49FE4D</guid>
-    <write-proxies>
-      <member>EC465590-E9E9-4746-ACE8-6C756A49FE4D</member>
-      <member>00599DAF-3E75-42DD-9DB7-52617E79943F</member>
-    </write-proxies>
-  </record>
-</proxies>

Deleted: CalendarServer/trunk/twistedcaldav/directory/test/resources.xml
===================================================================
--- CalendarServer/trunk/twistedcaldav/directory/test/resources.xml	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/twistedcaldav/directory/test/resources.xml	2014-05-09 23:38:16 UTC (rev 13468)
@@ -1,120 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-
-<!--
-Copyright (c) 2006-2014 Apple Inc. All rights reserved.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
- -->
-
-<!DOCTYPE accounts SYSTEM "../../../conf/auth/accounts.dtd">
-
-<directory realm="Test">
-  <record type="location">
-    <uid>mercury</uid>
-    <short-name>mercury</short-name>
-    <password>mercury</password>
-    <full-name>Mercury Seven</full-name>
-    <email>mercury at example.com</email>
-  </record>
-  <record type="location">
-    <uid>gemini</uid>
-    <short-name>gemini</short-name>
-    <password>gemini</password>
-    <full-name>Gemini Twelve</full-name>
-    <email>gemini at example.com</email>
-  </record>
-  <record type="location">
-    <uid>apollo</uid>
-    <short-name>apollo</short-name>
-    <password>apollo</password>
-    <full-name>Apollo Eleven</full-name>
-    <email>apollo at example.com</email>
-  </record>
-  <record type="location">
-    <uid>orion</uid>
-    <short-name>orion</short-name>
-    <password>orion</password>
-    <full-name>Orion</full-name>
-    <email>orion at example.com</email>
-  </record>
-  <record type="resource">
-    <uid>transporter</uid>
-    <short-name>transporter</short-name>
-    <password>transporter</password>
-    <full-name>Mass Transporter</full-name>
-    <email>transporter at example.com</email>
-  </record>
-  <record type="resource">
-    <uid>ftlcpu</uid>
-    <short-name>ftlcpu</short-name>
-    <password>ftlcpu</password>
-    <full-name>Faster-Than-Light Microprocessor</full-name>
-    <email>ftlcpu at example.com</email>
-  </record>
-  <record type="resource">
-    <uid>non_calendar_proxy</uid>
-    <short-name>non_calendar_proxy</short-name>
-    <password>non_calendar_proxy</password>
-    <full-name>Non-calendar proxy</full-name>
-    <email>non_calendar_proxy at example.com</email>
-  </record>
-  <record type="resource">
-    <uid>disabled</uid>
-    <short-name>disabled</short-name>
-    <password>disabled</password>
-    <full-name>Disabled Record</full-name>
-    <email>disabled at example.com</email>
-  </record>
-  <record type="location">
-    <uid>__sanchezoffice__</uid>
-    <short-name>sanchezoffice</short-name>
-    <full-name>Sanchez Office</full-name>
-  </record>
-  <record type="location">
-    <uid>75EA36BE-F71B-40F9-81F9-CF59BF40CA8F</uid>
-    <guid>75EA36BE-F71B-40F9-81F9-CF59BF40CA8F</guid>
-    <short-name>location01</short-name>
-    <password>location01</password>
-    <full-name>Room 01</full-name>
-  </record>
-  <record type="location">
-    <uid>room-addr-1</uid>
-    <guid>634A102B-6902-464F-9451-8A86A31628C1</guid>
-    <short-name>room-with-address-1</short-name>
-    <password>room-addr-2</password>
-    <full-name>Room with Address 1</full-name>
-    <associated-address>1-infinite-loop</associated-address>
-  </record>
-  <record type="location">
-    <uid>room-addr-2</uid>
-    <short-name>room-with-address-2</short-name>
-    <password>room-addr-2</password>
-    <full-name>Room with Address 2</full-name>
-    <associated-address>2-infinite-loop</associated-address>
-  </record>
-  <record type="address">
-    <uid>1-infinite-loop</uid>
-    <short-name>il1</short-name>
-    <full-name>One Infinite Loop</full-name>
-    <street-address>1 Infinite Loop, Cupertino, CA 95014</street-address>
-    <geographic-location>37.331741,-122.030333</geographic-location>
-  </record>
-  <record type="address">
-    <uid>2-infinite-loop</uid>
-    <short-name>il2</short-name>
-    <full-name>Two Infinite Loop</full-name>
-    <street-address>2 Infinite Loop, Cupertino, CA 95014</street-address>
-    <geographic-location>37.332633,-122.030502</geographic-location>
-  </record>
-
-</directory>

Modified: CalendarServer/trunk/twistedcaldav/stdconfig.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/stdconfig.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/twistedcaldav/stdconfig.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -1651,18 +1651,6 @@
 
 
 
-def _updateServers(configDict, reloading=False):
-    from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers
-    if configDict.Servers.Enabled:
-        Servers.load()
-        Servers.installReverseProxies(
-            configDict.Servers.MaxClients,
-        )
-    else:
-        Servers.clear()
-
-
-
 def _updateCompliance(configDict, reloading=False):
 
     if configDict.EnableCalDAV:
@@ -1730,7 +1718,7 @@
     _updateICalendar,
     _updateScheduling,
     _updateSharing,
-    _updateServers,
+    # _updateServers,
     _updateCompliance,
     )
 

Modified: CalendarServer/trunk/twistedcaldav/test/util.py
===================================================================
--- CalendarServer/trunk/twistedcaldav/test/util.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/twistedcaldav/test/util.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -35,10 +35,8 @@
 from twistedcaldav.directory.util import transactionFromRequest
 from twistedcaldav.memcacheclient import ClientFactory
 from twistedcaldav.stdconfig import config
-from txdav.caldav.datastore.test.util import buildCalendarStore
 from txdav.common.datastore.file import CommonDataStore
 from txdav.common.datastore.test.util import deriveQuota, CommonCommonTests
-from txdav.who.util import directoryFromConfig
 from txdav.xml import element as element
 from txweb2.dav.test.util import SimpleRequest
 import txweb2.dav.test.util
@@ -110,22 +108,17 @@
 
 class StoreTestCase(CommonCommonTests, txweb2.dav.test.util.TestCase):
     """
-    A base class for tests that use the SQL store.
+    A base class for tests that use the SQL store and need to create and
+    verify filesystem hierarchies.
     """
 
     @inlineCallbacks
     def setUp(self):
         yield super(StoreTestCase, self).setUp()
 
-        self.configure()
+        yield self.buildStoreAndDirectory()
 
-        self._sqlCalendarStore = yield buildCalendarStore(
-            self, self.notifierFactory, None
-        )
-        self.directory = directoryFromConfig(config, self._sqlCalendarStore)
-        self._sqlCalendarStore.setDirectoryService(self.directory)
-
-        self.rootResource = getRootResource(config, self._sqlCalendarStore)
+        self.rootResource = getRootResource(config, self.store)
         self.actualRoot = self.rootResource.resource.resource
         self.site = Site(self.actualRoot)
 
@@ -136,37 +129,12 @@
         return succeed(None)
 
 
-    def storeUnderTest(self):
-        """
-        Return a store for testing.
-        """
-        return self._sqlCalendarStore
-
-
     def configure(self):
         """
         Adjust the global configuration for this test.
         """
-        self.serverRoot = self.mktemp()
-        os.mkdir(self.serverRoot)
+        super(StoreTestCase, self).configure()
 
-        config.reset()
-        self.configInit()
-
-        config.ServerRoot = os.path.abspath(self.serverRoot)
-        config.ConfigRoot = "config"
-        config.LogRoot = "logs"
-        config.RunRoot = "logs"
-
-        if not os.path.exists(config.DataRoot):
-            os.makedirs(config.DataRoot)
-        if not os.path.exists(config.DocumentRoot):
-            os.makedirs(config.DocumentRoot)
-        if not os.path.exists(config.ConfigRoot):
-            os.makedirs(config.ConfigRoot)
-        if not os.path.exists(config.LogRoot):
-            os.makedirs(config.LogRoot)
-
         config.Memcached.Pools.Default.ClientEnabled = False
         config.Memcached.Pools.Default.ServerEnabled = False
         ClientFactory.allowTestCache = True
@@ -175,19 +143,7 @@
         config.DirectoryAddressBook.Enabled = False
         config.UsePackageTimezones = True
 
-        accounts = FilePath(config.DataRoot).child("accounts.xml")
-        accounts.setContent(xmlFile.getContent())
 
-        resources = FilePath(config.DataRoot).child("resources.xml")
-        resources.setContent(resourcesFile.getContent())
-
-        augments = FilePath(config.DataRoot).child("augments.xml")
-        augments.setContent(augmentsFile.getContent())
-
-        proxies = FilePath(config.DataRoot).child("proxies.xml")
-        proxies.setContent(proxiesFile.getContent())
-
-
     def createHierarchy(self, structure, root=None):
         if root is None:
             root = os.path.abspath(self.mktemp())

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/caldav/test/test_scheduler.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/caldav/test/test_scheduler.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/caldav/test/test_scheduler.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -23,10 +23,10 @@
 
 from twistedcaldav.ical import Component
 
-from txdav.caldav.datastore.test.util import buildCalendarStore
 from txdav.common.datastore.test.util import CommonCommonTests, populateCalendarsFrom
 from txdav.caldav.datastore.scheduling.caldav.scheduler import CalDAVScheduler
 
+
 def normalizeiCalendarText(data):
     data = data.replace("\r\n ", "")
     data = [line for line in data.splitlines() if not (line.startswith("UID") or line.startswith("DTSTAMP"))]
@@ -42,7 +42,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(SchedulerFreeBusyRequest, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
+        yield self.buildStoreAndDirectory()
         yield self.populate()
 
         self.now = DateTime.getNowUTC()

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/imip/test/test_inbound.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -28,20 +28,19 @@
 from txdav.caldav.datastore.scheduling.imip.inbound import shouldDeleteAllMail
 from txdav.caldav.datastore.scheduling.imip.inbound import IMAP4DownloadProtocol
 from txdav.caldav.datastore.scheduling.itip import iTIPRequestStatus
-from txdav.caldav.datastore.test.util import buildCalendarStore
+from txdav.common.datastore.test.util import CommonCommonTests
 
 import email
 from twisted.trial import unittest
 
 
-class InboundTests(unittest.TestCase):
+class InboundTests(CommonCommonTests, unittest.TestCase):
 
     @inlineCallbacks
     def setUp(self):
         super(InboundTests, self).setUp()
 
-        self.store = yield buildCalendarStore(self, None)
-        self.directory = self.store.directoryService()
+        yield self.buildStoreAndDirectory()
         self.receiver = MailReceiver(self.store, self.directory)
         self.retriever = MailRetriever(self.store, self.directory,
             ConfigDict({
@@ -283,7 +282,7 @@
 DTSTAMP:20130208T120000Z
 DTSTART:20180601T120000Z
 DTEND:20180601T130000Z
-ORGANIZER:urn:uuid:user01
+ORGANIZER:urn:x-uid:user01
 ATTENDEE:mailto:xyzzy at example.com;PARTSTAT=ACCEPTED
 END:VEVENT
 END:VCALENDAR
@@ -292,7 +291,7 @@
         txn = self.store.newTransaction()
         result = (yield injectMessage(
                 txn,
-                "urn:uuid:user01",
+                "urn:x-uid:user01",
                 "mailto:xyzzy at example.com",
                 calendar
             )
@@ -316,7 +315,7 @@
 DTSTAMP:20130208T120000Z
 DTSTART:20180601T120000Z
 DTEND:20180601T130000Z
-ORGANIZER:urn:uuid:unknown_user
+ORGANIZER:urn:x-uid:unknown_user
 ATTENDEE:mailto:xyzzy at example.com;PARTSTAT=ACCEPTED
 END:VEVENT
 END:VCALENDAR
@@ -325,7 +324,7 @@
         txn = self.store.newTransaction()
         result = (yield injectMessage(
                 txn,
-                "urn:uuid:unknown_user",
+                "urn:x-uid:unknown_user",
                 "mailto:xyzzy at example.com",
                 calendar
             )
@@ -349,14 +348,14 @@
 DTSTAMP:20130208T120000Z
 DTSTART:20180601T120000Z
 DTEND:20180601T130000Z
-ORGANIZER:urn:uuid:user01
+ORGANIZER:urn:x-uid:user01
 ATTENDEE:mailto:xyzzy at example.com;PARTSTAT=ACCEPTED
 END:VEVENT
 END:VCALENDAR
 """
         txn = self.store.newTransaction()
         wp = (yield txn.enqueue(IMIPReplyWork,
-            organizer="urn:uuid:user01",
+            organizer="urn:x-uid:user01",
             attendee="mailto:xyzzy at example.com",
             icalendarText=calendar
         ))

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/localservers.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/localservers.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/localservers.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -40,7 +40,7 @@
 """
 
 __all__ = [
-    "Servers",
+    "buildServersDB",
 ]
 
 log = Logger()
@@ -122,10 +122,17 @@
                 maxClients,
             )
 
-Servers = ServersDB()   # Global server DB
 
 
+def buildServersDB(maxClients=5):
+    serversDB = ServersDB()
+    serversDB.load()
+    serversDB.installReverseProxies(maxClients)
+    return serversDB
 
+
+
+
 class Server(object):
     """
     Represents a server.

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/scheduler.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/scheduler.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/scheduler.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -31,7 +31,6 @@
 from txdav.caldav.datastore.scheduling.ischedule import xml
 from txdav.caldav.datastore.scheduling.ischedule.dkim import DKIMVerifier, \
     DKIMVerificationError, DKIMMissingError
-from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers
 from txdav.caldav.datastore.scheduling.ischedule.remoteservers import IScheduleServers
 from txdav.caldav.datastore.scheduling.ischedule.utils import getIPsFromHost
 from txdav.caldav.datastore.scheduling.ischedule.xml import ischedule_namespace
@@ -57,6 +56,7 @@
 
 log = Logger()
 
+
 class ErrorResponse(Response):
     """
     A L{Response} object which contains a status code and a L{element.Error}
@@ -339,13 +339,14 @@
 
         # Check against this server.
         matched = False
-        if Servers.getThisServer().checkThisIP(clientip):
+        serversDB = self.txn._store.directoryService().serversDB
+        if serversDB.getThisServer().checkThisIP(clientip):
             matched = True
 
         # Checked allowed IPs - if any were defined we only check against them, we do not
         # go on to check the expected server host ip
-        elif Servers.getThisServer().hasAllowedFromIP():
-            matched = Servers.getThisServer().checkAllowedFromIP(clientip)
+        elif serversDB.getThisServer().hasAllowedFromIP():
+            matched = serversDB.getThisServer().checkAllowedFromIP(clientip)
             if not matched:
                 log.error("Invalid iSchedule connection from client: %s" % (clientip,))
 
@@ -365,7 +366,7 @@
                 log.debug("iSchedule cannot lookup client ip '%s': %s" % (clientip, str(e),))
 
         # Check possible shared secret
-        if matched and not Servers.getThisServer().checkSharedSecret(self.headers):
+        if matched and not serversDB.getThisServer().checkSharedSecret(self.headers):
             log.error("Invalid iSchedule shared secret")
             matched = False
 

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_localservers.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -20,10 +20,13 @@
 
 from twistedcaldav.stdconfig import config
 
-from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers, SERVER_SECRET_HEADER
+from txdav.caldav.datastore.scheduling.ischedule.localservers import (
+    ServersDB, SERVER_SECRET_HEADER
+)
 
 import StringIO as StringIO
 
+
 class ServerTests(unittest.TestCase):
 
     data1 = """<?xml version="1.0" encoding="utf-8"?>
@@ -61,7 +64,7 @@
         self.patch(config, "HTTPPort", 8008)
 
         xmlFile = StringIO.StringIO(data)
-        servers = Servers
+        servers = ServersDB()
         servers.load(xmlFile, ignoreIPLookupFailures=True)
 
         return servers
@@ -96,7 +99,7 @@
         self.patch(config, "BindSSLPorts", [8843])
 
         xmlFile = StringIO.StringIO(ServerTests.data1)
-        servers = Servers
+        servers = ServersDB()
         servers.load(xmlFile, ignoreIPLookupFailures=True)
 
         self.assertFalse(servers.getServerById("00001").thisServer)

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_resource.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_resource.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/ischedule/test/test_resource.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -29,45 +29,29 @@
     CommonCommonTests
 from twext.python.clsprop import classproperty
 import txweb2.dav.test.util
-from txdav.caldav.datastore.test.util import buildCalendarStore, \
-    TestCalendarStoreDirectoryRecord
-from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers, \
-    Server
+from txdav.caldav.datastore.scheduling.ischedule.localservers import (
+    ServersDB, Server
+)
 
+
 class iSchedulePOST (CommonCommonTests, txweb2.dav.test.util.TestCase):
 
     @inlineCallbacks
     def setUp(self):
         yield super(iSchedulePOST, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
-        self.directory = self._sqlCalendarStore.directoryService()
 
+        serversDB = ServersDB()
+        a_server = Server("A", "http://localhost:8008", thisServer=True)
+        serversDB.addServer(a_server)
+        b_server = Server("B", "http://localhost:8108", thisServer=False)
+        serversDB.addServer(b_server)
+        yield self.buildStoreAndDirectory(serversDB=serversDB)
+
         self.site.resource.putChild("ischedule", IScheduleInboxResource(self.site.resource, self.storeUnderTest()))
         self.site.resource.putChild("podding", IScheduleInboxResource(self.site.resource, self.storeUnderTest(), podding=True))
 
         yield self.populate()
 
-        # Pod servers
-        a_server = Server(id="A", uri="http://localhost:8008", thisServer=True)
-        Servers.addServer(a_server)
-        b_server = Server(id="B", uri="http://localhost:8108", thisServer=False)
-        Servers.addServer(b_server)
-        Servers._thisServer = a_server
-
-        # Podded users
-        for ctr in range(1, 100):
-            self.directory.addRecord(TestCalendarStoreDirectoryRecord(
-                "puser{:02d}".format(ctr),
-                ("puser{:02d}".format(ctr),),
-                "Puser {:02d}".format(ctr),
-                frozenset((
-                    "urn:uuid:puser{:02d}".format(ctr),
-                    "mailto:puser{:02d}@example.com".format(ctr),
-                )),
-                thisServer=False,
-                server=b_server,
-            ))
-
         # iSchedule server
         IScheduleServers()
         server = IScheduleServerRecord("http://127.0.0.1")
@@ -76,13 +60,6 @@
         self.addCleanup(lambda : IScheduleServers._domainMap.pop("example.org")) #@UndefinedVariable
 
 
-    def storeUnderTest(self):
-        """
-        Return a store for testing.
-        """
-        return self._sqlCalendarStore
-
-
     @inlineCallbacks
     def populate(self):
         yield populateCalendarsFrom(self.requirements, self.storeUnderTest())

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_cuaddress.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_cuaddress.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_cuaddress.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -22,7 +22,6 @@
 
 from txdav.caldav.datastore.scheduling.cuaddress import calendarUserFromCalendarUserAddress, \
     LocalCalendarUser, InvalidCalendarUser
-from txdav.caldav.datastore.test.util import buildCalendarStore
 from txdav.common.datastore.test.util import populateCalendarsFrom, CommonCommonTests
 
 
@@ -50,10 +49,9 @@
     def setUp(self):
 
         yield super(CalendarUser, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
-        self.directory = self._sqlCalendarStore.directoryService()
+        yield self.buildStoreAndDirectory()
         yield self.populate()
-        self.directory.removeRecord("user03")
+        yield self.removeRecord(u"user03")
 
 
     @inlineCallbacks
@@ -62,13 +60,6 @@
         self.notifierFactory.reset()
 
 
-    def storeUnderTest(self):
-        """
-        Create and return a L{CalendarStore} for testing.
-        """
-        return self._sqlCalendarStore
-
-
     @inlineCallbacks
     def test_lookup(self):
         """

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_freebusy.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_freebusy.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_freebusy.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -27,7 +27,6 @@
 
 from txdav.caldav.datastore.scheduling.freebusy import buildFreeBusyResult, \
     generateFreeBusyInfo
-from txdav.caldav.datastore.test.util import buildCalendarStore
 from txdav.common.datastore.test.util import CommonCommonTests, populateCalendarsFrom
 
 def normalizeiCalendarText(data):
@@ -266,7 +265,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(GenerateFreeBusyInfo, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
+        yield self.buildStoreAndDirectory()
         yield self.populate()
 
         self.now = DateTime.getNowUTC()

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_implicit.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_implicit.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_implicit.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -34,8 +34,6 @@
 from txdav.caldav.datastore.scheduling.implicit import ImplicitScheduler, \
     ScheduleReplyWork
 from txdav.caldav.datastore.scheduling.scheduler import ScheduleResponseQueue
-from txdav.caldav.datastore.test.util import buildCalendarStore, \
-    buildDirectoryRecord
 from txdav.caldav.icalendarstore import AttendeeAllowedError, \
     ComponentUpdateState
 from txdav.common.datastore.test.util import CommonCommonTests, populateCalendarsFrom
@@ -58,45 +56,18 @@
 
 
 
-class FakeDirectoryService(object):
-
-    def recordWithUID(self, uid):
-        return buildDirectoryRecord(uid)
-
-
-
-class FakeCalendarHome(object):
-
-    def __init__(self, uid):
-        self._uid = uid
-
-
-    def uid(self):
-        return self._uid
-
-
-    def directoryService(self):
-        if not hasattr(self, "_directoryService"):
-            self._directoryService = FakeDirectoryService()
-        return self._directoryService
-
-
-
-class FakeTxn(object):
-
-    def directoryService(self):
-        if not hasattr(self, "_directoryService"):
-            self._directoryService = FakeDirectoryService()
-        return self._directoryService
-
-
-
-class Implicit(TestCase):
+class Implicit(CommonCommonTests, TestCase):
     """
     iCalendar support tests
     """
 
     @inlineCallbacks
+    def setUp(self):
+        yield super(Implicit, self).setUp()
+        yield self.buildStoreAndDirectory()
+
+
+    @inlineCallbacks
     def test_removed_attendees(self):
 
         data = (
@@ -109,9 +80,9 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
@@ -122,9 +93,9 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
@@ -139,9 +110,9 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
@@ -152,12 +123,12 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
-                (("mailto:user2 at example.com", None),),
+                (("mailto:user02 at example.com", None),),
             ),
             (
                 "#1.3 Simple component, two removals",
@@ -168,10 +139,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
@@ -182,14 +153,14 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
                 (
-                    ("mailto:user2 at example.com", None),
-                    ("mailto:user3 at example.com", None),
+                    ("mailto:user02 at example.com", None),
+                    ("mailto:user03 at example.com", None),
                 ),
             ),
             (
@@ -201,10 +172,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 END:VCALENDAR
@@ -216,15 +187,15 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 END:VCALENDAR
 """,
                 (
-                    ("mailto:user2 at example.com", None),
-                    ("mailto:user3 at example.com", None),
+                    ("mailto:user02 at example.com", None),
+                    ("mailto:user03 at example.com", None),
                 ),
             ),
             (
@@ -236,10 +207,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 END:VCALENDAR
@@ -251,19 +222,19 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 EXDATE:20080801T120000Z
 END:VEVENT
 END:VCALENDAR
 """,
                 (
-                    ("mailto:user1 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user2 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user3 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user01 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user02 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user03 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
                 ),
             ),
             (
@@ -275,10 +246,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 END:VCALENDAR
@@ -290,22 +261,22 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 EXDATE:20080801T120000Z,20080901T120000Z
 END:VEVENT
 END:VCALENDAR
 """,
                 (
-                    ("mailto:user1 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user2 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user3 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user1 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user2 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user3 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user01 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user02 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user03 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user01 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user02 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user03 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
                 ),
             ),
             (
@@ -317,10 +288,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 END:VCALENDAR
@@ -332,10 +303,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 EXDATE:20080801T120000Z,20080901T120000Z
 EXDATE:20081201T120000Z
@@ -343,15 +314,15 @@
 END:VCALENDAR
 """,
                 (
-                    ("mailto:user1 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user2 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user3 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user1 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user2 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user3 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user1 at example.com", DateTime(2008, 12, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user2 at example.com", DateTime(2008, 12, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user3 at example.com", DateTime(2008, 12, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user01 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user02 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user03 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user01 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user02 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user03 at example.com", DateTime(2008, 9, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user01 at example.com", DateTime(2008, 12, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user02 at example.com", DateTime(2008, 12, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user03 at example.com", DateTime(2008, 12, 1, 12, 0, 0, tzid=Timezone(utc=True))),
                 ),
             ),
             (
@@ -363,10 +334,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 BEGIN:VEVENT
@@ -374,10 +345,10 @@
 RECURRENCE-ID:20080801T120000Z
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
@@ -388,10 +359,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 BEGIN:VEVENT
@@ -399,10 +370,10 @@
 RECURRENCE-ID:20080801T120000Z
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
@@ -417,10 +388,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 BEGIN:VEVENT
@@ -428,10 +399,10 @@
 RECURRENCE-ID:20080801T120000Z
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
@@ -442,9 +413,9 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 BEGIN:VEVENT
@@ -452,15 +423,15 @@
 RECURRENCE-ID:20080801T120000Z
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
                 (
-                    ("mailto:user3 at example.com", None),
-                    ("mailto:user3 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user03 at example.com", None),
+                    ("mailto:user03 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
                 ),
             ),
             (
@@ -472,10 +443,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 BEGIN:VEVENT
@@ -483,10 +454,10 @@
 RECURRENCE-ID:20080801T120000Z
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
@@ -497,10 +468,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 BEGIN:VEVENT
@@ -508,14 +479,14 @@
 RECURRENCE-ID:20080801T120000Z
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
                 (
-                    ("mailto:user3 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user03 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
                 ),
             ),
             (
@@ -527,10 +498,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 BEGIN:VEVENT
@@ -538,10 +509,10 @@
 RECURRENCE-ID:20080801T120000Z
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
@@ -552,9 +523,9 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 BEGIN:VEVENT
@@ -562,15 +533,15 @@
 RECURRENCE-ID:20080801T120000Z
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
                 (
-                    ("mailto:user3 at example.com", None),
+                    ("mailto:user03 at example.com", None),
                 ),
             ),
             (
@@ -582,10 +553,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 BEGIN:VEVENT
@@ -593,10 +564,10 @@
 RECURRENCE-ID:20080801T120000Z
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
@@ -607,10 +578,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 END:VCALENDAR
@@ -626,10 +597,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 BEGIN:VEVENT
@@ -637,10 +608,10 @@
 RECURRENCE-ID:20080801T120000Z
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
@@ -651,19 +622,19 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 EXDATE:20080801T120000Z
 END:VEVENT
 END:VCALENDAR
 """,
                 (
-                    ("mailto:user1 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user2 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user3 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user01 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user02 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user03 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
                 ),
             ),
             (
@@ -675,10 +646,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 BEGIN:VEVENT
@@ -686,10 +657,10 @@
 RECURRENCE-ID:20080801T120000Z
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user4 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user04 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
@@ -700,9 +671,9 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 BEGIN:VEVENT
@@ -710,15 +681,15 @@
 RECURRENCE-ID:20080801T120000Z
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
                 (
-                    ("mailto:user3 at example.com", None),
-                    ("mailto:user4 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user03 at example.com", None),
+                    ("mailto:user04 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
                 ),
             ),
             (
@@ -730,10 +701,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 BEGIN:VEVENT
@@ -741,10 +712,10 @@
 RECURRENCE-ID:20080801T120000Z
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user4 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user04 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
@@ -755,16 +726,16 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 END:VCALENDAR
 """,
                 (
-                    ("mailto:user4 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user04 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
                 ),
             ),
             (
@@ -776,10 +747,10 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 END:VEVENT
 BEGIN:VEVENT
@@ -787,10 +758,10 @@
 RECURRENCE-ID:20080801T120000Z
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user4 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user04 at example.com
 END:VEVENT
 END:VCALENDAR
 """,
@@ -801,19 +772,19 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
 RRULE:FREQ=MONTHLY
 EXDATE:20080801T120000Z
 END:VEVENT
 END:VCALENDAR
 """,
                 (
-                    ("mailto:user1 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user2 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
-                    ("mailto:user4 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user01 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user02 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
+                    ("mailto:user04 at example.com", DateTime(2008, 8, 1, 12, 0, 0, tzid=Timezone(utc=True))),
                 ),
             ),
         )
@@ -826,12 +797,14 @@
             scheduler.oldInstances = set(scheduler.oldcalendar.getComponentInstances())
             scheduler.calendar = Component.fromString(calendar2)
 
-            scheduler.calendar_home = FakeCalendarHome("user01")
-            scheduler.txn = FakeTxn()
+            txn = self.transactionUnderTest()
+            scheduler.txn = txn
+            scheduler.calendar_home = yield self.homeUnderTest(txn=txn, name=u"user01", create=True)
 
             yield scheduler.extractCalendarData()
             scheduler.findRemovedAttendees()
             self.assertEqual(scheduler.cancelledAttendees, set(result), msg=description)
+            yield self.commit()
 
 
     @inlineCallbacks
@@ -841,9 +814,9 @@
         """
 
         data = (
-            ((), None, 3, ("mailto:user2 at example.com", "mailto:user3 at example.com", "mailto:user4 at example.com",),),
-            (("mailto:user2 at example.com",), None, 2, ("mailto:user3 at example.com", "mailto:user4 at example.com",),),
-            ((), ("mailto:user2 at example.com", "mailto:user4 at example.com",) , 2, ("mailto:user2 at example.com", "mailto:user4 at example.com",),),
+            ((), None, 3, ("mailto:user02 at example.com", "mailto:user03 at example.com", "mailto:user04 at example.com",),),
+            (("mailto:user02 at example.com",), None, 2, ("mailto:user03 at example.com", "mailto:user04 at example.com",),),
+            ((), ("mailto:user02 at example.com", "mailto:user04 at example.com",) , 2, ("mailto:user02 at example.com", "mailto:user04 at example.com",),),
         )
 
         calendar = """BEGIN:VCALENDAR
@@ -853,11 +826,11 @@
 UID:12345-67890
 DTSTART:20080601T120000Z
 DTEND:20080601T130000Z
-ORGANIZER;CN="User 01":mailto:user1 at example.com
-ATTENDEE:mailto:user1 at example.com
-ATTENDEE:mailto:user2 at example.com
-ATTENDEE:mailto:user3 at example.com
-ATTENDEE:mailto:user4 at example.com
+ORGANIZER;CN="User 01":mailto:user01 at example.com
+ATTENDEE:mailto:user01 at example.com
+ATTENDEE:mailto:user02 at example.com
+ATTENDEE:mailto:user03 at example.com
+ATTENDEE:mailto:user04 at example.com
 END:VEVENT
 END:VCALENDAR
 """
@@ -874,14 +847,16 @@
             scheduler.changed_rids = None
             scheduler.reinvites = None
 
-            scheduler.calendar_home = FakeCalendarHome("user1")
-            scheduler.txn = FakeTxn()
+            txn = self.transactionUnderTest()
+            scheduler.txn = txn
+            scheduler.calendar_home = yield self.homeUnderTest(txn=txn, name=u"user01", create=True)
 
             # Get some useful information from the calendar
             yield scheduler.extractCalendarData()
+            record = yield self.directory.recordWithUID(scheduler.calendar_home.uid())
             scheduler.organizerAddress = LocalCalendarUser(
-                "mailto:user1 at example.com",
-                buildDirectoryRecord(scheduler.calendar_home.uid()),
+                "mailto:user01 at example.com",
+                record,
             )
 
             recipients = []
@@ -894,6 +869,7 @@
             self.assertEqual(count, result_count)
             self.assertEqual(len(recipients), result_count)
             self.assertEqual(set(recipients), set(result_set))
+            yield self.commit()
 
 
 
@@ -905,7 +881,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(ImplicitRequests, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
+        yield self.buildStoreAndDirectory()
         yield self.populate()
 
 
@@ -939,13 +915,6 @@
     }
 
 
-    def storeUnderTest(self):
-        """
-        Create and return a L{CalendarStore} for testing.
-        """
-        return self._sqlCalendarStore
-
-
     @inlineCallbacks
     def _createCalendarObject(self, data, user, name):
         calendar_collection = (yield self.calendarUnderTest(home=user))

Modified: CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_utils.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_utils.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/scheduling/test/test_utils.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -25,8 +25,6 @@
 
 from txdav.caldav.datastore.scheduling.utils import getCalendarObjectForRecord, \
     extractEmailDomain, uidFromCalendarUserAddress
-from txdav.caldav.datastore.test.util import buildCalendarStore, \
-    buildDirectoryRecord
 from txdav.common.datastore.test.util import populateCalendarsFrom, CommonCommonTests
 
 
@@ -107,10 +105,9 @@
     def setUp(self):
 
         yield super(RecipientCopy, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
+        yield self.buildStoreAndDirectory()
         yield self.populate()
 
-        self.directory = self._sqlCalendarStore.directoryService()
 
 
     @inlineCallbacks
@@ -145,7 +142,7 @@
         yield self.commit()
 
         # Look up resource by UID in home where only one exists
-        principal = buildDirectoryRecord("user01")
+        principal = yield self.directory.recordWithUID(u"user01")
         txn = self.transactionUnderTest()
         resource = (yield getCalendarObjectForRecord(txn, principal, "685BC3A1-195A-49B3-926D-388DDACA78A6"))
         self.assertEqual(resource.name(), "1.ics")
@@ -165,7 +162,7 @@
         yield self.commit()
 
         # Look up resource by UID in home where two exists
-        principal = buildDirectoryRecord("user02")
+        principal = yield self.directory.recordWithUID("user02")
         txn = self.transactionUnderTest()
         resource = (yield getCalendarObjectForRecord(txn, principal, "685BC3A1-195A-49B3-926D-388DDACA78A6"))
         self.assertTrue(resource.name() in ("2.ics", "3.ics",))
@@ -183,7 +180,7 @@
         yield self.commit()
 
         # Look up resource where principal exists but home does not
-        principal = buildDirectoryRecord("user102")
+        principal = yield self.directory.recordWithUID("user102")  # ASKCYRUS: but user102 doesn't exist
         txn = self.transactionUnderTest()
         resource = (yield getCalendarObjectForRecord(txn, principal, "685BC3A1-195A-49B3-926D-388DDACA78A6"))
         self.assertTrue(resource is None)

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_attachments.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -14,8 +14,6 @@
 # limitations under the License.
 ##
 
-from txdav.who.util import directoryFromConfig
-
 from pycalendar.datetime import DateTime
 from pycalendar.value import Value
 
@@ -34,7 +32,6 @@
 from txdav.caldav.datastore.sql import CalendarStoreFeatures, DropBoxAttachment, \
     ManagedAttachment
 from txdav.caldav.datastore.test.common import CaptureProtocol
-from txdav.caldav.datastore.test.util import buildCalendarStore
 from txdav.caldav.icalendarstore import IAttachmentStorageTransport, IAttachment, \
     QuotaExceeded, AttachmentSizeTooLarge
 from txdav.common.datastore.sql_tables import schema
@@ -99,7 +96,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(AttachmentTests, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
+        yield self.buildStoreAndDirectory()
         yield self.populate()
 
 
@@ -1536,18 +1533,13 @@
     def setUp(self):
         yield super(AttachmentMigrationTests, self).setUp()
 
-        self.patch(config.DirectoryService.params, "xmlFile",
-            os.path.join(
-                os.path.dirname(__file__), "attachments", "accounts.xml"
-            )
+        attachmentsFilePath = FilePath(
+            os.path.join(os.path.dirname(__file__), "attachments")
         )
-        self.patch(config.ResourceService.params, "xmlFile",
-            os.path.join(
-                os.path.dirname(__file__), "attachments", "resources.xml"
-            )
+        yield self.buildStoreAndDirectory(
+            accounts=attachmentsFilePath.child("accounts.xml"),
+            resources=attachmentsFilePath.child("resources.xml"),
         )
-
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory, directoryFromConfig(config))
         yield self.populate()
 
         self.paths = {}
@@ -1577,13 +1569,6 @@
         yield txn.commit()
 
 
-    def storeUnderTest(self):
-        """
-        Create and return a L{CalendarStore} for testing.
-        """
-        return self._sqlCalendarStore
-
-
     @inlineCallbacks
     def _addAttachment(self, home, calendar, event, dropboxid, name):
 

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_file.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -13,7 +13,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 ##
-from txdav.caldav.datastore.test.util import buildDirectory
 
 """
 File calendar store tests.
@@ -23,29 +22,27 @@
 # deleted and replaced with either implementation-specific methods on
 # FileStorageTests, or implementation-agnostic methods on CommonTests.
 
+from pycalendar.datetime import DateTime
+from twext.python.filepath import CachingFilePath as FilePath
+from twisted.internet.defer import inlineCallbacks
 from twisted.trial import unittest
-from twisted.internet.defer import inlineCallbacks
-
-from twext.python.filepath import CachingFilePath as FilePath
-
 from twistedcaldav.ical import Component as VComponent
-
+from txdav.caldav.datastore.file import Calendar, CalendarObject
+from txdav.caldav.datastore.file import CalendarStore, CalendarHome
+from txdav.caldav.datastore.test.common import (
+    CommonTests, test_event_text, event1modified_text
+)
+from txdav.common.datastore.test.util import buildTestDirectory
+from txdav.common.datastore.test.util import StubNotifierFactory
+from txdav.common.datastore.test.util import deriveQuota
 from txdav.common.icommondatastore import HomeChildNameNotAllowedError
+from txdav.common.icommondatastore import NoSuchHomeChildError
 from txdav.common.icommondatastore import ObjectResourceNameNotAllowedError
 from txdav.common.icommondatastore import ObjectResourceUIDAlreadyExistsError
-from txdav.common.icommondatastore import NoSuchHomeChildError
 
-from txdav.caldav.datastore.file import CalendarStore, CalendarHome
-from txdav.caldav.datastore.file import Calendar, CalendarObject
+storePath = FilePath(__file__).parent().child("calendar_store")
 
-from txdav.common.datastore.test.util import deriveQuota
-from txdav.caldav.datastore.test.common import (
-    CommonTests, test_event_text, event1modified_text)
 
-from pycalendar.datetime import DateTime
-
-storePath = FilePath(__file__).parent().child("calendar_store")
-
 def _todo(f, why):
     f.todo = why
     return f
@@ -78,12 +75,15 @@
                             resource.setContent(resource.getContent() % {"now": nowYear})
 
     testID = test.id()
+    test.counter = 0
+    test.notifierFactory = StubNotifierFactory()
     test.calendarStore = CalendarStore(
         storeRootPath,
         {"push": test.notifierFactory} if test.notifierFactory else {},
-        buildDirectory(),
+        None,  # must create directory later
         quota=deriveQuota(test),
     )
+    test.directory = buildTestDirectory(test.calendarStore, test.mktemp())
     test.txn = test.calendarStore.newTransaction(testID + "(old)")
     assert test.calendarStore is not None, "No calendar store?"
 
@@ -131,6 +131,7 @@
 class CalendarHomeTest(unittest.TestCase):
 
     notifierFactory = None
+
     def setUp(self):
         return setUpHome1(self)
 

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_implicit.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_implicit.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_implicit.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -30,9 +30,9 @@
     TooManyAttendeesError, InvalidCalendarAccessError, ComponentUpdateState, \
     DuplicatePrivateCommentsError
 from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE
-from txdav.caldav.datastore.test.util import buildCalendarStore
 from txdav.caldav.datastore.sql import CalendarObject
 
+
 class ImplicitRequests (CommonCommonTests, TestCase):
     """
     Test twistedcaldav.scheduyling.implicit with a Request object.
@@ -41,7 +41,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(ImplicitRequests, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
+        yield self.buildStoreAndDirectory()
         yield self.populate()
 
 

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_sql.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -55,7 +55,6 @@
 from txdav.caldav.datastore.test.common import CommonTests as CalendarCommonTests, \
     test_event_text
 from txdav.caldav.datastore.test.test_file import setUpCalendarStore
-from txdav.caldav.datastore.test.util import buildCalendarStore
 from txdav.caldav.datastore.util import _migrateCalendar, migrateHome
 from txdav.caldav.icalendarstore import ComponentUpdateState, InvalidDefaultCalendar, \
     InvalidSplit
@@ -79,7 +78,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(CalendarSQLStorageTests, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
+        yield self.buildStoreAndDirectory()
         yield self.populate()
 
         self.nowYear = {"now": DateTime.getToday().getYear()}
@@ -91,13 +90,6 @@
         self.notifierFactory.reset()
 
 
-    def storeUnderTest(self):
-        """
-        Create and return a L{CalendarStore} for testing.
-        """
-        return self._sqlCalendarStore
-
-
     @inlineCallbacks
     def assertCalendarsSimilar(self, a, b, bCalendarFilter=None):
         """
@@ -1990,7 +1982,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(SchedulingTests, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
+        yield self.buildStoreAndDirectory()
 
         # Make sure homes are provisioned
         txn = self.transactionUnderTest()
@@ -2006,13 +1998,6 @@
         self.notifierFactory.reset()
 
 
-    def storeUnderTest(self):
-        """
-        Create and return a L{CalendarStore} for testing.
-        """
-        return self._sqlCalendarStore
-
-
     @inlineCallbacks
     def test_doImplicitAttendeeEventFix(self):
         """
@@ -2181,7 +2166,7 @@
 DTSTART;TZID=America/Los_Angeles:20131211T164500
 DTEND;TZID=America/Los_Angeles:20131211T174500
 ATTENDEE;CN=Conference Room One;CUTYPE=ROOM;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPAN
- T;SCHEDULE-STATUS=2.0:urn:x-uid:room1
+ T;SCHEDULE-STATUS=2.0:urn:x-uid:room-addr-1
 ATTENDEE;CN=User 01;CUTYPE=INDIVIDUAL;EMAIL=user01 at example.com;PARTSTAT=AC
  CEPTED:urn:x-uid:user01
 CREATED:20131211T221854Z
@@ -2198,7 +2183,7 @@
 DTSTART;TZID=America/Los_Angeles:20131214T160000
 DTEND;TZID=America/Los_Angeles:20131214T170000
 ATTENDEE;CN=Conference Room Two;CUTYPE=ROOM;PARTSTAT=ACCEPTED;ROLE=REQ-PARTICIPAN
- T;SCHEDULE-STATUS=2.0:urn:x-uid:room2
+ T;SCHEDULE-STATUS=2.0:urn:x-uid:room-addr-2
 ATTENDEE;CN=User 01;CUTYPE=INDIVIDUAL;EMAIL=user01 at example.com;PARTSTAT=AC
  CEPTED:urn:x-uid:user01
 CREATED:20131211T221854Z
@@ -2222,7 +2207,7 @@
         # Check first component
         locProp = components[0].getProperty("LOCATION")
         self.assertEquals(locProp.value(),
-            "Conference Room One\n1 Infinite Loop, Cupertino, CA 95014")
+            "Room with Address 1\n1 Infinite Loop, Cupertino, CA 95014")
         structProp = components[0].getProperty("X-APPLE-STRUCTURED-LOCATION")
         self.assertEquals(structProp.value(),
             "geo:37.331741,-122.030333")
@@ -2230,7 +2215,7 @@
         # Check second component
         locProp = components[1].getProperty("LOCATION")
         self.assertEquals(locProp.value(),
-            "Conference Room Two\n2 Infinite Loop, Cupertino, CA 95014")
+            "Room with Address 2\n2 Infinite Loop, Cupertino, CA 95014")
         structProp = components[1].getProperty("X-APPLE-STRUCTURED-LOCATION")
         self.assertEquals(structProp.value(),
             "geo:37.332633,-122.030502")
@@ -2247,7 +2232,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(CalendarObjectSplitting, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
+        yield self.buildStoreAndDirectory()
 
         # Make sure homes are provisioned
         txn = self.transactionUnderTest()
@@ -2299,13 +2284,6 @@
         self.notifierFactory.reset()
 
 
-    def storeUnderTest(self):
-        """
-        Create and return a L{CalendarStore} for testing.
-        """
-        return self._sqlCalendarStore
-
-
     @inlineCallbacks
     def _splitDetails(self, home):
         # Get home data

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_sql_sharing.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_sql_sharing.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_sql_sharing.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -21,7 +21,6 @@
 from twext.python.clsprop import classproperty
 from txdav.common.datastore.test.util import CommonCommonTests, \
     populateCalendarsFrom
-from txdav.caldav.datastore.test.util import buildCalendarStore
 from txdav.common.datastore.sql_tables import _BIND_MODE_READ, \
     _BIND_STATUS_INVITED, _BIND_MODE_DIRECT, _BIND_STATUS_ACCEPTED
 
@@ -34,7 +33,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(BaseSharingTests, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
+        yield self.buildStoreAndDirectory()
         yield self.populate()
 
 

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/test_util.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/test_util.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/test_util.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -29,7 +29,6 @@
 from twistedcaldav.test.util import TestCase
 
 from txdav.common.datastore.test.util import populateCalendarsFrom, CommonCommonTests
-from txdav.caldav.datastore.test.util import buildCalendarStore
 
 from txdav.caldav.datastore.util import dropboxIDFromCalendarObject, \
     StorageTransportBase, migrateHome
@@ -323,18 +322,17 @@
     @inlineCallbacks
     def setUp(self):
         yield super(HomeMigrationTests, self).setUp()
-        self.theStore = yield buildCalendarStore(self, self.notifierFactory, homes=(
-            "conflict1",
-            "conflict2",
-            "empty_home",
-            "non_empty_home",
-        ))
+        yield self.buildStoreAndDirectory(
+            extraUids=(
+                u"conflict1",
+                u"conflict2",
+                u"empty_home",
+                u"non_empty_home",
+            )
+        )
 
 
-    def storeUnderTest(self):
-        return self.theStore
 
-
     @inlineCallbacks
     def test_migrateEmptyHome(self):
         """

Modified: CalendarServer/trunk/txdav/caldav/datastore/test/util.py
===================================================================
--- CalendarServer/trunk/txdav/caldav/datastore/test/util.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/caldav/datastore/test/util.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -16,230 +16,23 @@
 ##
 from twisted.trial.unittest import TestCase
 from twext.python.clsprop import classproperty
-from twisted.internet.defer import inlineCallbacks, succeed
+from twisted.internet.defer import inlineCallbacks
 
 """
 Store test utility functions
 """
 
-from twistedcaldav.config import config
-from txdav.caldav.icalendardirectoryservice import ICalendarStoreDirectoryService, \
-    ICalendarStoreDirectoryRecord
-from txdav.common.datastore.test.util import TestStoreDirectoryService, \
-    TestStoreDirectoryRecord, theStoreBuilder, CommonCommonTests, \
-    populateCalendarsFrom
-from zope.interface.declarations import implements
+from txdav.common.datastore.test.util import (
+    CommonCommonTests, populateCalendarsFrom
+)
 
-class TestCalendarStoreDirectoryService(TestStoreDirectoryService):
 
-    implements(ICalendarStoreDirectoryService)
-
-    def __init__(self):
-        super(TestCalendarStoreDirectoryService, self).__init__()
-        self.recordsByCUA = {}
-
-
-    def recordWithCalendarUserAddress(self, cuaddr):
-        return succeed(self.recordsByCUA.get(cuaddr))
-
-
-    def addRecord(self, record):
-        super(TestCalendarStoreDirectoryService, self).addRecord(record)
-        for cuaddr in record.calendarUserAddresses:
-            self.recordsByCUA[cuaddr] = record
-
-
-    def removeRecord(self, uid):
-        record = self.records[uid]
-        del self.records[uid]
-        for cuaddr in record.calendarUserAddresses:
-            del self.recordsByCUA[cuaddr]
-
-
-
-class TestCalendarStoreDirectoryRecord(TestStoreDirectoryRecord):
-
-    implements(ICalendarStoreDirectoryRecord)
-
-    def __init__(
-        self,
-        uid,
-        shortNames,
-        fullName,
-        calendarUserAddresses,
-        cutype="INDIVIDUAL",
-        thisServer=True,
-        server=None,
-        associatedAddress=None,
-        streetAddress=None,
-        geographicLocation=None
-    ):
-
-        super(TestCalendarStoreDirectoryRecord, self).__init__(
-            uid, shortNames, fullName, thisServer, server,
-        )
-        self.calendarUserAddresses = calendarUserAddresses
-        self.cutype = cutype
-        self.associatedAddress = associatedAddress
-        self.streetAddress = streetAddress
-        self.geographicLocation = geographicLocation
-
-
-    def canonicalCalendarUserAddress(self):
-        """
-            Return a CUA for this record, preferring in this order:
-            urn:x-uid: form
-            urn:uuid: form
-            mailto: form
-            /principals/__uids__/ form
-            first in calendarUserAddresses list (sorted)
-        """
-
-        sortedCuas = sorted(self.calendarUserAddresses)
-
-        for prefix in (
-            "urn:x-uid:",
-            "urn:uuid:",
-            "mailto:",
-            "/principals/__uids__/"
-        ):
-            for candidate in sortedCuas:
-                if candidate.startswith(prefix):
-                    return candidate
-
-        # fall back to using the first one
-        return sortedCuas[0]
-
-
-    def calendarsEnabled(self):
-        return True
-
-
-    def enabledAsOrganizer(self):
-        if self.cutype == "INDIVIDUAL":
-            return True
-        elif self.recordType == "GROUP":
-            return config.Scheduling.Options.AllowGroupAsOrganizer
-        elif self.recordType == "ROOM":
-            return config.Scheduling.Options.AllowLocationAsOrganizer
-        elif self.recordType == "RESOURCE":
-            return config.Scheduling.Options.AllowResourceAsOrganizer
-        else:
-            return False
-
-
-    def getCUType(self):
-        return self.cutype
-
-
-    def canAutoSchedule(self, organizer):
-        return succeed(False)
-
-
-    def getAutoScheduleMode(self, organizer):
-        return succeed("automatic")
-
-
-    def isProxyFor(self, other):
-        return succeed(False)
-
-
-
-def buildDirectory(homes=None):
-
-    directory = TestCalendarStoreDirectoryService()
-
-    # User accounts
-    for ctr in range(1, 100):
-        directory.addRecord(TestCalendarStoreDirectoryRecord(
-            "user%02d" % (ctr,),
-            ("user%02d" % (ctr,),),
-            "User %02d" % (ctr,),
-            frozenset((
-                "urn:x-uid:user%02d" % (ctr,),
-                "urn:uuid:user%02d" % (ctr,),
-                "mailto:user%02d at example.com" % (ctr,),
-            )),
-        ))
-
-    homes = set(homes) if homes is not None else set()
-    homes.update((
-        "home1",
-        "home2",
-        "home3",
-        "home_attachments",
-        "home_bad",
-        "home_defaults",
-        "home_no_splits",
-        "home_provision1",
-        "home_provision2",
-        "home_splits",
-        "home_splits_shared",
-        "uid1",
-        "uid2",
-        "new-home",
-        "xyzzy",
-    ))
-    for uid in homes:
-        directory.addRecord(buildDirectoryRecord(uid))
-
-    # Structured Locations
-    directory.addRecord(TestCalendarStoreDirectoryRecord(
-        "il1", ("il1",), "1 Infinite Loop", [],
-        cutype="ROOM",
-        geographicLocation="37.331741,-122.030333",
-        streetAddress="1 Infinite Loop, Cupertino, CA 95014"
-    ))
-    directory.addRecord(TestCalendarStoreDirectoryRecord(
-        "il2", ("il2",), "2 Infinite Loop", [],
-        cutype="ROOM",
-        geographicLocation="37.332633,-122.030502",
-        streetAddress="2 Infinite Loop, Cupertino, CA 95014"
-    ))
-    directory.addRecord(TestCalendarStoreDirectoryRecord(
-        "room1", ("room1",), "Conference Room One",
-        frozenset(("urn:x-uid:room1",)),
-        cutype="ROOM",
-        associatedAddress="il1",
-    ))
-    directory.addRecord(TestCalendarStoreDirectoryRecord(
-        "room2", ("room2",), "Conference Room Two",
-        frozenset(("urn:x-uid:room2",)),
-        cutype="ROOM",
-        associatedAddress="il2",
-    ))
-
-    return directory
-
-
-
-def buildDirectoryRecord(uid):
-    return TestCalendarStoreDirectoryRecord(
-        uid,
-        (uid,),
-        uid.capitalize(),
-        frozenset((
-            "urn:x-uid:{0}".format(uid,),
-            "urn:uuid:{0}".format(uid,),
-            "mailto:{0}@example.com".format(uid,),
-        )),
-    )
-
-
-
-def buildCalendarStore(testCase, notifierFactory, directoryService=None, homes=None):
-    if directoryService is None:
-        directoryService = buildDirectory(homes=homes)
-    return theStoreBuilder.buildStore(testCase, notifierFactory, directoryService)
-
-
-
 class CommonStoreTests(CommonCommonTests, TestCase):
 
     @inlineCallbacks
     def setUp(self):
         yield super(CommonStoreTests, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
+        yield self.buildStoreAndDirectory()
         yield self.populate()
 
 
@@ -277,10 +70,3 @@
             },
         },
     }
-
-
-    def storeUnderTest(self):
-        """
-        Create and return a L{CalendarStore} for testing.
-        """
-        return self._sqlCalendarStore

Modified: CalendarServer/trunk/txdav/carddav/datastore/test/common.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/test/common.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/carddav/datastore/test/common.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -201,18 +201,7 @@
         },
     }
 
-    def storeUnderTest(self):
-        """
-        Subclasses must override this to return an L{IAddressBookStore}
-        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 addressbook homes,
-        then addressbook names mapping to addressbook collections, and finally
-        addressbook object names mapping to addressbook object text.
-        """
-        raise NotImplementedError()
 
-
     def homeUnderTest(self, txn=None, name=None):
         """
         Get the addressbook home detailed by C{requirements['home1']}.

Modified: CalendarServer/trunk/txdav/carddav/datastore/test/test_file.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/test/test_file.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/carddav/datastore/test/test_file.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -60,6 +60,7 @@
     addressbookPath.parent().makedirs()
     storePath.copyTo(addressbookPath)
 
+    test.counter = 0
     test.notifierFactory = StubNotifierFactory()
     test.addressbookStore = AddressBookStore(storeRootPath, {"push": test.notifierFactory}, None)
     test.txn = test.addressbookStore.newTransaction(test.id() + " (old)")

Modified: CalendarServer/trunk/txdav/carddav/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/test/test_sql.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/carddav/datastore/test/test_sql.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -41,7 +41,7 @@
 from txdav.common.icommondatastore import NoSuchObjectResourceError
 from txdav.common.datastore.sql import EADDRESSBOOKTYPE, CommonObjectResource
 from txdav.common.datastore.sql_tables import  _ABO_KIND_PERSON, _ABO_KIND_GROUP, schema
-from txdav.common.datastore.test.util import buildStore, cleanStore
+from txdav.common.datastore.test.util import cleanStore
 from txdav.carddav.datastore.sql import AddressBook
 
 from txdav.xml.rfc2518 import GETContentLanguage, ResourceType
@@ -56,20 +56,10 @@
     @inlineCallbacks
     def setUp(self):
         yield super(AddressBookSQLStorageTests, self).setUp()
-        self._sqlStore = yield buildStore(
-            self,
-            self.notifierFactory,
-            homes=(
-                "home1",
-                "home2",
-                "home3",
-                "home_bad",
-                "home_empty",
-                "homeNew",
-                "new-home",
-                "uid1",
-                "uid2",
-                "xyzzy",
+        yield self.buildStoreAndDirectory(
+            extraUids=(
+                u"home_empty",
+                u"homeNew",
             )
         )
         yield self.populate()
@@ -96,13 +86,7 @@
         self.notifierFactory.reset()
 
 
-    def storeUnderTest(self):
-        """
-        Create and return a L{AddressBookStore} for testing.
-        """
-        return self._sqlStore
 
-
     @inlineCallbacks
     def assertAddressbooksSimilar(self, a, b, bAddressbookFilter=None):
         """
@@ -252,7 +236,7 @@
         operations, that we do not block other reads of the table.
         """
 
-        addressbookStore = self._sqlStore
+        addressbookStore = self.store
 
         txn1 = addressbookStore.newTransaction()
         txn2 = addressbookStore.newTransaction()
@@ -304,7 +288,7 @@
         Test that two concurrent attempts to PUT different address book object resources to the
         same address book home does not cause a deadlock.
         """
-        addressbookStore = self._sqlStore
+        addressbookStore = self.store
 
         # Provision the home and addressbook now
         txn = addressbookStore.newTransaction()
@@ -373,7 +357,7 @@
         C{INSERT} that violates a unique constraint.
         """
 
-        addressbookStore = self._sqlStore
+        addressbookStore = self.store
 
         txn1 = addressbookStore.newTransaction()
         txn2 = addressbookStore.newTransaction()
@@ -410,7 +394,7 @@
         """
         Test that kind property UID is stored correctly in database
         """
-        addressbookStore = self._sqlStore
+        addressbookStore = self.store
 
         # Provision the home and addressbook, one user and one group
         txn = addressbookStore.newTransaction()
@@ -456,7 +440,7 @@
         """
         Test that kind property vCard is stored correctly in database
         """
-        addressbookStore = self._sqlStore
+        addressbookStore = self.store
 
         # Provision the home and addressbook, one user and one group
         txn = addressbookStore.newTransaction()
@@ -547,7 +531,7 @@
         """
         Test that kind property vCard is stored correctly in database
         """
-        addressbookStore = self._sqlStore
+        addressbookStore = self.store
         cleanStore(self, addressbookStore)
 
         # Provision the home and addressbook, one user and one group
@@ -739,7 +723,7 @@
         work concurrently without an exception.
         """
 
-        addressbookStore = self._sqlStore
+        addressbookStore = self.store
 
         # Provision the home and addressbook now
         txn = addressbookStore.newTransaction()
@@ -799,7 +783,7 @@
         self.assertTrue(resource._locked)
 
         # Setup a new transaction to verify the lock and also verify wait behavior
-        newTxn = self._sqlStore.newTransaction()
+        newTxn = self.store.newTransaction()
         newResource = yield self.addressbookObjectUnderTest(txn=newTxn)
         try:
             yield newResource.lock(wait=False)
@@ -818,7 +802,7 @@
         self.assertTrue(resource._locked)
 
         # Setup a new transaction to verify the lock but pass in an alternative txn directly
-        newTxn = self._sqlStore.newTransaction()
+        newTxn = self.store.newTransaction()
 
         # FIXME: not sure why, but without this statement here, this portion of the test fails in a funny way.
         # Basically the query in the try block seems to execute twice, failing each time, one of which is caught,

Modified: CalendarServer/trunk/txdav/carddav/datastore/test/test_sql_sharing.py
===================================================================
--- CalendarServer/trunk/txdav/carddav/datastore/test/test_sql_sharing.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/carddav/datastore/test/test_sql_sharing.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -20,7 +20,7 @@
 
 from twistedcaldav.vcard import Component as VCard, Component
 from twext.python.clsprop import classproperty
-from txdav.common.datastore.test.util import CommonCommonTests, buildStore
+from txdav.common.datastore.test.util import CommonCommonTests
 from txdav.common.datastore.sql_tables import _BIND_MODE_READ, \
     _BIND_STATUS_INVITED, _BIND_MODE_DIRECT, _BIND_STATUS_ACCEPTED, \
     _BIND_MODE_WRITE
@@ -35,7 +35,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(BaseSharingTests, self).setUp()
-        self._sqlStore = yield buildStore(self, self.notifierFactory)
+        yield self.buildStoreAndDirectory()
         yield self.populate()
 
 
@@ -136,13 +136,6 @@
     group2_children = ["group2.vcf", "card1.vcf", "card3.vcf", ]
 
 
-    def storeUnderTest(self):
-        """
-        Create and return a L{CalendarStore} for testing.
-        """
-        return self._sqlStore
-
-
     @inlineCallbacks
     def _createShare(self, mode=_BIND_MODE_READ):
         inviteUID = yield self._inviteShare(mode)
@@ -1209,8 +1202,8 @@
         """
 
         # Create first events in different addressbook homes
-        txn1 = self._sqlStore.newTransaction()
-        txn2 = self._sqlStore.newTransaction()
+        txn1 = self.store.newTransaction()
+        txn2 = self.store.newTransaction()
 
         addressbook_uid1_in_txn1 = yield self.addressbookUnderTest(txn1, "addressbook", "user01")
         addressbook_uid2_in_txn2 = yield self.addressbookUnderTest(txn2, "addressbook", "user02")

Modified: CalendarServer/trunk/txdav/common/datastore/podding/resource.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/podding/resource.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/common/datastore/podding/resource.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -29,12 +29,12 @@
     deliverSchedulePrivilegeSet
 
 from txdav.xml import element as davxml
-from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers
 from txdav.common.datastore.podding.conduit import FailedCrossPodRequestError
 
 import base64
 import json
 
+
 class ConduitResource(ReadOnlyNoCopyResourceMixIn, DAVResourceWithoutChildrenMixin, DAVResource):
     """
     Podding cross-pod RPC conduit resource.
@@ -119,7 +119,7 @@
         """
 
         # Check shared secret
-        if not Servers.getThisServer().checkSharedSecret(request.headers):
+        if not self.store.directoryService().serversDB.getThisServer().checkSharedSecret(request.headers):
             self.log.error("Invalid shared secret header in cross-pod request")
             raise HTTPError(StatusResponse(responsecode.FORBIDDEN, "Not authorized to make this request"))
 

Modified: CalendarServer/trunk/txdav/common/datastore/podding/test/test_conduit.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/podding/test/test_conduit.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/common/datastore/podding/test/test_conduit.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -31,11 +31,9 @@
 
 from txdav.caldav.datastore.query.filter import Filter
 from txdav.caldav.datastore.scheduling.freebusy import generateFreeBusyInfo
-from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers, Server
+from txdav.caldav.datastore.scheduling.ischedule.localservers import ServersDB, Server
 from txdav.caldav.datastore.sql import ManagedAttachment
 from txdav.caldav.datastore.test.common import CaptureProtocol
-from txdav.caldav.datastore.test.util import buildCalendarStore, \
-    TestCalendarStoreDirectoryRecord
 from txdav.common.datastore.podding.conduit import PoddingConduit, \
     FailedCrossPodRequestError
 from txdav.common.datastore.podding.resource import ConduitResource
@@ -63,36 +61,19 @@
     @inlineCallbacks
     def setUp(self):
         yield super(TestConduit, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
-        self.directory = self._sqlCalendarStore.directoryService()
 
-        for ctr in range(1, 100):
-            self.directory.addRecord(TestCalendarStoreDirectoryRecord(
-                "puser{:02d}".format(ctr),
-                ("puser{:02d}".format(ctr),),
-                "Puser {:02d}".format(ctr),
-                frozenset((
-                    "urn:uuid:puser{:02d}".format(ctr),
-                    "mailto:puser{:02d}@example.com".format(ctr),
-                )),
-                thisServer=False,
-            ))
+        serversDB = ServersDB()
+        serversDB.addServer(Server("A", "http://127.0.0.1", "A", True))
+        serversDB.addServer(Server("B", "http://127.0.0.2", "B", False))
 
+        yield self.buildStoreAndDirectory(serversDB=serversDB)
+
         self.site.resource.putChild("conduit", ConduitResource(self.site.resource, self.storeUnderTest()))
 
-        self.thisServer = Server("A", "http://127.0.0.1", "A", True)
-        Servers.addServer(self.thisServer)
-
         yield self.populate()
 
 
-    def storeUnderTest(self):
-        """
-        Return a store for testing.
-        """
-        return self._sqlCalendarStore
 
-
     @inlineCallbacks
     def populate(self):
         yield populateCalendarsFrom(self.requirements, self.storeUnderTest())

Modified: CalendarServer/trunk/txdav/common/datastore/podding/test/test_external_home.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/podding/test/test_external_home.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/common/datastore/podding/test/test_external_home.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -16,10 +16,9 @@
 
 from twisted.internet.defer import inlineCallbacks
 
-from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers, \
-    Server
-from txdav.caldav.datastore.test.util import buildCalendarStore, \
-    TestCalendarStoreDirectoryRecord
+from txdav.caldav.datastore.scheduling.ischedule.localservers import (
+    ServersDB, Server
+)
 from txdav.common.datastore.podding.resource import ConduitResource
 from txdav.common.datastore.sql_tables import _HOME_STATUS_NORMAL, \
     _HOME_STATUS_EXTERNAL
@@ -34,34 +33,16 @@
     @inlineCallbacks
     def setUp(self):
         yield super(ExternalHome, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
-        self.directory = self._sqlCalendarStore.directoryService()
 
-        for ctr in range(1, 100):
-            self.directory.addRecord(TestCalendarStoreDirectoryRecord(
-                "puser{:02d}".format(ctr),
-                ("puser{:02d}".format(ctr),),
-                "Puser {:02d}".format(ctr),
-                frozenset((
-                    "urn:uuid:puser{:02d}".format(ctr),
-                    "mailto:puser{:02d}@example.com".format(ctr),
-                )),
-                thisServer=False,
-            ))
+        serversDB = ServersDB()
+        serversDB.addServer(Server("A", "http://127.0.0.1", "A", True))
+        serversDB.addServer(Server("B", "http://127.0.0.2", "B", False))
 
+        yield self.buildStoreAndDirectory(serversDB=serversDB)
+
         self.site.resource.putChild("conduit", ConduitResource(self.site.resource, self.storeUnderTest()))
 
-        self.thisServer = Server("A", "http://127.0.0.1", "A", True)
-        Servers.addServer(self.thisServer)
 
-
-    def storeUnderTest(self):
-        """
-        Return a store for testing.
-        """
-        return self._sqlCalendarStore
-
-
     @inlineCallbacks
     def test_validNormalHome(self):
         """

Modified: CalendarServer/trunk/txdav/common/datastore/podding/test/test_resource.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/podding/test/test_resource.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/common/datastore/podding/test/test_resource.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -23,8 +23,9 @@
 
 from twisted.internet.defer import inlineCallbacks, succeed
 
-from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers, Server
-from txdav.caldav.datastore.test.util import buildCalendarStore
+from txdav.caldav.datastore.scheduling.ischedule.localservers import (
+    ServersDB, Server
+)
 from txdav.common.datastore.podding.resource import ConduitResource
 from txdav.common.datastore.test.util import populateCalendarsFrom, CommonCommonTests
 import json
@@ -45,24 +46,17 @@
     @inlineCallbacks
     def setUp(self):
         yield super(ConduitPOST, self).setUp()
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory)
-        self.directory = self._sqlCalendarStore.directoryService()
 
-        self.site.resource.putChild("conduit", ConduitResource(self.site.resource, self.storeUnderTest()))
-
+        serversDB = ServersDB()
         self.thisServer = Server("A", "http://127.0.0.1", "A", True)
-        Servers.addServer(self.thisServer)
+        serversDB.addServer(self.thisServer)
+        yield self.buildStoreAndDirectory(serversDB=serversDB)
 
+        self.site.resource.putChild("conduit", ConduitResource(self.site.resource, self.storeUnderTest()))
+
         yield self.populate()
 
 
-    def storeUnderTest(self):
-        """
-        Return a store for testing.
-        """
-        return self._sqlCalendarStore
-
-
     @inlineCallbacks
     def populate(self):
         yield populateCalendarsFrom(self.requirements, self.storeUnderTest())

Modified: CalendarServer/trunk/txdav/common/datastore/podding/test/util.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/podding/test/util.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/common/datastore/podding/test/util.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -16,14 +16,14 @@
 
 from twisted.internet.defer import inlineCallbacks, returnValue
 
-from txdav.caldav.datastore.scheduling.ischedule.localservers import Server, \
-    Servers
-from txdav.caldav.datastore.test.util import \
-    TestCalendarStoreDirectoryRecord, TestCalendarStoreDirectoryService
+from txdav.caldav.datastore.scheduling.ischedule.localservers import (
+    Server, ServersDB
+)
 from txdav.common.datastore.podding.conduit import PoddingConduit
 from txdav.common.datastore.sql_tables import _BIND_MODE_WRITE
-from txdav.common.datastore.test.util import CommonCommonTests, SQLStoreBuilder,\
-    theStoreBuilder
+from txdav.common.datastore.test.util import (
+    CommonCommonTests, SQLStoreBuilder, buildTestDirectory
+)
 
 import txweb2.dav.test.util
 
@@ -31,6 +31,7 @@
 
 import json
 
+
 class FakeConduitRequest(object):
     """
     A conduit request that sends messages internally rather than using HTTP
@@ -103,37 +104,54 @@
     def setUp(self):
         yield super(MultiStoreConduitTest, self).setUp()
 
-        server1 = Server("A", "http://127.0.0.1:8008", "A", True)
-        Servers.addServer(server1)
+        # Store 1
+        serversDB1 = ServersDB()
+        server1a = Server("A", "http://127.0.0.1:8008", "A", True)
+        serversDB1.addServer(server1a)
+        server1b = Server("B", "http://127.0.0.1:8108", "B", False)
+        serversDB1.addServer(server1b)
+        yield self.buildStoreAndDirectory(serversDB=serversDB1)
+        self.store.queryCacher = None     # Cannot use query caching
+        self.store.conduit = self.makeConduit(self.store)
 
-        server2 = Server("B", "http://127.0.0.1:8108", "B", False)
-        Servers.addServer(server2)
+        # Store 2
+        serversDB2 = ServersDB()
+        server2a = Server("A", "http://127.0.0.1:8008", "A", False)
+        serversDB2.addServer(server2a)
+        server2b = Server("B", "http://127.0.0.1:8108", "B", True)
+        serversDB2.addServer(server2b)
 
-        self._sqlCalendarStore1 = yield self.makeStore(theStoreBuilder, True, server1, server2)
-        self._sqlCalendarStore2 = yield self.makeStore(self.theStoreBuilder2, False, server1, server2)
+        self.store2 = yield self.buildStore(self.theStoreBuilder2)
+        directory2 = buildTestDirectory(
+            self.store2, self.mktemp(), serversDB=serversDB2
+        )
 
-        FakeConduitRequest.addServerStore(server1, self._sqlCalendarStore1)
-        FakeConduitRequest.addServerStore(server2, self._sqlCalendarStore2)
+        self.store2.setDirectoryService(directory2)
+        self.store2.queryCacher = None     # Cannot use query caching
+        self.store2.conduit = self.makeConduit(self.store2)
 
+        FakeConduitRequest.addServerStore(server1a, self.store)
+        FakeConduitRequest.addServerStore(server2b, self.store2)
 
-    def storeUnderTest(self):
-        """
-        Return a store for testing.
-        """
-        return self._sqlCalendarStore1
 
 
+    def configure(self):
+        super(MultiStoreConduitTest, self).configure()
+        self.config.Servers.Enabled = True
+
+
     def otherStoreUnderTest(self):
         """
         Return a store for testing.
         """
-        return self._sqlCalendarStore2
+        return self.store2
 
 
     def newOtherTransaction(self):
         assert self.otherTransaction is None
         store2 = self.otherStoreUnderTest()
         txn = store2.newTransaction()
+
         @inlineCallbacks
         def maybeCommitThis():
             try:
@@ -165,50 +183,7 @@
         self.otherTransaction = None
 
 
-    @inlineCallbacks
-    def makeStore(self, builder, internal, server1, server2):
 
-        directory = self.makeDirectory(internal, server1, server2)
-        store = yield builder.buildStore(self, self.notifierFactory, directory)
-        store.queryCacher = None     # Cannot use query caching
-        store.conduit = self.makeConduit(store)
-        returnValue(store)
-
-
-    def makeDirectory(self, internal, server1, server2):
-
-        directory = TestCalendarStoreDirectoryService()
-
-        # User accounts
-        for ctr in range(1, 100):
-            directory.addRecord(TestCalendarStoreDirectoryRecord(
-                "user%02d" % (ctr,),
-                ("user%02d" % (ctr,),),
-                "User %02d" % (ctr,),
-                frozenset((
-                    "urn:uuid:user%02d" % (ctr,),
-                    "mailto:user%02d at example.com" % (ctr,),
-                )),
-                thisServer=internal,
-                server=server1
-            ))
-
-        for ctr in range(1, 100):
-            directory.addRecord(TestCalendarStoreDirectoryRecord(
-                "puser{:02d}".format(ctr),
-                ("puser{:02d}".format(ctr),),
-                "Puser {:02d}".format(ctr),
-                frozenset((
-                    "urn:uuid:puser{:02d}".format(ctr),
-                    "mailto:puser{:02d}@example.com".format(ctr),
-                )),
-                thisServer=not internal,
-                server=server2
-            ))
-
-        return directory
-
-
     def makeConduit(self, store):
         conduit = PoddingConduit(store)
         conduit.conduitRequestClass = FakeConduitRequest
@@ -223,6 +198,7 @@
         yield calendar.inviteUserToShare(shareeGUID, _BIND_MODE_WRITE, "shared", shareName="shared-calendar")
         yield self.commit()
 
+        # ACK: home2 is None
         home2 = yield self.homeUnderTest(txn=self.newOtherTransaction(), name=shareeGUID)
         yield home2.acceptShare("shared-calendar")
         yield self.otherCommit()

Added: CalendarServer/trunk/txdav/common/datastore/test/accounts/accounts.xml
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/accounts/accounts.xml	                        (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/test/accounts/accounts.xml	2014-05-09 23:38:16 UTC (rev 13468)
@@ -0,0 +1,1829 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2014 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ -->
+
+<!DOCTYPE accounts SYSTEM "accounts.dtd">
+
+<directory realm="Test Realm">
+  <record type="user">
+    <uid>D11F03A0-97EA-48AF-9A6C-FAC7F3975766</uid>
+    <short-name>admin</short-name>
+    <password>nimda</password>
+    <full-name>Administrators</full-name>
+  </record>
+  <record type="user">
+    <uid>6423F94A-6B76-4A3A-815B-D52CFD77935D</uid>
+    <guid>6423F94A-6B76-4A3A-815B-D52CFD77935D</guid>
+    <short-name>wsanchez</short-name>
+    <password>zehcnasw</password>
+    <full-name>Wilfredo Sanchez</full-name>
+    <email>wsanchez at example.com</email>
+  </record>
+  <record type="user">
+    <uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</uid>
+    <guid>5A985493-EE2C-4665-94CF-4DFEA3A89500</guid>
+    <short-name>cdaboo</short-name>
+    <password>oobadc</password>
+    <full-name>Cyrus Daboo</full-name>
+    <email>cdaboo at example.com</email>
+  </record>
+  <record type="user">
+    <uid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</uid>
+    <short-name>lecroy</short-name>
+    <password>yorcel</password>
+    <full-name>Chris Lecroy</full-name>
+    <email>lecroy at example.com</email>
+  </record>
+  <record type="user">
+    <uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
+    <short-name>dreid</short-name>
+    <password>dierd</password>
+    <full-name>David Reid</full-name>
+    <email>dreid at example.com</email>
+  </record>
+  <record type="user">
+    <uid>8E04787E-336D-41ED-A70B-D233AD0DCE6F</uid>
+    <guid>8E04787E-336D-41ED-A70B-D233AD0DCE6F</guid>
+    <short-name>doublequotes</short-name>
+    <password>setouqelbuod</password>
+    <full-name>Double "quotey" Quotes</full-name>
+    <email>doublequotes at example.com</email>
+  </record>
+  <record type="user">
+    <uid>37DB0C90-4DB1-4932-BC69-3DAB66F374F5</uid>
+    <guid>37DB0C90-4DB1-4932-BC69-3DAB66F374F5</guid>
+    <short-name>purgeuser</short-name>
+    <password>purgeuser</password>
+    <full-name>Purge User</full-name>
+    <email>purgeuser at example.com</email>
+  </record>
+  <record type="user">
+    <uid>uid1</uid>
+    <short-name>uid1</short-name>
+    <password>uid1</password>
+    <full-name>uid One</full-name>
+    <email>uid1 at example.com</email>
+  </record>
+  <record type="user">
+    <uid>uid2</uid>
+    <short-name>uid2</short-name>
+    <password>uid2</password>
+    <full-name>uid Two</full-name>
+    <email>uid2 at example.com</email>
+  </record>
+
+  <record type="user">
+    <uid>home1</uid>
+    <short-name>home1</short-name>
+    <password>home1</password>
+    <full-name>Home One</full-name>
+    <email>home1 at example.com</email>
+  </record>
+  <record type="user">
+    <uid>home2</uid>
+    <short-name>home2</short-name>
+    <password>home2</password>
+    <full-name>Home Two</full-name>
+    <email>home2 at example.com</email>
+  </record>
+    <record type="user">
+    <uid>home3</uid>
+    <short-name>home3</short-name>
+    <password>home3</password>
+    <full-name>Home Three</full-name>
+    <email>home3 at example.com</email>
+  </record>
+
+  <record type="user">
+    <uid>home_splits</uid>
+    <short-name>home_splits</short-name>
+    <password>home_splits</password>
+    <full-name>Home Splits</full-name>
+    <email>home_splits at example.com</email>
+  </record>
+  <record type="user">
+    <uid>home_no_splits</uid>
+    <short-name>home_no_splits</short-name>
+    <password>home_no_splits</password>
+    <full-name>Home NoSplits</full-name>
+    <email>home_no_splits at example.com</email>
+  </record>
+  <record type="user">
+    <uid>home_defaults</uid>
+    <short-name>home_defaults</short-name>
+    <password>home_defaults</password>
+    <full-name>Home Defaults</full-name>
+    <email>home_defaults at example.com</email>
+  </record>
+  <record type="user">
+    <uid>home_splits_shared</uid>
+    <short-name>home_splits_shared</short-name>
+    <password>home_splits_shared</password>
+    <full-name>Home SplitsShared</full-name>
+    <email>home_splits_shared at example.com</email>
+  </record>
+  <record type="user">
+    <uid>new-home</uid>
+    <short-name>new-home</short-name>
+    <password>new-home</password>
+    <full-name>New Home</full-name>
+    <email>new-home at example.com</email>
+  </record>
+  <record type="user">
+    <uid>home_provision1</uid>
+    <short-name>home_provision1</short-name>
+    <password>home_provision1</password>
+    <full-name>Home Provision1</full-name>
+    <email>home_provision1 at example.com</email>
+  </record>
+  <record type="user">
+    <uid>home_provision2</uid>
+    <short-name>home_provision2</short-name>
+    <password>home_provision2</password>
+    <full-name>Home Provision2</full-name>
+    <email>home_provision2 at example.com</email>
+  </record>
+  <record type="user">
+    <uid>home_attachments</uid>
+    <short-name>home_attachments</short-name>
+    <password>home_attachments</password>
+    <full-name>Home Attachments</full-name>
+    <email>home_attachments at example.com</email>
+  </record>
+  <record type="user">
+    <uid>xyzzy</uid>
+    <short-name>xyzzy</short-name>
+    <password>xyzzy</password>
+    <full-name>xyzzy</full-name>
+    <email>xyzzy at example.com</email>
+  </record>
+
+  <record type="user">
+    <uid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</uid>
+    <short-name>nocalendar</short-name>
+    <password>radnelacon</password>
+    <full-name>No Calendar</full-name>
+    <email>nocalendar at example.com</email>
+  </record>
+  <record type="user">
+    <uid>7423F94A-6B76-4A3A-815B-D52CFD77935D</uid>
+    <short-name>usera</short-name>
+    <password>a</password>
+    <full-name>a</full-name>
+    <email>a at example.com</email>
+  </record>
+  <record type="user">
+    <uid>8A985493-EE2C-4665-94CF-4DFEA3A89500</uid>
+    <short-name>userb</short-name>
+    <password>b</password>
+    <full-name>b</full-name>
+    <email>b at example.com</email>
+  </record>
+  <record type="user">
+    <uid>9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD2</uid>
+    <short-name>userc</short-name>
+    <password>c</password>
+    <full-name>c</full-name>
+    <email>c at example.com</email>
+  </record>
+  <record type="user">
+    <uid>9E1FFAC4-3CCD-45A1-8272-D161C92D2EEE</uid>
+    <short-name>usercalonly</short-name>
+    <password>a</password>
+    <full-name>a calonly</full-name>
+    <email>a-calonly at example.com</email>
+  </record>
+  <record type="user">
+    <uid>7678EC8A-A069-4E82-9066-7279C6718507</uid>
+    <short-name>useradbkonly</short-name>
+    <password>a</password>
+    <full-name>a adbkonly</full-name>
+    <email>a-adbkonly at example.com</email>
+  </record>
+  <record type="user">
+    <uid>320B73A1-46E2-4180-9563-782DFDBE1F63</uid>
+    <short-name>nonascii</short-name>
+    <short-name>nonascii佐藤</short-name>
+    <password>a</password>
+    <full-name>佐藤佐藤佐藤</full-name>
+    <email>nonascii at example.com</email>
+  </record>
+  <record type="user">
+    <uid>FC465590-E9E9-4746-ACE8-6C756A49FE4D</uid>
+    <short-name>delegator</short-name>
+    <password>a</password>
+    <full-name>Calendar Delegator</full-name>
+    <email>calendardelegator at example.com</email>
+  </record>
+  <record type="user">
+    <uid>EC465590-E9E9-4746-ACE8-6C756A49FE4D</uid>
+    <short-name>occasionaldelegate</short-name>
+    <password>a</password>
+    <full-name>Occasional Delegate</full-name>
+    <email>occasional at example.com</email>
+  </record>
+  <record type="user">
+    <uid>46D9D716-CBEE-490F-907A-66FA6C3767FF</uid>
+    <short-name>delegateviagroup</short-name>
+    <password>a</password>
+    <full-name>Delegate Via Group</full-name>
+    <email>delegateviagroup at example.com</email>
+  </record>
+  <record type="group">
+    <uid>00599DAF-3E75-42DD-9DB7-52617E79943F</uid>
+    <short-name>delegategroup</short-name>
+    <full-name>Delegate Group</full-name>
+    <member-uid>46D9D716-CBEE-490F-907A-66FA6C3767FF</member-uid>
+  </record>
+
+  <record type="user">
+    <uid>__wsanchez1__</uid>
+    <short-name>wsanchez1</short-name>
+    <short-name>wilfredo_sanchez</short-name>
+    <password>zehcnasw</password>
+    <full-name>Wilfredo Sanchez</full-name>
+    <email>wsanchez at bitbucket.calendarserver.org</email>
+    <email>wsanchez at devnull.twistedmatrix.com</email>
+  </record>
+
+  <record type="user">
+    <uid>__glyph1__</uid>
+    <short-name>glyph1</short-name>
+    <password>hpylg</password>
+    <full-name>Glyph Lefkowitz</full-name>
+    <email>glyph at bitbucket.calendarserver.org</email>
+    <email>glyph at devnull.twistedmatrix.com</email>
+  </record>
+
+  <record type="user">
+    <uid>__sagen1__</uid>
+    <short-name>sagen</short-name>
+    <short-name>sagen1</short-name>
+    <password>negas</password>
+    <full-name>Morgen Sagen</full-name>
+    <email>sagen at bitbucket.calendarserver.org</email>
+  </record>
+
+  <record type="user">
+    <uid>__cdaboo1__</uid>
+    <short-name>cdaboo1</short-name>
+    <password>suryc</password>
+    <full-name>Cyrus Daboo</full-name>
+    <email>cdaboo at bitbucket.calendarserver.org</email>
+  </record>
+
+  <record type="user">
+    <uid>__dre1__</uid>
+    <short-name>dre1</short-name>
+    <short-name>dre</short-name>
+    <password>erd</password>
+    <full-name>Andre LaBranche</full-name>
+    <email>dre at bitbucket.calendarserver.org</email>
+  </record>
+
+  <record type="group">
+    <uid>__top_group_1__</uid>
+    <short-name>top-group-1</short-name>
+    <full-name>Top Group 1</full-name>
+    <email>topgroup1 at example.com</email>
+    <member-uid>__wsanchez1__</member-uid>
+    <member-uid>__glyph1__</member-uid>
+    <member-uid>__sub_group_1__</member-uid>
+  </record>
+
+  <record type="group">
+    <uid>__sub_group_1__</uid>
+    <short-name>sub-group-1</short-name>
+    <full-name>Sub Group 1</full-name>
+    <email>subgroup1 at example.com</email>
+    <member-uid>__sagen1__</member-uid>
+    <member-uid>__cdaboo1__</member-uid>
+  </record>
+
+
+  <record type="group">
+    <uid>9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
+    <short-name>managers</short-name>
+    <password>managers</password>
+    <full-name>Managers</full-name>
+      <member-uid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</member-uid>
+  </record>
+  <record type="group">
+    <uid>admin</uid>
+    <short-name>admin</short-name>
+    <password>admin</password>
+    <full-name>Administrators</full-name>
+      <member-uid>9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</member-uid>
+  </record>
+  <record type="group">
+    <uid>grunts</uid>
+    <short-name>grunts</short-name>
+    <password>grunts</password>
+    <full-name>We do all the work</full-name>
+      <member-uid>6423F94A-6B76-4A3A-815B-D52CFD77935D</member-uid>
+      <member-uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</member-uid>
+      <member-uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</member-uid>
+  </record>
+  <record type="group">
+    <uid>right_coast</uid>
+    <short-name>right_coast</short-name>
+    <password>right_coast</password>
+    <full-name>East Coast</full-name>
+      <member-uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</member-uid>
+  </record>
+  <record type="group">
+    <uid>left_coast</uid>
+    <short-name>left_coast</short-name>
+    <password>left_coast</password>
+    <full-name>West Coast</full-name>
+      <member-uid>6423F94A-6B76-4A3A-815B-D52CFD77935D</member-uid>
+      <member-uid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</member-uid>
+      <member-uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</member-uid>
+  </record>
+  <record type="group">
+    <uid>both_coasts</uid>
+    <short-name>both_coasts</short-name>
+    <password>both_coasts</password>
+    <full-name>Both Coasts</full-name>
+      <member-uid>right_coast</member-uid>
+      <member-uid>left_coast</member-uid>
+  </record>
+  <record type="group">
+    <uid>recursive1_coasts</uid>
+    <short-name>recursive1_coasts</short-name>
+    <password>recursive1_coasts</password>
+    <full-name>Recursive1 Coasts</full-name>
+      <member-uid>recursive2_coasts</member-uid>
+      <member-uid>6423F94A-6B76-4A3A-815B-D52CFD77935D</member-uid>
+  </record>
+  <record type="group">
+    <uid>recursive2_coasts</uid>
+    <short-name>recursive2_coasts</short-name>
+    <password>recursive2_coasts</password>
+    <full-name>Recursive2 Coasts</full-name>
+      <member-uid>recursive1_coasts</member-uid>
+      <member-uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</member-uid>
+  </record>
+  <record type="group">
+    <uid>non_calendar_group</uid>
+    <short-name>non_calendar_group</short-name>
+    <password>non_calendar_group</password>
+    <full-name>Non-calendar group</full-name>
+      <member-uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</member-uid>
+      <member-uid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</member-uid>
+  </record>
+
+  <!-- Calverify test records -->
+
+  <record type="user">
+    <uid>D46F3D71-04B7-43C2-A7B6-6F92F92E61D0</uid>
+    <guid>D46F3D71-04B7-43C2-A7B6-6F92F92E61D0</guid>
+    <short-name>example1</short-name>
+    <password>example</password>
+    <full-name>Example User1</full-name>
+    <email>example1 at example.com</email>
+  </record>
+
+  <record type="user">
+    <uid>47B16BB4-DB5F-4BF6-85FE-A7DA54230F92</uid>
+    <guid>47B16BB4-DB5F-4BF6-85FE-A7DA54230F92</guid>
+    <short-name>example2</short-name>
+    <password>example</password>
+    <full-name>Example User2</full-name>
+    <email>example2 at example.com</email>
+  </record>
+
+  <record type="user">
+    <uid>AC478592-7783-44D1-B2AE-52359B4E8415</uid>
+    <guid>AC478592-7783-44D1-B2AE-52359B4E8415</guid>
+    <short-name>example3</short-name>
+    <password>example</password>
+    <full-name>Example User3</full-name>
+    <email>example3 at example.com</email>
+  </record>
+
+  <record type="user">
+    <uid>A89E3A97-1658-4E45-A185-479F3E49D446</uid>
+    <guid>A89E3A97-1658-4E45-A185-479F3E49D446</guid>
+    <short-name>example4</short-name>
+    <password>example</password>
+    <full-name>Example User4</full-name>
+    <email>example4 at example.com</email>
+  </record>
+
+
+<record type="user">
+    <uid>user01</uid>
+    <short-name>user01</short-name>
+    <password>user01</password>
+    <full-name>User 01</full-name>
+    <email>user01 at example.com</email>
+</record>
+<record type="user">
+    <uid>user02</uid>
+    <short-name>user02</short-name>
+    <password>user02</password>
+    <full-name>User 02</full-name>
+    <email>user02 at example.com</email>
+</record>
+<record type="user">
+    <uid>user03</uid>
+    <short-name>user03</short-name>
+    <password>user03</password>
+    <full-name>User 03</full-name>
+    <email>user03 at example.com</email>
+</record>
+<record type="user">
+    <uid>user04</uid>
+    <short-name>user04</short-name>
+    <password>user04</password>
+    <full-name>User 04</full-name>
+    <email>user04 at example.com</email>
+</record>
+<record type="user">
+    <uid>user05</uid>
+    <short-name>user05</short-name>
+    <password>user05</password>
+    <full-name>User 05</full-name>
+    <email>user05 at example.com</email>
+</record>
+<record type="user">
+    <uid>user06</uid>
+    <short-name>user06</short-name>
+    <password>user06</password>
+    <full-name>User 06</full-name>
+    <email>user06 at example.com</email>
+</record>
+<record type="user">
+    <uid>user07</uid>
+    <short-name>user07</short-name>
+    <password>user07</password>
+    <full-name>User 07</full-name>
+    <email>user07 at example.com</email>
+</record>
+<record type="user">
+    <uid>user08</uid>
+    <short-name>user08</short-name>
+    <password>user08</password>
+    <full-name>User 08</full-name>
+    <email>user08 at example.com</email>
+</record>
+<record type="user">
+    <uid>user09</uid>
+    <short-name>user09</short-name>
+    <password>user09</password>
+    <full-name>User 09</full-name>
+    <email>user09 at example.com</email>
+</record>
+<record type="user">
+    <uid>user10</uid>
+    <short-name>user10</short-name>
+    <password>user10</password>
+    <full-name>User 10</full-name>
+    <email>user10 at example.com</email>
+</record>
+<record type="user">
+    <uid>user11</uid>
+    <short-name>user11</short-name>
+    <password>user11</password>
+    <full-name>User 11</full-name>
+    <email>user11 at example.com</email>
+</record>
+<record type="user">
+    <uid>user12</uid>
+    <short-name>user12</short-name>
+    <password>user12</password>
+    <full-name>User 12</full-name>
+    <email>user12 at example.com</email>
+</record>
+<record type="user">
+    <uid>user13</uid>
+    <short-name>user13</short-name>
+    <password>user13</password>
+    <full-name>User 13</full-name>
+    <email>user13 at example.com</email>
+</record>
+<record type="user">
+    <uid>user14</uid>
+    <short-name>user14</short-name>
+    <password>user14</password>
+    <full-name>User 14</full-name>
+    <email>user14 at example.com</email>
+</record>
+<record type="user">
+    <uid>user15</uid>
+    <short-name>user15</short-name>
+    <password>user15</password>
+    <full-name>User 15</full-name>
+    <email>user15 at example.com</email>
+</record>
+<record type="user">
+    <uid>user16</uid>
+    <short-name>user16</short-name>
+    <password>user16</password>
+    <full-name>User 16</full-name>
+    <email>user16 at example.com</email>
+</record>
+<record type="user">
+    <uid>user17</uid>
+    <short-name>user17</short-name>
+    <password>user17</password>
+    <full-name>User 17</full-name>
+    <email>user17 at example.com</email>
+</record>
+<record type="user">
+    <uid>user18</uid>
+    <short-name>user18</short-name>
+    <password>user18</password>
+    <full-name>User 18</full-name>
+    <email>user18 at example.com</email>
+</record>
+<record type="user">
+    <uid>user19</uid>
+    <short-name>user19</short-name>
+    <password>user19</password>
+    <full-name>User 19</full-name>
+    <email>user19 at example.com</email>
+</record>
+<record type="user">
+    <uid>user20</uid>
+    <short-name>user20</short-name>
+    <password>user20</password>
+    <full-name>User 20</full-name>
+    <email>user20 at example.com</email>
+</record>
+<record type="user">
+    <uid>user21</uid>
+    <short-name>user21</short-name>
+    <password>user21</password>
+    <full-name>User 21</full-name>
+    <email>user21 at example.com</email>
+</record>
+<record type="user">
+    <uid>user22</uid>
+    <short-name>user22</short-name>
+    <password>user22</password>
+    <full-name>User 22</full-name>
+    <email>user22 at example.com</email>
+</record>
+<record type="user">
+    <uid>user23</uid>
+    <short-name>user23</short-name>
+    <password>user23</password>
+    <full-name>User 23</full-name>
+    <email>user23 at example.com</email>
+</record>
+<record type="user">
+    <uid>user24</uid>
+    <short-name>user24</short-name>
+    <password>user24</password>
+    <full-name>User 24</full-name>
+    <email>user24 at example.com</email>
+</record>
+<record type="user">
+    <uid>user25</uid>
+    <short-name>user25</short-name>
+    <password>user25</password>
+    <full-name>User 25</full-name>
+    <email>user25 at example.com</email>
+</record>
+<record type="user">
+    <uid>user26</uid>
+    <short-name>user26</short-name>
+    <password>user26</password>
+    <full-name>User 26</full-name>
+    <email>user26 at example.com</email>
+</record>
+<record type="user">
+    <uid>user27</uid>
+    <short-name>user27</short-name>
+    <password>user27</password>
+    <full-name>User 27</full-name>
+    <email>user27 at example.com</email>
+</record>
+<record type="user">
+    <uid>user28</uid>
+    <short-name>user28</short-name>
+    <password>user28</password>
+    <full-name>User 28</full-name>
+    <email>user28 at example.com</email>
+</record>
+<record type="user">
+    <uid>user29</uid>
+    <short-name>user29</short-name>
+    <password>user29</password>
+    <full-name>User 29</full-name>
+    <email>user29 at example.com</email>
+</record>
+<record type="user">
+    <uid>user30</uid>
+    <short-name>user30</short-name>
+    <password>user30</password>
+    <full-name>User 30</full-name>
+    <email>user30 at example.com</email>
+</record>
+<record type="user">
+    <uid>user31</uid>
+    <short-name>user31</short-name>
+    <password>user31</password>
+    <full-name>User 31</full-name>
+    <email>user31 at example.com</email>
+</record>
+<record type="user">
+    <uid>user32</uid>
+    <short-name>user32</short-name>
+    <password>user32</password>
+    <full-name>User 32</full-name>
+    <email>user32 at example.com</email>
+</record>
+<record type="user">
+    <uid>user33</uid>
+    <short-name>user33</short-name>
+    <password>user33</password>
+    <full-name>User 33</full-name>
+    <email>user33 at example.com</email>
+</record>
+<record type="user">
+    <uid>user34</uid>
+    <short-name>user34</short-name>
+    <password>user34</password>
+    <full-name>User 34</full-name>
+    <email>user34 at example.com</email>
+</record>
+<record type="user">
+    <uid>user35</uid>
+    <short-name>user35</short-name>
+    <password>user35</password>
+    <full-name>User 35</full-name>
+    <email>user35 at example.com</email>
+</record>
+<record type="user">
+    <uid>user36</uid>
+    <short-name>user36</short-name>
+    <password>user36</password>
+    <full-name>User 36</full-name>
+    <email>user36 at example.com</email>
+</record>
+<record type="user">
+    <uid>user37</uid>
+    <short-name>user37</short-name>
+    <password>user37</password>
+    <full-name>User 37</full-name>
+    <email>user37 at example.com</email>
+</record>
+<record type="user">
+    <uid>user38</uid>
+    <short-name>user38</short-name>
+    <password>user38</password>
+    <full-name>User 38</full-name>
+    <email>user38 at example.com</email>
+</record>
+<record type="user">
+    <uid>user39</uid>
+    <short-name>user39</short-name>
+    <password>user39</password>
+    <full-name>User 39</full-name>
+    <email>user39 at example.com</email>
+</record>
+<record type="user">
+    <uid>user40</uid>
+    <short-name>user40</short-name>
+    <password>user40</password>
+    <full-name>User 40</full-name>
+    <email>user40 at example.com</email>
+</record>
+<record type="user">
+    <uid>user41</uid>
+    <short-name>user41</short-name>
+    <password>user41</password>
+    <full-name>User 41</full-name>
+    <email>user41 at example.com</email>
+</record>
+<record type="user">
+    <uid>user42</uid>
+    <short-name>user42</short-name>
+    <password>user42</password>
+    <full-name>User 42</full-name>
+    <email>user42 at example.com</email>
+</record>
+<record type="user">
+    <uid>user43</uid>
+    <short-name>user43</short-name>
+    <password>user43</password>
+    <full-name>User 43</full-name>
+    <email>user43 at example.com</email>
+</record>
+<record type="user">
+    <uid>user44</uid>
+    <short-name>user44</short-name>
+    <password>user44</password>
+    <full-name>User 44</full-name>
+    <email>user44 at example.com</email>
+</record>
+<record type="user">
+    <uid>user45</uid>
+    <short-name>user45</short-name>
+    <password>user45</password>
+    <full-name>User 45</full-name>
+    <email>user45 at example.com</email>
+</record>
+<record type="user">
+    <uid>user46</uid>
+    <short-name>user46</short-name>
+    <password>user46</password>
+    <full-name>User 46</full-name>
+    <email>user46 at example.com</email>
+</record>
+<record type="user">
+    <uid>user47</uid>
+    <short-name>user47</short-name>
+    <password>user47</password>
+    <full-name>User 47</full-name>
+    <email>user47 at example.com</email>
+</record>
+<record type="user">
+    <uid>user48</uid>
+    <short-name>user48</short-name>
+    <password>user48</password>
+    <full-name>User 48</full-name>
+    <email>user48 at example.com</email>
+</record>
+<record type="user">
+    <uid>user49</uid>
+    <short-name>user49</short-name>
+    <password>user49</password>
+    <full-name>User 49</full-name>
+    <email>user49 at example.com</email>
+</record>
+<record type="user">
+    <uid>user50</uid>
+    <short-name>user50</short-name>
+    <password>user50</password>
+    <full-name>User 50</full-name>
+    <email>user50 at example.com</email>
+</record>
+<record type="user">
+    <uid>user51</uid>
+    <short-name>user51</short-name>
+    <password>user51</password>
+    <full-name>User 51</full-name>
+    <email>user51 at example.com</email>
+</record>
+<record type="user">
+    <uid>user52</uid>
+    <short-name>user52</short-name>
+    <password>user52</password>
+    <full-name>User 52</full-name>
+    <email>user52 at example.com</email>
+</record>
+<record type="user">
+    <uid>user53</uid>
+    <short-name>user53</short-name>
+    <password>user53</password>
+    <full-name>User 53</full-name>
+    <email>user53 at example.com</email>
+</record>
+<record type="user">
+    <uid>user54</uid>
+    <short-name>user54</short-name>
+    <password>user54</password>
+    <full-name>User 54</full-name>
+    <email>user54 at example.com</email>
+</record>
+<record type="user">
+    <uid>user55</uid>
+    <short-name>user55</short-name>
+    <password>user55</password>
+    <full-name>User 55</full-name>
+    <email>user55 at example.com</email>
+</record>
+<record type="user">
+    <uid>user56</uid>
+    <short-name>user56</short-name>
+    <password>user56</password>
+    <full-name>User 56</full-name>
+    <email>user56 at example.com</email>
+</record>
+<record type="user">
+    <uid>user57</uid>
+    <short-name>user57</short-name>
+    <password>user57</password>
+    <full-name>User 57</full-name>
+    <email>user57 at example.com</email>
+</record>
+<record type="user">
+    <uid>user58</uid>
+    <short-name>user58</short-name>
+    <password>user58</password>
+    <full-name>User 58</full-name>
+    <email>user58 at example.com</email>
+</record>
+<record type="user">
+    <uid>user59</uid>
+    <short-name>user59</short-name>
+    <password>user59</password>
+    <full-name>User 59</full-name>
+    <email>user59 at example.com</email>
+</record>
+<record type="user">
+    <uid>user60</uid>
+    <short-name>user60</short-name>
+    <password>user60</password>
+    <full-name>User 60</full-name>
+    <email>user60 at example.com</email>
+</record>
+<record type="user">
+    <uid>user61</uid>
+    <short-name>user61</short-name>
+    <password>user61</password>
+    <full-name>User 61</full-name>
+    <email>user61 at example.com</email>
+</record>
+<record type="user">
+    <uid>user62</uid>
+    <short-name>user62</short-name>
+    <password>user62</password>
+    <full-name>User 62</full-name>
+    <email>user62 at example.com</email>
+</record>
+<record type="user">
+    <uid>user63</uid>
+    <short-name>user63</short-name>
+    <password>user63</password>
+    <full-name>User 63</full-name>
+    <email>user63 at example.com</email>
+</record>
+<record type="user">
+    <uid>user64</uid>
+    <short-name>user64</short-name>
+    <password>user64</password>
+    <full-name>User 64</full-name>
+    <email>user64 at example.com</email>
+</record>
+<record type="user">
+    <uid>user65</uid>
+    <short-name>user65</short-name>
+    <password>user65</password>
+    <full-name>User 65</full-name>
+    <email>user65 at example.com</email>
+</record>
+<record type="user">
+    <uid>user66</uid>
+    <short-name>user66</short-name>
+    <password>user66</password>
+    <full-name>User 66</full-name>
+    <email>user66 at example.com</email>
+</record>
+<record type="user">
+    <uid>user67</uid>
+    <short-name>user67</short-name>
+    <password>user67</password>
+    <full-name>User 67</full-name>
+    <email>user67 at example.com</email>
+</record>
+<record type="user">
+    <uid>user68</uid>
+    <short-name>user68</short-name>
+    <password>user68</password>
+    <full-name>User 68</full-name>
+    <email>user68 at example.com</email>
+</record>
+<record type="user">
+    <uid>user69</uid>
+    <short-name>user69</short-name>
+    <password>user69</password>
+    <full-name>User 69</full-name>
+    <email>user69 at example.com</email>
+</record>
+<record type="user">
+    <uid>user70</uid>
+    <short-name>user70</short-name>
+    <password>user70</password>
+    <full-name>User 70</full-name>
+    <email>user70 at example.com</email>
+</record>
+<record type="user">
+    <uid>user71</uid>
+    <short-name>user71</short-name>
+    <password>user71</password>
+    <full-name>User 71</full-name>
+    <email>user71 at example.com</email>
+</record>
+<record type="user">
+    <uid>user72</uid>
+    <short-name>user72</short-name>
+    <password>user72</password>
+    <full-name>User 72</full-name>
+    <email>user72 at example.com</email>
+</record>
+<record type="user">
+    <uid>user73</uid>
+    <short-name>user73</short-name>
+    <password>user73</password>
+    <full-name>User 73</full-name>
+    <email>user73 at example.com</email>
+</record>
+<record type="user">
+    <uid>user74</uid>
+    <short-name>user74</short-name>
+    <password>user74</password>
+    <full-name>User 74</full-name>
+    <email>user74 at example.com</email>
+</record>
+<record type="user">
+    <uid>user75</uid>
+    <short-name>user75</short-name>
+    <password>user75</password>
+    <full-name>User 75</full-name>
+    <email>user75 at example.com</email>
+</record>
+<record type="user">
+    <uid>user76</uid>
+    <short-name>user76</short-name>
+    <password>user76</password>
+    <full-name>User 76</full-name>
+    <email>user76 at example.com</email>
+</record>
+<record type="user">
+    <uid>user77</uid>
+    <short-name>user77</short-name>
+    <password>user77</password>
+    <full-name>User 77</full-name>
+    <email>user77 at example.com</email>
+</record>
+<record type="user">
+    <uid>user78</uid>
+    <short-name>user78</short-name>
+    <password>user78</password>
+    <full-name>User 78</full-name>
+    <email>user78 at example.com</email>
+</record>
+<record type="user">
+    <uid>user79</uid>
+    <short-name>user79</short-name>
+    <password>user79</password>
+    <full-name>User 79</full-name>
+    <email>user79 at example.com</email>
+</record>
+<record type="user">
+    <uid>user80</uid>
+    <short-name>user80</short-name>
+    <password>user80</password>
+    <full-name>User 80</full-name>
+    <email>user80 at example.com</email>
+</record>
+<record type="user">
+    <uid>user81</uid>
+    <short-name>user81</short-name>
+    <password>user81</password>
+    <full-name>User 81</full-name>
+    <email>user81 at example.com</email>
+</record>
+<record type="user">
+    <uid>user82</uid>
+    <short-name>user82</short-name>
+    <password>user82</password>
+    <full-name>User 82</full-name>
+    <email>user82 at example.com</email>
+</record>
+<record type="user">
+    <uid>user83</uid>
+    <short-name>user83</short-name>
+    <password>user83</password>
+    <full-name>User 83</full-name>
+    <email>user83 at example.com</email>
+</record>
+<record type="user">
+    <uid>user84</uid>
+    <short-name>user84</short-name>
+    <password>user84</password>
+    <full-name>User 84</full-name>
+    <email>user84 at example.com</email>
+</record>
+<record type="user">
+    <uid>user85</uid>
+    <short-name>user85</short-name>
+    <password>user85</password>
+    <full-name>User 85</full-name>
+    <email>user85 at example.com</email>
+</record>
+<record type="user">
+    <uid>user86</uid>
+    <short-name>user86</short-name>
+    <password>user86</password>
+    <full-name>User 86</full-name>
+    <email>user86 at example.com</email>
+</record>
+<record type="user">
+    <uid>user87</uid>
+    <short-name>user87</short-name>
+    <password>user87</password>
+    <full-name>User 87</full-name>
+    <email>user87 at example.com</email>
+</record>
+<record type="user">
+    <uid>user88</uid>
+    <short-name>user88</short-name>
+    <password>user88</password>
+    <full-name>User 88</full-name>
+    <email>user88 at example.com</email>
+</record>
+<record type="user">
+    <uid>user89</uid>
+    <short-name>user89</short-name>
+    <password>user89</password>
+    <full-name>User 89</full-name>
+    <email>user89 at example.com</email>
+</record>
+<record type="user">
+    <uid>user90</uid>
+    <short-name>user90</short-name>
+    <password>user90</password>
+    <full-name>User 90</full-name>
+    <email>user90 at example.com</email>
+</record>
+<record type="user">
+    <uid>user91</uid>
+    <short-name>user91</short-name>
+    <password>user91</password>
+    <full-name>User 91</full-name>
+    <email>user91 at example.com</email>
+</record>
+<record type="user">
+    <uid>user92</uid>
+    <short-name>user92</short-name>
+    <password>user92</password>
+    <full-name>User 92</full-name>
+    <email>user92 at example.com</email>
+</record>
+<record type="user">
+    <uid>user93</uid>
+    <short-name>user93</short-name>
+    <password>user93</password>
+    <full-name>User 93</full-name>
+    <email>user93 at example.com</email>
+</record>
+<record type="user">
+    <uid>user94</uid>
+    <short-name>user94</short-name>
+    <password>user94</password>
+    <full-name>User 94</full-name>
+    <email>user94 at example.com</email>
+</record>
+<record type="user">
+    <uid>user95</uid>
+    <short-name>user95</short-name>
+    <password>user95</password>
+    <full-name>User 95</full-name>
+    <email>user95 at example.com</email>
+</record>
+<record type="user">
+    <uid>user96</uid>
+    <short-name>user96</short-name>
+    <password>user96</password>
+    <full-name>User 96</full-name>
+    <email>user96 at example.com</email>
+</record>
+<record type="user">
+    <uid>user97</uid>
+    <short-name>user97</short-name>
+    <password>user97</password>
+    <full-name>User 97</full-name>
+    <email>user97 at example.com</email>
+</record>
+<record type="user">
+    <uid>user98</uid>
+    <short-name>user98</short-name>
+    <password>user98</password>
+    <full-name>User 98</full-name>
+    <email>user98 at example.com</email>
+</record>
+<record type="user">
+    <uid>user99</uid>
+    <short-name>user99</short-name>
+    <password>user99</password>
+    <full-name>User 99</full-name>
+    <email>user99 at example.com</email>
+</record>
+<record type="user">
+    <uid>user100</uid>
+    <short-name>user100</short-name>
+    <password>user100</password>
+    <full-name>User 100</full-name>
+    <email>user100 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser01</uid>
+    <short-name>puser01</short-name>
+    <password>puser01</password>
+    <full-name>Puser 01</full-name>
+    <email>puser01 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser02</uid>
+    <short-name>puser02</short-name>
+    <password>puser02</password>
+    <full-name>Puser 02</full-name>
+    <email>puser02 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser03</uid>
+    <short-name>puser03</short-name>
+    <password>puser03</password>
+    <full-name>Puser 03</full-name>
+    <email>puser03 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser04</uid>
+    <short-name>puser04</short-name>
+    <password>puser04</password>
+    <full-name>Puser 04</full-name>
+    <email>puser04 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser05</uid>
+    <short-name>puser05</short-name>
+    <password>puser05</password>
+    <full-name>Puser 05</full-name>
+    <email>puser05 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser06</uid>
+    <short-name>puser06</short-name>
+    <password>puser06</password>
+    <full-name>Puser 06</full-name>
+    <email>puser06 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser07</uid>
+    <short-name>puser07</short-name>
+    <password>puser07</password>
+    <full-name>Puser 07</full-name>
+    <email>puser07 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser08</uid>
+    <short-name>puser08</short-name>
+    <password>puser08</password>
+    <full-name>Puser 08</full-name>
+    <email>puser08 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser09</uid>
+    <short-name>puser09</short-name>
+    <password>puser09</password>
+    <full-name>Puser 09</full-name>
+    <email>puser09 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser10</uid>
+    <short-name>puser10</short-name>
+    <password>puser10</password>
+    <full-name>Puser 10</full-name>
+    <email>puser10 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser11</uid>
+    <short-name>puser11</short-name>
+    <password>puser11</password>
+    <full-name>Puser 11</full-name>
+    <email>puser11 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser12</uid>
+    <short-name>puser12</short-name>
+    <password>puser12</password>
+    <full-name>Puser 12</full-name>
+    <email>puser12 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser13</uid>
+    <short-name>puser13</short-name>
+    <password>puser13</password>
+    <full-name>Puser 13</full-name>
+    <email>puser13 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser14</uid>
+    <short-name>puser14</short-name>
+    <password>puser14</password>
+    <full-name>Puser 14</full-name>
+    <email>puser14 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser15</uid>
+    <short-name>puser15</short-name>
+    <password>puser15</password>
+    <full-name>Puser 15</full-name>
+    <email>puser15 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser16</uid>
+    <short-name>puser16</short-name>
+    <password>puser16</password>
+    <full-name>Puser 16</full-name>
+    <email>puser16 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser17</uid>
+    <short-name>puser17</short-name>
+    <password>puser17</password>
+    <full-name>Puser 17</full-name>
+    <email>puser17 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser18</uid>
+    <short-name>puser18</short-name>
+    <password>puser18</password>
+    <full-name>Puser 18</full-name>
+    <email>puser18 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser19</uid>
+    <short-name>puser19</short-name>
+    <password>puser19</password>
+    <full-name>Puser 19</full-name>
+    <email>puser19 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser20</uid>
+    <short-name>puser20</short-name>
+    <password>puser20</password>
+    <full-name>Puser 20</full-name>
+    <email>puser20 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser21</uid>
+    <short-name>puser21</short-name>
+    <password>puser21</password>
+    <full-name>Puser 21</full-name>
+    <email>puser21 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser22</uid>
+    <short-name>puser22</short-name>
+    <password>puser22</password>
+    <full-name>Puser 22</full-name>
+    <email>puser22 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser23</uid>
+    <short-name>puser23</short-name>
+    <password>puser23</password>
+    <full-name>Puser 23</full-name>
+    <email>puser23 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser24</uid>
+    <short-name>puser24</short-name>
+    <password>puser24</password>
+    <full-name>Puser 24</full-name>
+    <email>puser24 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser25</uid>
+    <short-name>puser25</short-name>
+    <password>puser25</password>
+    <full-name>Puser 25</full-name>
+    <email>puser25 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser26</uid>
+    <short-name>puser26</short-name>
+    <password>puser26</password>
+    <full-name>Puser 26</full-name>
+    <email>puser26 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser27</uid>
+    <short-name>puser27</short-name>
+    <password>puser27</password>
+    <full-name>Puser 27</full-name>
+    <email>puser27 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser28</uid>
+    <short-name>puser28</short-name>
+    <password>puser28</password>
+    <full-name>Puser 28</full-name>
+    <email>puser28 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser29</uid>
+    <short-name>puser29</short-name>
+    <password>puser29</password>
+    <full-name>Puser 29</full-name>
+    <email>puser29 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser30</uid>
+    <short-name>puser30</short-name>
+    <password>puser30</password>
+    <full-name>Puser 30</full-name>
+    <email>puser30 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser31</uid>
+    <short-name>puser31</short-name>
+    <password>puser31</password>
+    <full-name>Puser 31</full-name>
+    <email>puser31 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser32</uid>
+    <short-name>puser32</short-name>
+    <password>puser32</password>
+    <full-name>Puser 32</full-name>
+    <email>puser32 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser33</uid>
+    <short-name>puser33</short-name>
+    <password>puser33</password>
+    <full-name>Puser 33</full-name>
+    <email>puser33 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser34</uid>
+    <short-name>puser34</short-name>
+    <password>puser34</password>
+    <full-name>Puser 34</full-name>
+    <email>puser34 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser35</uid>
+    <short-name>puser35</short-name>
+    <password>puser35</password>
+    <full-name>Puser 35</full-name>
+    <email>puser35 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser36</uid>
+    <short-name>puser36</short-name>
+    <password>puser36</password>
+    <full-name>Puser 36</full-name>
+    <email>puser36 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser37</uid>
+    <short-name>puser37</short-name>
+    <password>puser37</password>
+    <full-name>Puser 37</full-name>
+    <email>puser37 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser38</uid>
+    <short-name>puser38</short-name>
+    <password>puser38</password>
+    <full-name>Puser 38</full-name>
+    <email>puser38 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser39</uid>
+    <short-name>puser39</short-name>
+    <password>puser39</password>
+    <full-name>Puser 39</full-name>
+    <email>puser39 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser40</uid>
+    <short-name>puser40</short-name>
+    <password>puser40</password>
+    <full-name>Puser 40</full-name>
+    <email>puser40 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser41</uid>
+    <short-name>puser41</short-name>
+    <password>puser41</password>
+    <full-name>Puser 41</full-name>
+    <email>puser41 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser42</uid>
+    <short-name>puser42</short-name>
+    <password>puser42</password>
+    <full-name>Puser 42</full-name>
+    <email>puser42 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser43</uid>
+    <short-name>puser43</short-name>
+    <password>puser43</password>
+    <full-name>Puser 43</full-name>
+    <email>puser43 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser44</uid>
+    <short-name>puser44</short-name>
+    <password>puser44</password>
+    <full-name>Puser 44</full-name>
+    <email>puser44 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser45</uid>
+    <short-name>puser45</short-name>
+    <password>puser45</password>
+    <full-name>Puser 45</full-name>
+    <email>puser45 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser46</uid>
+    <short-name>puser46</short-name>
+    <password>puser46</password>
+    <full-name>Puser 46</full-name>
+    <email>puser46 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser47</uid>
+    <short-name>puser47</short-name>
+    <password>puser47</password>
+    <full-name>Puser 47</full-name>
+    <email>puser47 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser48</uid>
+    <short-name>puser48</short-name>
+    <password>puser48</password>
+    <full-name>Puser 48</full-name>
+    <email>puser48 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser49</uid>
+    <short-name>puser49</short-name>
+    <password>puser49</password>
+    <full-name>Puser 49</full-name>
+    <email>puser49 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser50</uid>
+    <short-name>puser50</short-name>
+    <password>puser50</password>
+    <full-name>Puser 50</full-name>
+    <email>puser50 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser51</uid>
+    <short-name>puser51</short-name>
+    <password>puser51</password>
+    <full-name>Puser 51</full-name>
+    <email>puser51 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser52</uid>
+    <short-name>puser52</short-name>
+    <password>puser52</password>
+    <full-name>Puser 52</full-name>
+    <email>puser52 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser53</uid>
+    <short-name>puser53</short-name>
+    <password>puser53</password>
+    <full-name>Puser 53</full-name>
+    <email>puser53 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser54</uid>
+    <short-name>puser54</short-name>
+    <password>puser54</password>
+    <full-name>Puser 54</full-name>
+    <email>puser54 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser55</uid>
+    <short-name>puser55</short-name>
+    <password>puser55</password>
+    <full-name>Puser 55</full-name>
+    <email>puser55 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser56</uid>
+    <short-name>puser56</short-name>
+    <password>puser56</password>
+    <full-name>Puser 56</full-name>
+    <email>puser56 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser57</uid>
+    <short-name>puser57</short-name>
+    <password>puser57</password>
+    <full-name>Puser 57</full-name>
+    <email>puser57 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser58</uid>
+    <short-name>puser58</short-name>
+    <password>puser58</password>
+    <full-name>Puser 58</full-name>
+    <email>puser58 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser59</uid>
+    <short-name>puser59</short-name>
+    <password>puser59</password>
+    <full-name>Puser 59</full-name>
+    <email>puser59 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser60</uid>
+    <short-name>puser60</short-name>
+    <password>puser60</password>
+    <full-name>Puser 60</full-name>
+    <email>puser60 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser61</uid>
+    <short-name>puser61</short-name>
+    <password>puser61</password>
+    <full-name>Puser 61</full-name>
+    <email>puser61 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser62</uid>
+    <short-name>puser62</short-name>
+    <password>puser62</password>
+    <full-name>Puser 62</full-name>
+    <email>puser62 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser63</uid>
+    <short-name>puser63</short-name>
+    <password>puser63</password>
+    <full-name>Puser 63</full-name>
+    <email>puser63 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser64</uid>
+    <short-name>puser64</short-name>
+    <password>puser64</password>
+    <full-name>Puser 64</full-name>
+    <email>puser64 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser65</uid>
+    <short-name>puser65</short-name>
+    <password>puser65</password>
+    <full-name>Puser 65</full-name>
+    <email>puser65 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser66</uid>
+    <short-name>puser66</short-name>
+    <password>puser66</password>
+    <full-name>Puser 66</full-name>
+    <email>puser66 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser67</uid>
+    <short-name>puser67</short-name>
+    <password>puser67</password>
+    <full-name>Puser 67</full-name>
+    <email>puser67 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser68</uid>
+    <short-name>puser68</short-name>
+    <password>puser68</password>
+    <full-name>Puser 68</full-name>
+    <email>puser68 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser69</uid>
+    <short-name>puser69</short-name>
+    <password>puser69</password>
+    <full-name>Puser 69</full-name>
+    <email>puser69 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser70</uid>
+    <short-name>puser70</short-name>
+    <password>puser70</password>
+    <full-name>Puser 70</full-name>
+    <email>puser70 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser71</uid>
+    <short-name>puser71</short-name>
+    <password>puser71</password>
+    <full-name>Puser 71</full-name>
+    <email>puser71 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser72</uid>
+    <short-name>puser72</short-name>
+    <password>puser72</password>
+    <full-name>Puser 72</full-name>
+    <email>puser72 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser73</uid>
+    <short-name>puser73</short-name>
+    <password>puser73</password>
+    <full-name>Puser 73</full-name>
+    <email>puser73 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser74</uid>
+    <short-name>puser74</short-name>
+    <password>puser74</password>
+    <full-name>Puser 74</full-name>
+    <email>puser74 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser75</uid>
+    <short-name>puser75</short-name>
+    <password>puser75</password>
+    <full-name>Puser 75</full-name>
+    <email>puser75 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser76</uid>
+    <short-name>puser76</short-name>
+    <password>puser76</password>
+    <full-name>Puser 76</full-name>
+    <email>puser76 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser77</uid>
+    <short-name>puser77</short-name>
+    <password>puser77</password>
+    <full-name>Puser 77</full-name>
+    <email>puser77 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser78</uid>
+    <short-name>puser78</short-name>
+    <password>puser78</password>
+    <full-name>Puser 78</full-name>
+    <email>puser78 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser79</uid>
+    <short-name>puser79</short-name>
+    <password>puser79</password>
+    <full-name>Puser 79</full-name>
+    <email>puser79 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser80</uid>
+    <short-name>puser80</short-name>
+    <password>puser80</password>
+    <full-name>Puser 80</full-name>
+    <email>puser80 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser81</uid>
+    <short-name>puser81</short-name>
+    <password>puser81</password>
+    <full-name>Puser 81</full-name>
+    <email>puser81 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser82</uid>
+    <short-name>puser82</short-name>
+    <password>puser82</password>
+    <full-name>Puser 82</full-name>
+    <email>puser82 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser83</uid>
+    <short-name>puser83</short-name>
+    <password>puser83</password>
+    <full-name>Puser 83</full-name>
+    <email>puser83 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser84</uid>
+    <short-name>puser84</short-name>
+    <password>puser84</password>
+    <full-name>Puser 84</full-name>
+    <email>puser84 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser85</uid>
+    <short-name>puser85</short-name>
+    <password>puser85</password>
+    <full-name>Puser 85</full-name>
+    <email>puser85 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser86</uid>
+    <short-name>puser86</short-name>
+    <password>puser86</password>
+    <full-name>Puser 86</full-name>
+    <email>puser86 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser87</uid>
+    <short-name>puser87</short-name>
+    <password>puser87</password>
+    <full-name>Puser 87</full-name>
+    <email>puser87 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser88</uid>
+    <short-name>puser88</short-name>
+    <password>puser88</password>
+    <full-name>Puser 88</full-name>
+    <email>puser88 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser89</uid>
+    <short-name>puser89</short-name>
+    <password>puser89</password>
+    <full-name>Puser 89</full-name>
+    <email>puser89 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser90</uid>
+    <short-name>puser90</short-name>
+    <password>puser90</password>
+    <full-name>Puser 90</full-name>
+    <email>puser90 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser91</uid>
+    <short-name>puser91</short-name>
+    <password>puser91</password>
+    <full-name>Puser 91</full-name>
+    <email>puser91 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser92</uid>
+    <short-name>puser92</short-name>
+    <password>puser92</password>
+    <full-name>Puser 92</full-name>
+    <email>puser92 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser93</uid>
+    <short-name>puser93</short-name>
+    <password>puser93</password>
+    <full-name>Puser 93</full-name>
+    <email>puser93 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser94</uid>
+    <short-name>puser94</short-name>
+    <password>puser94</password>
+    <full-name>Puser 94</full-name>
+    <email>puser94 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser95</uid>
+    <short-name>puser95</short-name>
+    <password>puser95</password>
+    <full-name>Puser 95</full-name>
+    <email>puser95 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser96</uid>
+    <short-name>puser96</short-name>
+    <password>puser96</password>
+    <full-name>Puser 96</full-name>
+    <email>puser96 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser97</uid>
+    <short-name>puser97</short-name>
+    <password>puser97</password>
+    <full-name>Puser 97</full-name>
+    <email>puser97 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser98</uid>
+    <short-name>puser98</short-name>
+    <password>puser98</password>
+    <full-name>Puser 98</full-name>
+    <email>puser98 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser99</uid>
+    <short-name>puser99</short-name>
+    <password>puser99</password>
+    <full-name>Puser 99</full-name>
+    <email>puser99 at example.com</email>
+</record>
+<record type="user">
+    <uid>puser100</uid>
+    <short-name>puser100</short-name>
+    <password>puser100</password>
+    <full-name>Puser 100</full-name>
+    <email>puser100 at example.com</email>
+</record>
+</directory>

Added: CalendarServer/trunk/txdav/common/datastore/test/accounts/augments.xml
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/accounts/augments.xml	                        (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/test/accounts/augments.xml	2014-05-09 23:38:16 UTC (rev 13468)
@@ -0,0 +1,1356 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2009-2014 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ -->
+
+<!DOCTYPE augments SYSTEM "../../../conf/auth/augments.dtd">
+
+<augments>
+  <!--
+  <record>
+    <uid>Location-Default</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+    <auto-schedule-mode>automatic</auto-schedule-mode>
+  </record>
+  <record>
+    <uid>Resource-Default</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+    <auto-schedule-mode>automatic</auto-schedule-mode>
+  </record>
+  -->
+
+  <record>
+    <uid>D11F03A0-97EA-48AF-9A6C-FAC7F3975766</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+  </record>
+  <record>
+    <uid>6423F94A-6B76-4A3A-815B-D52CFD77935D</uid>
+    <server-id>00001</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+  </record>
+  <record>
+    <uid>5A985493-EE2C-4665-94CF-4DFEA3A89500</uid>
+    <server-id>00002</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+  </record>
+  <record>
+    <uid>8B4288F6-CC82-491D-8EF9-642EF4F3E7D0</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+  </record>
+  <record>
+    <uid>5FF60DAD-0BDE-4508-8C77-15F0CA5C8DD1</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+  </record>
+  <record>
+    <uid>543D28BA-F74F-4D5F-9243-B3E3A61171E5</uid>
+    <enable-calendar>false</enable-calendar>
+    <enable-addressbook>false</enable-addressbook>
+  </record>
+  <record>
+    <uid>right_coast</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+  </record>
+  <record>
+    <uid>left_coast</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+  </record>
+  <record>
+    <uid>mercury</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+  </record>
+  <record>
+    <uid>gemini</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+  </record>
+  <record>
+    <uid>apollo</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+    <auto-accept-group>both_coasts</auto-accept-group>
+  </record>
+  <record>
+    <uid>orion</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+  </record>
+  <record>
+    <uid>transporter</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+  </record>
+  <record>
+    <uid>ftlcpu</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+  </record>
+  <!--
+  <record>
+    <uid>non_calendar_proxy</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+  </record>
+-->
+  <record>
+    <uid>7423F94A-6B76-4A3A-815B-D52CFD77935D</uid>
+    <enable-calendar>true</enable-calendar>
+  </record>
+  <record>
+    <uid>8A985493-EE2C-4665-94CF-4DFEA3A89500</uid>
+    <enable-calendar>true</enable-calendar>
+  </record>
+  <record>
+    <uid>9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD2</uid>
+    <enable-calendar>true</enable-calendar>
+  </record>
+  <record>
+    <uid>9E1FFAC4-3CCD-45A1-8272-D161C92D2EEE</uid>
+    <enable-calendar>true</enable-calendar>
+  </record>
+  <record>
+    <uid>7678EC8A-A069-4E82-9066-7279C6718507</uid>
+    <enable-addressbook>true</enable-addressbook>
+  </record>
+  <record>
+    <uid>FC465590-E9E9-4746-ACE8-6C756A49FE4D</uid>
+    <enable-calendar>true</enable-calendar>
+  </record>
+  <record>
+    <uid>EC465590-E9E9-4746-ACE8-6C756A49FE4D</uid>
+    <enable-calendar>true</enable-calendar>
+    <enable-login>true</enable-login>
+  </record>
+  <record>
+    <uid>00599DAF-3E75-42DD-9DB7-52617E79943F</uid>
+    <enable-calendar>false</enable-calendar>
+    <enable-login>false</enable-login>
+  </record>
+  <record>
+    <uid>75EA36BE-F71B-40F9-81F9-CF59BF40CA8F</uid>
+    <enable-calendar>true</enable-calendar>
+    <auto-schedule-mode>automatic</auto-schedule-mode>
+  </record>
+<record>
+    <uid>user01</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user02</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user03</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user04</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user05</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user06</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user07</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user08</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user09</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user10</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user11</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user12</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user13</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user14</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user15</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user16</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user17</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user18</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user19</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user20</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user21</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user22</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user23</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user24</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user25</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user26</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user27</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user28</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user29</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user30</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user31</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user32</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user33</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user34</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user35</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user36</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user37</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user38</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user39</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user40</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user41</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user42</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user43</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user44</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user45</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user46</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user47</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user48</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user49</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user50</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user51</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user52</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user53</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user54</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user55</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user56</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user57</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user58</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user59</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user60</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user61</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user62</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user63</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user64</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user65</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user66</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user67</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user68</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user69</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user70</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user71</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user72</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user73</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user74</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user75</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user76</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user77</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user78</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user79</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user80</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user81</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user82</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user83</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user84</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user85</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user86</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user87</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user88</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user89</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user90</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user91</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user92</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user93</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user94</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user95</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user96</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user97</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user98</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user99</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>user100</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser01</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser02</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser03</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser04</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser05</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser06</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser07</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser08</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser09</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser10</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser11</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser12</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser13</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser14</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser15</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser16</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser17</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser18</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser19</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser20</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser21</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser22</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser23</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser24</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser25</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser26</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser27</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser28</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser29</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser30</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser31</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser32</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser33</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser34</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser35</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser36</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser37</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser38</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser39</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser40</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser41</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser42</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser43</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser44</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser45</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser46</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser47</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser48</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser49</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser50</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser51</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser52</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser53</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser54</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser55</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser56</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser57</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser58</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser59</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser60</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser61</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser62</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser63</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser64</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser65</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser66</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser67</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser68</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser69</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser70</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser71</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser72</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser73</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser74</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser75</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser76</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser77</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser78</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser79</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser80</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser81</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser82</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser83</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser84</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser85</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser86</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser87</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser88</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser89</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser90</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser91</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser92</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser93</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser94</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser95</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser96</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser97</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser98</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser99</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+<record>
+    <uid>puser100</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+</augments>

Added: CalendarServer/trunk/txdav/common/datastore/test/accounts/generate_test_accounts.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/accounts/generate_test_accounts.py	                        (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/test/accounts/generate_test_accounts.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -0,0 +1,82 @@
+#!/usr/bin/env python
+
+# Generates test directory records in accounts-test.xml,
+# (overwriting it if it exists in the current directory).
+
+prefix = """<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2014 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ -->
+
+"""
+
+# accounts-test.xml
+
+out = file("accounts-test.xml", "w")
+out.write(prefix)
+out.write('<!DOCTYPE accounts SYSTEM "accounts.dtd">\n\n')
+out.write('<directory realm="Test Realm">\n')
+
+
+
+# user01-100
+for i in xrange(1, 101):
+    out.write("""<record type="user">
+    <uid>user{ctr:02d}</uid>
+    <short-name>user{ctr:02d}</short-name>
+    <password>user{ctr:02d}</password>
+    <full-name>User {ctr:02d}</full-name>
+    <email>user{ctr:02d}@example.com</email>
+</record>
+""".format(ctr=i))
+for i in xrange(1, 101):
+    out.write("""<record type="user">
+    <uid>puser{ctr:02d}</uid>
+    <short-name>puser{ctr:02d}</short-name>
+    <password>puser{ctr:02d}</password>
+    <full-name>Puser {ctr:02d}</full-name>
+    <email>puser{ctr:02d}@example.com</email>
+</record>
+""".format(ctr=i))
+out.write("</directory>\n")
+out.close()
+
+
+
+out = file("augments-test.xml", "w")
+out.write(prefix)
+out.write('<!DOCTYPE augments SYSTEM "augments.dtd">\n\n')
+out.write("<augments>\n")
+
+for i in xrange(1, 101):
+    out.write("""<record>
+    <uid>user{ctr:02d}</uid>
+    <server-id>A</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+""".format(ctr=i))
+
+for i in xrange(1, 101):
+    out.write("""<record>
+    <uid>puser{ctr:02d}</uid>
+    <server-id>B</server-id>
+    <enable-calendar>true</enable-calendar>
+    <enable-addressbook>true</enable-addressbook>
+</record>
+""".format(ctr=i))
+
+out.close()

Added: CalendarServer/trunk/txdav/common/datastore/test/accounts/proxies.xml
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/accounts/proxies.xml	                        (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/test/accounts/proxies.xml	2014-05-09 23:38:16 UTC (rev 13468)
@@ -0,0 +1,69 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2014 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ -->
+
+<!DOCTYPE proxies SYSTEM "../../../conf/auth/proxies.dtd">
+
+<proxies>
+  <record>
+    <guid>mercury</guid>
+    <write-proxies>
+      <member>left_coast</member>
+    </write-proxies>
+  </record>
+  <record>
+    <guid>gemini</guid>
+    <write-proxies>
+      <member>6423F94A-6B76-4A3A-815B-D52CFD77935D</member>
+    </write-proxies>
+  </record>
+  <record>
+    <guid>apollo</guid>
+    <write-proxies>
+      <member>both_coasts</member>
+    </write-proxies>
+  </record>
+  <record>
+    <guid>orion</guid>
+    <write-proxies>
+      <member>recursive1_coasts</member>
+    </write-proxies>
+  </record>
+  <record>
+    <guid>non_calendar_proxy</guid>
+    <write-proxies>
+      <member>non_calendar_group</member>
+    </write-proxies>
+    <read-proxies>
+      <member>recursive2_coasts</member>
+    </read-proxies>
+  </record>
+  <record>
+    <guid>7423F94A-6B76-4A3A-815B-D52CFD77935D</guid>
+    <write-proxies>
+      <member>8A985493-EE2C-4665-94CF-4DFEA3A89500</member>
+      <member>9FF60DAD-0BDE-4508-8C77-15F0CA5C8DD2</member>
+    </write-proxies>
+  </record>
+  <record>
+    <guid>FC465590-E9E9-4746-ACE8-6C756A49FE4D</guid>
+    <write-proxies>
+      <member>EC465590-E9E9-4746-ACE8-6C756A49FE4D</member>
+      <member>00599DAF-3E75-42DD-9DB7-52617E79943F</member>
+    </write-proxies>
+  </record>
+</proxies>

Added: CalendarServer/trunk/txdav/common/datastore/test/accounts/resources.xml
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/accounts/resources.xml	                        (rev 0)
+++ CalendarServer/trunk/txdav/common/datastore/test/accounts/resources.xml	2014-05-09 23:38:16 UTC (rev 13468)
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?>
+
+<!--
+Copyright (c) 2006-2014 Apple Inc. All rights reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+    http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+ -->
+
+<!DOCTYPE accounts SYSTEM "../../../conf/auth/accounts.dtd">
+
+<directory realm="Test">
+  <record type="location">
+    <uid>mercury</uid>
+    <short-name>mercury</short-name>
+    <password>mercury</password>
+    <full-name>Mercury Seven</full-name>
+    <email>mercury at example.com</email>
+  </record>
+  <record type="location">
+    <uid>gemini</uid>
+    <short-name>gemini</short-name>
+    <password>gemini</password>
+    <full-name>Gemini Twelve</full-name>
+    <email>gemini at example.com</email>
+  </record>
+  <record type="location">
+    <uid>apollo</uid>
+    <short-name>apollo</short-name>
+    <password>apollo</password>
+    <full-name>Apollo Eleven</full-name>
+    <email>apollo at example.com</email>
+  </record>
+  <record type="location">
+    <uid>orion</uid>
+    <short-name>orion</short-name>
+    <password>orion</password>
+    <full-name>Orion</full-name>
+    <email>orion at example.com</email>
+  </record>
+  <record type="resource">
+    <uid>transporter</uid>
+    <short-name>transporter</short-name>
+    <password>transporter</password>
+    <full-name>Mass Transporter</full-name>
+    <email>transporter at example.com</email>
+  </record>
+  <record type="resource">
+    <uid>ftlcpu</uid>
+    <short-name>ftlcpu</short-name>
+    <password>ftlcpu</password>
+    <full-name>Faster-Than-Light Microprocessor</full-name>
+    <email>ftlcpu at example.com</email>
+  </record>
+  <record type="resource">
+    <uid>non_calendar_proxy</uid>
+    <short-name>non_calendar_proxy</short-name>
+    <password>non_calendar_proxy</password>
+    <full-name>Non-calendar proxy</full-name>
+    <email>non_calendar_proxy at example.com</email>
+  </record>
+  <record type="resource">
+    <uid>disabled</uid>
+    <short-name>disabled</short-name>
+    <password>disabled</password>
+    <full-name>Disabled Record</full-name>
+    <email>disabled at example.com</email>
+  </record>
+  <record type="location">
+    <uid>__sanchezoffice__</uid>
+    <short-name>sanchezoffice</short-name>
+    <full-name>Sanchez Office</full-name>
+  </record>
+  <record type="location">
+    <uid>75EA36BE-F71B-40F9-81F9-CF59BF40CA8F</uid>
+    <guid>75EA36BE-F71B-40F9-81F9-CF59BF40CA8F</guid>
+    <short-name>location01</short-name>
+    <password>location01</password>
+    <full-name>Room 01</full-name>
+  </record>
+  <record type="location">
+    <uid>room-addr-1</uid>
+    <guid>634A102B-6902-464F-9451-8A86A31628C1</guid>
+    <short-name>room-with-address-1</short-name>
+    <password>room-addr-2</password>
+    <full-name>Room with Address 1</full-name>
+    <associated-address>1-infinite-loop</associated-address>
+  </record>
+  <record type="location">
+    <uid>room-addr-2</uid>
+    <short-name>room-with-address-2</short-name>
+    <password>room-addr-2</password>
+    <full-name>Room with Address 2</full-name>
+    <associated-address>2-infinite-loop</associated-address>
+  </record>
+  <record type="address">
+    <uid>1-infinite-loop</uid>
+    <short-name>il1</short-name>
+    <full-name>One Infinite Loop</full-name>
+    <street-address>1 Infinite Loop, Cupertino, CA 95014</street-address>
+    <geographic-location>37.331741,-122.030333</geographic-location>
+  </record>
+  <record type="address">
+    <uid>2-infinite-loop</uid>
+    <short-name>il2</short-name>
+    <full-name>Two Infinite Loop</full-name>
+    <street-address>2 Infinite Loop, Cupertino, CA 95014</street-address>
+    <geographic-location>37.332633,-122.030502</geographic-location>
+  </record>
+
+</directory>

Modified: CalendarServer/trunk/txdav/common/datastore/test/test_sql.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/test_sql.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/common/datastore/test/test_sql.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -26,11 +26,10 @@
 from twisted.trial.unittest import TestCase
 from twisted.internet.defer import Deferred
 
-from txdav.caldav.datastore.test.util import buildDirectoryRecord
 from txdav.common.datastore.sql import log, CommonStoreTransactionMonitor, \
     CommonHome, CommonHomeChild, ECALENDARTYPE
 from txdav.common.datastore.sql_tables import schema
-from txdav.common.datastore.test.util import CommonCommonTests, buildStore
+from txdav.common.datastore.test.util import CommonCommonTests
 from txdav.common.icommondatastore import AllRetriesFailed
 from txdav.common.datastore.sql import fixUUIDNormalization
 from txdav.xml import element as davxml
@@ -38,9 +37,10 @@
 from uuid import UUID
 
 exampleUID = UUID("a" * 32)
-denormalizedUID = str(exampleUID)
+denormalizedUID = unicode(exampleUID)
 normalizedUID = denormalizedUID.upper()
 
+
 class CommonSQLStoreTests(CommonCommonTests, TestCase):
     """
     Tests for shared functionality in L{txdav.common.datastore.sql}.
@@ -52,19 +52,11 @@
         Set up two stores to migrate between.
         """
         yield super(CommonSQLStoreTests, self).setUp()
-        self._sqlStore = yield buildStore(self, self.notifierFactory)
-        self._sqlStore.directoryService().addRecord(buildDirectoryRecord(denormalizedUID))
-        self._sqlStore.directoryService().addRecord(buildDirectoryRecord(normalizedUID))
-        self._sqlStore.directoryService().addRecord(buildDirectoryRecord("uid"))
+        yield self.buildStoreAndDirectory(
+            extraUids=(denormalizedUID, normalizedUID, u"uid")
+        )
 
 
-    def storeUnderTest(self):
-        """
-        Return a store for testing.
-        """
-        return self._sqlStore
-
-
     @inlineCallbacks
     def test_logging(self):
         """
@@ -72,9 +64,9 @@
         """
 
         # Patch config to turn on logging then rebuild the store
-        self.patch(self._sqlStore, "logLabels", True)
-        self.patch(self._sqlStore, "logStats", True)
-        self.patch(self._sqlStore, "logSQL", True)
+        self.patch(self.store, "logLabels", True)
+        self.patch(self.store, "logStats", True)
+        self.patch(self.store, "logSQL", True)
 
         txn = self.transactionUnderTest()
         cs = schema.CALENDARSERVER
@@ -97,7 +89,7 @@
         self.patch(CommonStoreTransactionMonitor, "callLater", c.callLater)
 
         # Patch config to turn on log waits then rebuild the store
-        self.patch(self._sqlStore, "logTransactionWaits", 1)
+        self.patch(self.store, "logTransactionWaits", 1)
 
         ctr = [0]
         def counter(*args, **kwargs):
@@ -120,7 +112,7 @@
         self.patch(CommonStoreTransactionMonitor, "callLater", c.callLater)
 
         # Patch config to turn on transaction timeouts then rebuild the store
-        self.patch(self._sqlStore, "timeoutTransactions", 1)
+        self.patch(self.store, "timeoutTransactions", 1)
 
         ctr = [0]
         def counter(*args, **kwargs):
@@ -145,8 +137,8 @@
         self.patch(CommonStoreTransactionMonitor, "callLater", c.callLater)
 
         # Patch config to turn on log waits then rebuild the store
-        self.patch(self._sqlStore, "logTransactionWaits", 1)
-        self.patch(self._sqlStore, "timeoutTransactions", 2)
+        self.patch(self.store, "logTransactionWaits", 1)
+        self.patch(self.store, "timeoutTransactions", 2)
 
         ctr = [0, 0]
         def counter(logStr, *args, **kwargs):

Modified: CalendarServer/trunk/txdav/common/datastore/test/util.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/test/util.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/common/datastore/test/util.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -23,10 +23,6 @@
 
 import os
 
-from zope.interface.declarations import implements
-from txdav.common.idirectoryservice import IStoreDirectoryService, \
-    IStoreDirectoryRecord
-
 # FIXME: Don't import from calendarserver in txdav
 from calendarserver.push.notifier import Notifier
 
@@ -37,7 +33,7 @@
 from random import Random
 
 from twext.python.log import Logger
-from twext.python.filepath import CachingFilePath
+from twext.python.filepath import CachingFilePath as FilePath
 from twext.enterprise.adbapi2 import ConnectionPool
 from twext.enterprise.ienterprise import AlreadyFinishedError
 from twext.who.directory import DirectoryRecord
@@ -45,12 +41,12 @@
 from twisted.application.service import Service
 from twisted.internet import reactor
 from twisted.internet.defer import Deferred, inlineCallbacks
-from twisted.internet.defer import returnValue, succeed
+from twisted.internet.defer import returnValue
 from twisted.internet.task import deferLater
 from twisted.trial.unittest import TestCase
 
 from twistedcaldav import ical
-from twistedcaldav.config import config
+from twistedcaldav.config import config, ConfigDict
 from twistedcaldav.ical import Component as VComponent, Component
 from twistedcaldav.stdconfig import DEFAULT_CONFIG
 from twistedcaldav.vcard import Component as ABComponent
@@ -62,6 +58,7 @@
 from txdav.common.datastore.sql import CommonDataStore, current_sql_schema
 from txdav.common.datastore.sql_tables import schema
 from txdav.common.icommondatastore import NoSuchHomeChildError
+from txdav.who.util import buildDirectory
 
 from txweb2.dav.resource import TwistedGETContentMD5
 
@@ -103,100 +100,7 @@
 
 
 
-class TestStoreDirectoryService(object):
 
-    implements(IStoreDirectoryService)
-
-    def __init__(self):
-        self.records = {}
-
-
-    def recordWithUID(self, uid):
-        return succeed(self.records.get(uid))
-
-
-    def recordWithGUID(self, guid):
-        for record in self.records.itervalues():
-            if record.guid == guid:
-                return succeed(record)
-        return succeed(None)
-
-
-    def recordWithCalendarUserAddress(self, cuaddr):
-        if cuaddr.startswith("urn:x-uid:"):
-            return self.recordWithUID(cuaddr[10:])
-        elif cuaddr.startswith("urn:uuid:"):
-            return self.recordWithUID(cuaddr[9:])
-        else:
-            return succeed(None)
-
-
-    def addRecord(self, record):
-        self.records[record.uid] = record
-
-
-    def removeRecord(self, uid):
-        del self.records[uid]
-
-
-
-class TestStoreDirectoryRecord(object):
-
-    implements(IStoreDirectoryRecord)
-
-    def __init__(self, uid, shortNames, fullName, thisServer=True, server=None, extras={}):
-        self.uid = uid
-        self.guid = uid
-        self.shortNames = shortNames
-        self.fullName = fullName
-        self.displayName = self.fullName if self.fullName else self.shortNames[0]
-        self._thisServer = thisServer
-        self._server = server
-        self.extras = extras
-
-
-    def thisServer(self):
-        return self._thisServer
-
-
-    def server(self):
-        return self._server
-
-
-    def serverURI(self):
-        return self._server.uri if self._server else ""
-
-
-
-def buildDirectory(homes=None):
-
-    directory = TestStoreDirectoryService()
-
-    # User accounts
-    for ctr in range(1, 100):
-        directory.addRecord(TestStoreDirectoryRecord(
-            "user%02d" % (ctr,),
-            ("user%02d" % (ctr,),),
-            "User %02d" % (ctr,),
-        ))
-
-    homes = set(homes) if homes is not None else set()
-    for uid in homes:
-        directory.addRecord(buildDirectoryRecord(uid))
-
-    return directory
-
-
-
-def buildDirectoryRecord(uid):
-    return TestStoreDirectoryRecord(
-        uid,
-        (uid,),
-        uid.capitalize(),
-    )
-
-
-
 class SQLStoreBuilder(object):
     """
     Test-fixture-builder which can construct a PostgresStore.
@@ -211,7 +115,7 @@
         """
         Create a L{PostgresService} to use for building a store.
         """
-        dbRoot = CachingFilePath(self.sharedDBPath)
+        dbRoot = FilePath(self.sharedDBPath)
         return PostgresService(
             dbRoot, serviceFactory, current_sql_schema, resetSchema=True,
             databaseName="caldav",
@@ -234,7 +138,7 @@
         """
         disableMemcacheForTest(TestCase())
         staticQuota = 3000
-        attachmentRoot = (CachingFilePath(self.sharedDBPath).child("attachments"))
+        attachmentRoot = (FilePath(self.sharedDBPath).child("attachments"))
         stubsvc = self.createService(lambda cf: Service())
 
         cp = ConnectionPool(stubsvc.produceConnection, maxConnections=1)
@@ -244,7 +148,7 @@
         cds = CommonDataStore(
             cp.connection,
             {"push": StubNotifierFactory(), },
-            TestStoreDirectoryService(),
+            None,
             attachmentRoot, "",
             quota=staticQuota
         )
@@ -258,10 +162,9 @@
         @return: a L{Deferred} which fires with an L{IDataStore}.
         """
         disableMemcacheForTest(testCase)
-        dbRoot = CachingFilePath(self.sharedDBPath)
+        dbRoot = FilePath(self.sharedDBPath)
         attachmentRoot = dbRoot.child("attachments")
-        if directoryService is None:
-            directoryService = buildDirectory(homes=homes)
+        # The directory will be given to us later via setDirectoryService
         if self.sharedService is None:
             ready = Deferred()
             def getReady(connectionFactory, storageService):
@@ -694,6 +597,106 @@
 
 
 
+
+def buildTestDirectory(
+    store, dataRoot, accounts=None, resources=None, augments=None, proxies=None,
+    serversDB=None
+):
+    """
+    @param store: the store for the directory to use
+
+    @param dataRoot: the directory to copy xml files to
+
+    @param accounts: path to the accounts.xml file
+    @type accounts: L{FilePath}
+
+    @param resources: path to the resources.xml file
+    @type resources: L{FilePath}
+
+    @param augments: path to the augments.xml file
+    @type augments: L{FilePath}
+
+    @param proxies: path to the proxies.xml file
+    @type proxies: L{FilePath}
+
+    @return: the directory service
+    @rtype: L{IDirectoryService}
+    """
+
+    defaultDirectory = FilePath(__file__).sibling("accounts")
+    if accounts is None:
+        accounts = defaultDirectory.child("accounts.xml")
+    if resources is None:
+        resources = defaultDirectory.child("resources.xml")
+    if augments is None:
+        augments = defaultDirectory.child("augments.xml")
+    if proxies is None:
+        proxies = defaultDirectory.child("proxies.xml")
+
+    if not os.path.exists(dataRoot):
+        os.makedirs(dataRoot)
+
+    accountsCopy = FilePath(dataRoot).child("accounts.xml")
+    accountsCopy.setContent(accounts.getContent())
+
+    resourcesCopy = FilePath(dataRoot).child("resources.xml")
+    resourcesCopy.setContent(resources.getContent())
+
+    augmentsCopy = FilePath(dataRoot).child("augments.xml")
+    augmentsCopy.setContent(augments.getContent())
+
+    proxiesCopy = FilePath(dataRoot).child("proxies.xml")
+    proxiesCopy.setContent(proxies.getContent())
+
+    servicesInfo = (
+        ConfigDict(
+            {
+                "Enabled": True,
+                "type": "xml",
+                "params": {
+                    "xmlFile": "accounts.xml",
+                    "recordTypes": ("users", "groups"),
+                },
+            }
+        ),
+        ConfigDict(
+            {
+                "Enabled": True,
+                "type": "xml",
+                "params": {
+                    "xmlFile": "resources.xml",
+                    "recordTypes": ("locations", "resources", "addresses"),
+                },
+            }
+        ),
+    )
+    augmentServiceInfo = ConfigDict(
+        {
+            "type": "twistedcaldav.directory.augment.AugmentXMLDB",
+            "params": {
+                "xmlFiles": ["augments.xml", ],
+                "statSeconds": 15,
+            },
+        }
+    )
+    wikiServiceInfo = ConfigDict(
+        {
+            "Enabled": True,
+            "CollabHost": "localhost",
+            "CollabPort": 4444,
+        }
+    )
+    directory = buildDirectory(
+        store, dataRoot, servicesInfo, augmentServiceInfo, wikiServiceInfo,
+        serversDB
+    )
+
+    store.setDirectoryService(directory)
+
+    return directory
+
+
+
 class CommonCommonTests(object):
     """
     Common utility functionality for file/store combination tests.
@@ -703,6 +706,85 @@
     savedStore = None
     assertProvides = assertProvides
 
+
+    @inlineCallbacks
+    def buildStoreAndDirectory(
+        self, accounts=None, resources=None, augments=None, proxies=None,
+        extraUids=None, serversDB=None
+    ):
+
+        self.serverRoot = self.mktemp()
+        os.mkdir(self.serverRoot)
+
+        self.counter = 0
+        self.notifierFactory = StubNotifierFactory()
+
+        config.reset()
+        self.configure()
+
+        self.store = yield self.buildStore()
+        self._sqlCalendarStore = self.store  # FIXME: remove references to this
+
+        self.directory = buildTestDirectory(
+            self.store, config.DataRoot,
+            accounts=accounts, resources=resources,
+            augments=augments, proxies=proxies,
+            serversDB=serversDB
+        )
+        if extraUids:
+            for uid in extraUids:
+                yield self.addRecordFromFields(
+                    {
+                        self.directory.fieldName.uid:
+                            uid,
+                        self.directory.fieldName.recordType:
+                            self.directory.recordType.user,
+                    }
+                )
+
+
+    def configure(self):
+        """
+        Modify the configuration to suit unit tests, with a mktemp-created
+        ServerRoot
+        """
+
+        config.ServerRoot = os.path.abspath(self.serverRoot)
+        config.ConfigRoot = "config"
+        config.LogRoot = "logs"
+        config.RunRoot = "logs"
+
+        if not os.path.exists(config.DataRoot):
+            os.makedirs(config.DataRoot)
+        if not os.path.exists(config.DocumentRoot):
+            os.makedirs(config.DocumentRoot)
+        if not os.path.exists(config.ConfigRoot):
+            os.makedirs(config.ConfigRoot)
+        if not os.path.exists(config.LogRoot):
+            os.makedirs(config.LogRoot)
+
+        # Work queues for implicit scheduling slow down tests a lot and require them all to add
+        # "waits" for work to complete. Rewriting all the current tests to do that is not practical
+        # right now, so we will turn this off by default. Instead we will have a set of tests dedicated
+        # to work queue-based scheduling which will patch this option to True.
+        config.Scheduling.Options.WorkQueues.Enabled = False
+
+        self.config = config
+
+
+
+    def buildStore(self, storeBuilder=theStoreBuilder):
+        """
+        Builds and returns a store
+        """
+
+        # Build the store before the directory; the directory will be assigned
+        # to the store via setDirectoryService()
+        return storeBuilder.buildStore(self, self.notifierFactory, None)
+
+
+
+
     def transactionUnderTest(self, txn=None):
         """
         Create a transaction from C{storeUnderTest} and save it as
@@ -758,29 +840,12 @@
         return result
 
 
-    def setUp(self):
-        self.counter = 0
-        self.notifierFactory = StubNotifierFactory()
-        self.configInit()
 
-
-    def configInit(self):
-        """
-        Hard code some config options
-        """
-
-        # Work queues for implicit scheduling slow down tests a lot and require them all to add
-        # "waits" for work to complete. Rewriting all the current tests to do that is not practical
-        # right now, so we will turn this off by default. Instead we will have a set of tests dedicated
-        # to work queue-based scheduling which will patch this option to True.
-        config.Scheduling.Options.WorkQueues.Enabled = False
-
-
     def storeUnderTest(self):
         """
-        Subclasses must implement this method.
+        Create and return the L{CalendarStore} for testing.
         """
-        raise NotImplementedError("CommonCommonTests subclasses must implement.")
+        return self.store
 
 
     @inlineCallbacks
@@ -855,6 +920,16 @@
 
 
     @inlineCallbacks
+    def addRecordFromFields(self, fields):
+        updatedRecord = DirectoryRecord(self.directory, fields)
+        yield self.directory.updateRecords((updatedRecord,), create=True)
+
+    @inlineCallbacks
+    def removeRecord(self, uid):
+        yield self.directory.removeRecords([uid])
+
+
+    @inlineCallbacks
     def changeRecord(self, record, fieldname, value):
         fields = record.fields.copy()
         fields[fieldname] = value

Modified: CalendarServer/trunk/txdav/common/datastore/upgrade/test/test_migrate.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/upgrade/test/test_migrate.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/common/datastore/upgrade/test/test_migrate.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -39,12 +39,12 @@
 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, \
-    TestStoreDirectoryService
-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.test.util import (
+    populateCalendarsFrom, StubNotifierFactory, resetCalendarMD5s,
+    populateAddressBooksFrom, resetAddressBookMD5s, deriveValue,
+    withSpecialValue, CommonCommonTests
+)
 from txdav.common.datastore.upgrade.migrate import UpgradeToDatabaseStep, \
     StoreSpawnerService, swapAMP
 from txdav.xml import element
@@ -129,7 +129,7 @@
 
 
 
-class HomeMigrationTests(TestCase):
+class HomeMigrationTests(CommonCommonTests, TestCase):
     """
     Tests for L{UpgradeToDatabaseStep}.
     """
@@ -161,26 +161,28 @@
         """
         Set up two stores to migrate between.
         """
+
+        yield super(HomeMigrationTests, self).setUp()
+        yield self.buildStoreAndDirectory(
+            extraUids=(
+                u"home1",
+                u"home2",
+                u"home3",
+                u"home_defaults",
+                u"home_no_splits",
+                u"home_splits",
+                u"home_splits_shared",
+            )
+        )
+        self.sqlStore = self.store
+
         # Add some files to the file store.
 
         self.filesPath = CachingFilePath(self.mktemp())
         self.filesPath.createDirectory()
         fileStore = self.fileStore = CommonDataStore(
-            self.filesPath, {"push": StubNotifierFactory()}, TestStoreDirectoryService(), True, True
+            self.filesPath, {"push": StubNotifierFactory()}, self.directory, True, True
         )
-        self.sqlStore = yield theStoreBuilder.buildStore(
-            self,
-            StubNotifierFactory(),
-            homes=(
-                "home1",
-                "home2",
-                "home3",
-                "home_defaults",
-                "home_no_splits",
-                "home_splits",
-                "home_splits_shared",
-            )
-        )
         self.upgrader = UpgradeToDatabaseStep(self.fileStore, self.sqlStore)
 
         requirements = CommonTests.requirements

Modified: CalendarServer/trunk/txdav/common/datastore/work/test/test_inbox_cleanup.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/work/test/test_inbox_cleanup.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/common/datastore/work/test/test_inbox_cleanup.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -25,7 +25,6 @@
 from twisted.internet.defer import inlineCallbacks
 from twisted.trial.unittest import TestCase
 from twistedcaldav.config import config
-from txdav.caldav.datastore.test.util import buildCalendarStore
 from txdav.common.datastore.sql_tables import schema
 import datetime
 
@@ -38,7 +37,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(InboxCleanupTests, self).setUp()
-        self._sqlStore = yield buildCalendarStore(self, self.notifierFactory)
+        yield self.buildStoreAndDirectory()
         yield self.populate()
 
 
@@ -121,13 +120,6 @@
         }
 
 
-    def storeUnderTest(self):
-        """
-        Create and return a L{CalendarStore} for testing.
-        """
-        return self._sqlStore
-
-
     @inlineCallbacks
     def test_inboxCleanupWorkQueueing(self):
         """

Modified: CalendarServer/trunk/txdav/common/datastore/work/test/test_revision_cleanup.py
===================================================================
--- CalendarServer/trunk/txdav/common/datastore/work/test/test_revision_cleanup.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/common/datastore/work/test/test_revision_cleanup.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -23,7 +23,6 @@
 from twisted.trial.unittest import TestCase
 from twistedcaldav.config import config
 from twistedcaldav.vcard import Component as VCard
-from txdav.caldav.datastore.test.util import buildCalendarStore
 from txdav.common.datastore.sql_tables import schema, _BIND_MODE_READ
 from txdav.common.datastore.test.util import CommonCommonTests, populateCalendarsFrom
 from txdav.common.datastore.work.revision_cleanup import FindMinValidRevisionWork, RevisionCleanupWork
@@ -40,7 +39,7 @@
     @inlineCallbacks
     def setUp(self):
         yield super(RevisionCleanupTests, self).setUp()
-        self._sqlStore = yield buildCalendarStore(self, self.notifierFactory)
+        yield self.buildStoreAndDirectory()
         yield self.populate()
 
         class FakeWork(WorkItem):
@@ -221,13 +220,6 @@
         }
 
 
-    def storeUnderTest(self):
-        """
-        Create and return a L{CalendarStore} for testing.
-        """
-        return self._sqlStore
-
-
     @inlineCallbacks
     def _createCalendarShare(self):
         # Invite

Modified: CalendarServer/trunk/txdav/dps/test/test_client.py
===================================================================
--- CalendarServer/trunk/txdav/dps/test/test_client.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/dps/test/test_client.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -375,7 +375,7 @@
         records = (yield self.client.recordsWithRecordType(
             RecordType.user
         ))
-        self.assertEquals(len(records), 34)
+        self.assertEquals(len(records), 240)
 
 
     @inlineCallbacks

Modified: CalendarServer/trunk/txdav/who/directory.py
===================================================================
--- CalendarServer/trunk/txdav/who/directory.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/who/directory.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -29,7 +29,6 @@
 from twisted.internet.defer import inlineCallbacks, returnValue
 from twistedcaldav.config import config
 from twistedcaldav.ical import Property
-from txdav.caldav.datastore.scheduling.ischedule.localservers import Servers
 from txdav.caldav.datastore.scheduling.utils import normalizeCUAddr
 from txdav.who.delegates import RecordType as DelegateRecordType
 from txdav.who.idirectory import (
@@ -52,6 +51,14 @@
 
     guid = "1332A615-4D3A-41FE-B636-FBE25BFB982E"
 
+
+
+    serversDB = None
+
+    def setServersDB(self, serversDB):
+        self.serversDB = serversDB
+
+
     # Must maintain the hack for a bit longer:
     def setPrincipalCollection(self, principalCollection):
         """
@@ -395,8 +402,11 @@
         """
         URL of the server hosting this record. Return None if hosted on this server.
         """
-        if config.Servers.Enabled and getattr(self, "serviceNodeUID", None):
-            return Servers.getServerURIById(self.serviceNodeUID)
+        if (
+            self.service.serversDB is not None and
+            getattr(self, "serviceNodeUID", None)
+        ):
+            return self.service.serversDB.getServerURIById(self.serviceNodeUID)
         else:
             return None
 
@@ -405,8 +415,11 @@
         """
         Server hosting this record. Return None if hosted on this server.
         """
-        if config.Servers.Enabled and getattr(self, "serviceNodeUID", None):
-            return Servers.getServerById(self.serviceNodeUID)
+        if (
+            self.service.serversDB is not None and
+            getattr(self, "serviceNodeUID", None)
+        ):
+            return self.service.serversDB.getServerById(self.serviceNodeUID)
         else:
             return None
 

Added: CalendarServer/trunk/txdav/who/test/support.py
===================================================================
--- CalendarServer/trunk/txdav/who/test/support.py	                        (rev 0)
+++ CalendarServer/trunk/txdav/who/test/support.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -0,0 +1,71 @@
+##
+# Copyright (c) 2013 Apple Inc. All rights reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+##
+
+from twext.who.idirectory import (
+    RecordType,
+    NoSuchRecordError
+)
+from twext.who.index import DirectoryService as IndexDirectoryService
+from twext.who.util import ConstantsContainer
+from txdav.who.idirectory import (
+    RecordType as CalRecordType
+)
+from twisted.internet.defer import succeed, inlineCallbacks
+
+
+
+class InMemoryDirectoryService(IndexDirectoryService):
+    """
+    An in-memory IDirectoryService.  You must call updateRecords( ) if you want
+    to populate this service.
+    """
+
+    recordType = ConstantsContainer(
+        (
+            RecordType.user,
+            RecordType.group,
+            CalRecordType.location,
+            CalRecordType.resource,
+            CalRecordType.address
+        )
+    )
+
+
+    def loadRecords(self):
+        pass
+
+
+    @inlineCallbacks
+    def updateRecords(self, records, create=False):
+        recordsByUID = dict(((record.uid, record) for record in records))
+        if not create:
+            # Make sure all the records already exist
+            for uid, record in recordsByUID.items():
+                if uid not in self._index[self.fieldName.uid]:
+                    raise NoSuchRecordError(uid)
+
+        yield self.removeRecords(recordsByUID.keys())
+        self.indexRecords(records)
+
+
+    def removeRecords(self, uids):
+        index = self._index
+        for fieldName in self.indexedFields:
+            for recordSet in index[fieldName].itervalues():
+                for record in list(recordSet):
+                    if record.uid in uids:
+                        recordSet.remove(record)
+        return succeed(None)

Modified: CalendarServer/trunk/txdav/who/test/test_group_attendees.py
===================================================================
--- CalendarServer/trunk/txdav/who/test/test_group_attendees.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/who/test/test_group_attendees.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -18,17 +18,17 @@
     group attendee tests
 """
 
+import os
+
+from twext.python.filepath import CachingFilePath as FilePath
 from twext.who.directory import DirectoryService
-from twext.who.test.test_xml import xmlService
 from twisted.internet.defer import inlineCallbacks, returnValue
 from twisted.trial import unittest
 from twistedcaldav.config import config
 from twistedcaldav.ical import Component, normalize_iCalStr, ignoredComponents
-from txdav.caldav.datastore.test.util import buildCalendarStore, populateCalendarsFrom, CommonCommonTests
+from txdav.caldav.datastore.test.util import populateCalendarsFrom, CommonCommonTests
 from txdav.who.directory import CalendarDirectoryRecordMixin
 from txdav.who.groups import GroupCacher
-from txdav.who.util import directoryFromConfig
-import os
 
 
 class GroupAttendeeReconciliation(CommonCommonTests, unittest.TestCase):
@@ -38,43 +38,29 @@
 
     @inlineCallbacks
     def setUp(self):
-        self.patch(config.GroupAttendees, "Enabled", "True")
-        self.patch(config.GroupAttendees, "ReconciliationDelaySeconds", "0")
-
         yield super(GroupAttendeeReconciliation, self).setUp()
-        self.xmlService = xmlService(self.mktemp(), xmlData=None)
 
-        self.patch(
-            config.DirectoryService.params,
-            "xmlFile",
-            os.path.join(
-                os.path.dirname(__file__), "accounts", "groupAttendeeAccounts.xml"
-            )
+        accountsFilePath = FilePath(
+            os.path.join(os.path.dirname(__file__), "accounts")
         )
-        self.patch(
-            config.ResourceService.params,
-            "xmlFile",
-            os.path.join(
-                os.path.dirname(__file__), "accounts", "resources.xml"
-            )
+        yield self.buildStoreAndDirectory(
+            accounts=accountsFilePath.child("groupAttendeeAccounts.xml"),
+            resources=accountsFilePath.child("resources.xml"),
         )
-        self._sqlCalendarStore = yield buildCalendarStore(self, self.notifierFactory, directoryFromConfig(config))
         yield self.populate()
 
         self.paths = {}
 
 
-    def storeUnderTest(self):
-        """
-        Create and return a L{CalendarStore} for testing.
-        """
-        return self._sqlCalendarStore
+    def configure(self):
+        super(GroupAttendeeReconciliation, self).configure()
+        config.GroupAttendees.Enabled = True
+        config.GroupAttendees.ReconciliationDelaySeconds = 0
 
 
     @inlineCallbacks
     def populate(self):
         yield populateCalendarsFrom(self.requirements, self.storeUnderTest())
-        self.notifierFactory.reset()
 
     requirements = {
         "10000000-0000-0000-0000-000000000001" : None,

Modified: CalendarServer/trunk/txdav/who/test/test_util.py
===================================================================
--- CalendarServer/trunk/txdav/who/test/test_util.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/who/test/test_util.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -20,7 +20,8 @@
 
 import os
 
-from txdav.who.util import directoryFromConfig, InMemoryDirectoryService
+from txdav.who.util import directoryFromConfig
+from txdav.who.test.support import InMemoryDirectoryService
 from twisted.internet.defer import inlineCallbacks
 from twisted.trial.unittest import TestCase
 from twistedcaldav.config import ConfigDict

Modified: CalendarServer/trunk/txdav/who/util.py
===================================================================
--- CalendarServer/trunk/txdav/who/util.py	2014-05-09 20:48:38 UTC (rev 13467)
+++ CalendarServer/trunk/txdav/who/util.py	2014-05-09 23:38:16 UTC (rev 13468)
@@ -20,10 +20,8 @@
 from twext.python.types import MappingProxyType
 from twext.who.aggregate import DirectoryService as AggregateDirectoryService
 from twext.who.idirectory import (
-    FieldName as BaseFieldName, RecordType, DirectoryConfigurationError,
-    NoSuchRecordError
+    FieldName as BaseFieldName, RecordType, DirectoryConfigurationError
 )
-from twext.who.index import DirectoryService as IndexDirectoryService
 from twext.who.ldap import (
     DirectoryService as LDAPDirectoryService, LDAPAttribute,
     FieldName as LDAPFieldName,
@@ -31,7 +29,6 @@
 )
 from twext.who.util import ConstantsContainer
 from twisted.cred.credentials import UsernamePassword
-from twisted.internet.defer import succeed, inlineCallbacks
 from twisted.python.filepath import FilePath
 from twisted.python.reflect import namedClass
 from twistedcaldav.config import fullServerPath
@@ -48,16 +45,20 @@
 log = Logger()
 
 
-def directoryFromConfig(config, store=None):
+def directoryFromConfig(config, store=None, serversDB=None):
     """
     Return a directory service based on the config.  If you want to go through
     AMP to talk to one of these as a client, instantiate
     txdav.dps.client.DirectoryService
     """
 
-    # MOVE2WHO FIXME: this needs to talk to its own separate database.  In
-    # fact, don't pass store=None if you already have called storeFromConfig()
-    # within this process.  Pass the existing store in here.
+    # Note: Currently the directory needs a store, and the store needs a
+    # directory.  Originally the directory's store was going to be different
+    # from the calendar and contacts store, but we're not doing that, maybe
+    # ever, since it brings more headaches (managing multiple schema upgrades,
+    # etc.) You can pass store=None in here and the store will be created for
+    # you, but don't pass store=None if you already have called storeFromConfig()
+    # within this same process; pass that store in instead.
 
     # TODO: use proxyForInterface to ensure we're only using the DPS related
     # store API.  Also define an IDirectoryProxyStore Interface
@@ -65,10 +66,38 @@
         _ignore_pool, txnFactory = getDBPool(config)
         store = storeFromConfig(config, txnFactory, None)
 
+    return buildDirectory(
+        store,
+        config.DataRoot,
+        [config.DirectoryService, config.ResourceService],
+        config.AugmentService,
+        config.Authentication.Wiki,
+        serversDB=serversDB
+    )
+
+
+def buildDirectory(
+    store, dataRoot, servicesInfo, augmentServiceInfo, wikiServiceInfo,
+    serversDB=None
+):
+    """
+    Return a directory without using a config object; suitable for tests
+    which need to have mulitple directory instances.
+
+    @param store: The store.
+    @param dataRoot: The path to the directory containing xml files for any xml
+        based services.
+    @param servicesInfo:  An interable of ConfigDicts mirroring the
+        DirectoryService and ResourceService sections of stdconfig
+    @param augmentServiceInfo: A ConfigDict mirroring the AugmentService section
+        of stdconfig
+    @param wikiServiceInfo: A ConfigDict mirroring the Wiki section of stdconfig
+    @param serversDB: A ServersDB object to assign to the directory
+    """
+
     aggregatedServices = []
 
-    for serviceKey in ("DirectoryService", "ResourceService"):
-        serviceValue = config.get(serviceKey, None)
+    for serviceValue in servicesInfo:
 
         if not serviceValue.Enabled:
             continue
@@ -76,14 +105,9 @@
         directoryType = serviceValue.type.lower()
         params = serviceValue.params
 
-        # TODO: add a "test" directory service that produces test records
-        # from code -- no files needed.
-        # The InMemoryDirectoryService now exists, it just needs hooking up
-        # here.
-
         if "xml" in directoryType:
             xmlFile = params.xmlFile
-            xmlFile = fullServerPath(config.DataRoot, xmlFile)
+            xmlFile = fullServerPath(dataRoot, xmlFile)
             fp = FilePath(xmlFile)
             if not fp.exists():
                 fp.setContent(DEFAULT_XML_CONTENT)
@@ -144,6 +168,10 @@
                 })
             )
 
+        elif "inmemory" in directoryType:
+            from txdav.who.test.support import InMemoryDirectoryService
+            directory = InMemoryDirectoryService()
+
         else:
             log.error("Invalid DirectoryType: {dt}", dt=directoryType)
             raise DirectoryConfigurationError
@@ -180,14 +208,14 @@
     #
     # Setup the Augment Service
     #
-    if config.AugmentService.type:
-        augmentClass = namedClass(config.AugmentService.type)
+    if augmentServiceInfo.type:
+        augmentClass = namedClass(augmentServiceInfo.type)
         log.info(
             "Configuring augment service of type: {augmentClass}",
             augmentClass=augmentClass
         )
         try:
-            augmentService = augmentClass(**config.AugmentService.params)
+            augmentService = augmentClass(**augmentServiceInfo.params)
         except IOError:
             log.error("Could not start augment service")
             raise
@@ -211,12 +239,12 @@
     aggregatedServices.append(delegateDirectory)
 
     # Wiki service
-    if config.Authentication.Wiki.Enabled:
+    if wikiServiceInfo.Enabled:
         aggregatedServices.append(
             WikiDirectoryService(
                 userDirectory.realmName,
-                config.Authentication.Wiki.CollabHost,
-                config.Authentication.Wiki.CollabPort
+                wikiServiceInfo.CollabHost,
+                wikiServiceInfo.CollabPort
             )
         )
 
@@ -242,6 +270,9 @@
         log.error("Could not create directory service", error=e)
         raise
 
+    if serversDB is not None:
+        augmented.setServersDB(serversDB)
+
     return augmented
 
 
@@ -249,47 +280,3 @@
 
 <directory realm="Realm"/>
 """
-
-
-class InMemoryDirectoryService(IndexDirectoryService):
-    """
-    An in-memory IDirectoryService.  You must call updateRecords( ) if you want
-    to populate this service.
-    """
-
-    recordType = ConstantsContainer(
-        (
-            RecordType.user,
-            RecordType.group,
-            CalRecordType.location,
-            CalRecordType.resource,
-            CalRecordType.address
-        )
-    )
-
-
-    def loadRecords(self):
-        pass
-
-
-    @inlineCallbacks
-    def updateRecords(self, records, create=False):
-        recordsByUID = dict(((record.uid, record) for record in records))
-        if not create:
-            # Make sure all the records already exist
-            for uid, _ignore_record in recordsByUID.items():
-                if uid not in self._index[self.fieldName.uid]:
-                    raise NoSuchRecordError(uid)
-
-        yield self.removeRecords(recordsByUID.keys())
-        self.indexRecords(records)
-
-
-    def removeRecords(self, uids):
-        index = self._index
-        for fieldName in self.indexedFields:
-            for recordSet in index[fieldName].itervalues():
-                for record in list(recordSet):
-                    if record.uid in uids:
-                        recordSet.remove(record)
-        return succeed(None)
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.macosforge.org/pipermail/calendarserver-changes/attachments/20140509/c759e47b/attachment-0001.html>


More information about the calendarserver-changes mailing list