Ejemplo n.º 1
0
class GroupCacherTest(StoreTestCase):
    @inlineCallbacks
    def setUp(self):
        yield super(GroupCacherTest, self).setUp()
        self.groupCacher = GroupCacher(self.directory)

    @inlineCallbacks
    def test_multipleCalls(self):
        """
        Ensure multiple calls to groupByUID() don't raise an exception
        """

        store = self.storeUnderTest()
        txn = store.newTransaction()

        record = yield self.directory.recordWithUID(u"__top_group_1__")
        yield txn.groupByUID(record.uid)
        yield txn.groupByUID(record.uid)

        yield txn.commit()

    @inlineCallbacks
    def test_refreshGroup(self):
        """
        Verify refreshGroup() adds a group to the Groups table with the
        expected membership hash value and members
        """

        store = self.storeUnderTest()
        txn = store.newTransaction()

        record = yield self.directory.recordWithUID(u"__top_group_1__")
        yield self.groupCacher.refreshGroup(txn, record.uid)

        group = (yield txn.groupByUID(record.uid))

        self.assertEquals(group.extant, True)
        self.assertEquals(group.membershipHash,
                          "553eb54e3bbb26582198ee04541dbee4")

        group = yield txn.groupByID(group.groupID)
        self.assertEquals(group.groupUID, record.uid)
        self.assertEquals(group.name, u"Top Group 1")
        self.assertEquals(group.membershipHash,
                          "553eb54e3bbb26582198ee04541dbee4")
        self.assertEquals(group.extant, True)

        members = (yield txn.groupMemberUIDs(group.groupID))
        self.assertEquals(
            set([
                u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__'
            ]), members)

        records = (yield self.groupCacher.cachedMembers(txn, group.groupID))
        self.assertEquals(
            set([r.uid for r in records]),
            set([
                u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__'
            ]))

        # sagen is in the top group, even though it's actually one level
        # removed
        record = yield self.directory.recordWithUID(u"__sagen1__")
        groups = (yield self.groupCacher.cachedGroupsFor(txn, record.uid))
        self.assertEquals(set([u"__top_group_1__"]), groups)

        yield txn.commit()

    @inlineCallbacks
    def test_synchronizeMembers(self):
        """
        After loading in a group via refreshGroup(), pass new member sets to
        synchronizeMembers() and verify members are added and removed as
        expected
        """

        store = self.storeUnderTest()
        txn = store.newTransaction()

        # Refresh the group so it's assigned a group_id
        uid = u"__top_group_1__"
        yield self.groupCacher.refreshGroup(txn, uid)
        group = yield txn.groupByUID(uid)

        # Remove two members, and add one member
        newSet = set()
        for name in (u"wsanchez1", u"cdaboo1", u"dre1"):
            record = (yield self.directory.recordWithShortName(
                RecordType.user, name))
            newSet.add(record.uid)
        added, removed = (yield self.groupCacher.synchronizeMembers(
            txn, group.groupID, newSet))
        self.assertEquals(added, set([
            "__dre1__",
        ]))
        self.assertEquals(removed, set([
            "__glyph1__",
            "__sagen1__",
        ]))
        records = (yield self.groupCacher.cachedMembers(txn, group.groupID))
        self.assertEquals(set([r.shortNames[0] for r in records]),
                          set(["wsanchez1", "cdaboo1", "dre1"]))

        # Remove all members
        added, removed = (yield self.groupCacher.synchronizeMembers(
            txn, group.groupID, set()))
        self.assertEquals(added, set())
        self.assertEquals(removed,
                          set([
                              "__wsanchez1__",
                              "__cdaboo1__",
                              "__dre1__",
                          ]))
        records = (yield self.groupCacher.cachedMembers(txn, group.groupID))
        self.assertEquals(len(records), 0)

        yield txn.commit()

    @inlineCallbacks
    def test_groupByID(self):

        store = self.storeUnderTest()
        txn = store.newTransaction()

        # Non-existent groupID
        yield self.failUnlessFailure(txn.groupByID(42), NotFoundError)

        uid = u"__top_group_1__"
        hash = "553eb54e3bbb26582198ee04541dbee4"
        yield self.groupCacher.refreshGroup(txn, uid)
        group = yield txn.groupByUID(uid)
        group = yield txn.groupByID(group.groupID)
        self.assertEqual(group.groupUID, uid)
        self.assertEqual(group.name, u"Top Group 1")
        self.assertEqual(group.membershipHash, hash)
        self.assertEqual(group.extant, True)

        yield txn.commit()

    @inlineCallbacks
    def test_externalAssignments(self):

        store = self.storeUnderTest()
        txn = store.newTransaction()

        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(oldExternalAssignments, {})

        newAssignments = {u"__wsanchez1__": (None, u"__top_group_1__")}
        yield self.groupCacher.scheduleExternalAssignments(txn,
                                                           newAssignments,
                                                           immediately=True)
        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(oldExternalAssignments,
                          {u"__wsanchez1__": (None, u"__top_group_1__")})

        newAssignments = {
            u"__cdaboo1__": (u"__sub_group_1__", None),
            u"__wsanchez1__": (u"__sub_group_1__", u"__top_group_1__"),
        }

        yield self.groupCacher.scheduleExternalAssignments(txn,
                                                           newAssignments,
                                                           immediately=True)
        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(
            oldExternalAssignments, {
                u"__wsanchez1__": (u"__sub_group_1__", u"__top_group_1__"),
                u"__cdaboo1__": (u"__sub_group_1__", None)
            })

        allGroupDelegates = (yield txn.allGroupDelegates())
        self.assertEquals(allGroupDelegates,
                          set([u"__top_group_1__", u"__sub_group_1__"]))

        # Fault in the read-only group
        yield self.groupCacher.refreshGroup(txn, u"__sub_group_1__")

        # Wilfredo should have Sagen and Daboo as read-only delegates
        delegates = (yield txn.delegates(u"__wsanchez1__",
                                         False,
                                         expanded=True))
        self.assertEquals(delegates, set([u"__sagen1__", u"__cdaboo1__"]))

        # Fault in the read-write group
        yield self.groupCacher.refreshGroup(txn, u"__top_group_1__")

        # Wilfredo should have 4 users as read-write delegates
        delegates = (yield txn.delegates(u"__wsanchez1__", True,
                                         expanded=True))
        self.assertEquals(delegates,
                          set([u"__sagen1__", u"__cdaboo1__", u"__glyph1__"]))

        #
        # Now, remove some external assignments
        #
        newAssignments = {
            u"__wsanchez1__": (u"__sub_group_1__", None),
        }
        yield self.groupCacher.scheduleExternalAssignments(txn,
                                                           newAssignments,
                                                           immediately=True)
        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(oldExternalAssignments, {
            u"__wsanchez1__": (u"__sub_group_1__", None),
        })

        allGroupDelegates = (yield txn.allGroupDelegates())
        self.assertEquals(allGroupDelegates, set([u"__sub_group_1__"]))

        # Wilfredo should have Sagen and Daboo as read-only delegates
        delegates = (yield txn.delegates(u"__wsanchez1__",
                                         False,
                                         expanded=True))
        self.assertEquals(delegates, set([u"__sagen1__", u"__cdaboo1__"]))

        # Wilfredo should have no read-write delegates
        delegates = (yield txn.delegates(u"__wsanchez1__", True,
                                         expanded=True))
        self.assertEquals(delegates, set([]))

        # Only 1 group as delegate now:
        allGroupDelegates = (yield txn.allGroupDelegates())
        self.assertEquals(allGroupDelegates, set([u"__sub_group_1__"]))

        #
        # Say somebody messed up and stuck a non-existent group UID in
        # as a delegate
        #
        newAssignments = {
            u"__wsanchez1__": (
                u"__sub_group_1__",
                u"__non_existent_group__",
            ),
        }
        yield self.groupCacher.scheduleExternalAssignments(txn,
                                                           newAssignments,
                                                           immediately=True)
        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(
            oldExternalAssignments,
            {
                u"__wsanchez1__": (
                    u"__sub_group_1__",
                    None  # <--- (not __non_existent_group__)
                ),
            })

        yield txn.commit()

    def test_diffAssignments(self):
        """
        Ensure external proxy assignment diffing works
        """

        self.assertEquals(
            (
                # changed
                [],
                # removed
                [],
            ),
            diffAssignments(
                # old
                {},
                # new
                {}))

        self.assertEquals(
            (
                # changed
                [],
                # removed
                [],
            ),
            diffAssignments(
                # old
                {"B": ("1", "2")},
                # new
                {"B": ("1", "2")},
            ))

        self.assertEquals(
            map(
                set,
                (
                    # changed
                    [("A", ("1", "2")), ("B", ("3", "4"))],
                    # removed
                    [],
                )),
            map(
                set,
                diffAssignments(
                    # old
                    {},
                    # new
                    {
                        "A": ("1", "2"),
                        "B": ("3", "4")
                    })))

        self.assertEquals(
            map(
                set,
                (
                    # changed
                    [],
                    # removed
                    ["A", "B"],
                )),
            map(
                set,
                diffAssignments(
                    # old
                    {
                        "A": ("1", "2"),
                        "B": ("3", "4")
                    },
                    # new
                    {},
                )))

        self.assertEquals(
            map(
                set,
                (
                    # changed
                    [('C', ('4', '5')), ('D', ('7', '8'))],
                    # removed
                    ["B"],
                )),
            map(
                set,
                diffAssignments(
                    # old
                    {
                        "A": ("1", "2"),
                        "B": ("3", "4"),
                        "C": ("5", "6")
                    },
                    # new
                    {
                        "D": ("7", "8"),
                        "C": ("4", "5"),
                        "A": ("1", "2")
                    },
                )))

    @inlineCallbacks
    def test_recursiveGroup(self):
        """
        Verify refreshGroup() adds a group to the Groups table with the
        expected membership hash value and members
        """

        store = self.storeUnderTest()
        txn = store.newTransaction()

        record = yield self.directory.recordWithUID(u"recursive1_coasts")
        members = yield record.expandedMembers()
        self.assertEquals(
            set([r.uid for r in members]),
            set([
                u'6423F94A-6B76-4A3A-815B-D52CFD77935D',
                u'5A985493-EE2C-4665-94CF-4DFEA3A89500'
            ]))

        yield txn.commit()

    @inlineCallbacks
    def test_groupChangeCacheNotificationRefreshGroup(self):
        """
        Verify refreshGroup() triggers a cache notification for the group and all
        members that are added or removed
        """
        class TestNotifier(object):
            changedTokens = []

            def changed(self, token):
                self.changedTokens.append(token)

        self.groupCacher.cacheNotifier = TestNotifier()

        # No change
        yield self.groupCacher.refreshGroup(self.transactionUnderTest(),
                                            "__top_group_1__")
        yield self.groupCacher.refreshGroup(self.transactionUnderTest(),
                                            "__sub_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, [])

        # Add member to group
        record = yield self.directory.recordWithUID(u"__top_group_1__")
        addrecord = yield self.directory.recordWithUID(u"__dre1__")
        yield record.addMembers([
            addrecord,
        ])

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(),
                                            "__top_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, [
            "__top_group_1__",
            "__dre1__",
        ])
        TestNotifier.changedTokens = []

        # Remove member from group
        record = yield self.directory.recordWithUID(u"__top_group_1__")
        addrecord = yield self.directory.recordWithUID(u"__dre1__")
        yield record.removeMembers([
            addrecord,
        ])

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(),
                                            "__top_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, [
            "__top_group_1__",
            "__dre1__",
        ])
        TestNotifier.changedTokens = []

        # Add member to sub-group
        record = yield self.directory.recordWithUID(u"__sub_group_1__")
        addrecord = yield self.directory.recordWithUID(u"__dre1__")
        yield record.addMembers([
            addrecord,
        ])

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(),
                                            "__top_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, [
            "__top_group_1__",
            "__dre1__",
        ])
        TestNotifier.changedTokens = []

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(),
                                            "__sub_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, [
            "__sub_group_1__",
            "__dre1__",
        ])
        TestNotifier.changedTokens = []

        # Remove member from sub-group
        record = yield self.directory.recordWithUID(u"__sub_group_1__")
        addrecord = yield self.directory.recordWithUID(u"__dre1__")
        yield record.removeMembers([
            addrecord,
        ])

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(),
                                            "__top_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, [
            "__top_group_1__",
            "__dre1__",
        ])
        TestNotifier.changedTokens = []

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(),
                                            "__sub_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, [
            "__sub_group_1__",
            "__dre1__",
        ])
        TestNotifier.changedTokens = []

        # Remove sub-group member from group
        record = yield self.directory.recordWithUID(u"__top_group_1__")
        addrecord = yield self.directory.recordWithUID(u"__sub_group_1__")
        yield record.removeMembers([
            addrecord,
        ])

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(),
                                            "__top_group_1__")
        yield self.commit()

        self.assertEqual(
            set(TestNotifier.changedTokens),
            set([
                "__top_group_1__",
                "__sagen1__",
                "__cdaboo1__",
            ]))
        TestNotifier.changedTokens = []

        # Add sub-group member to group
        record = yield self.directory.recordWithUID(u"__top_group_1__")
        addrecord = yield self.directory.recordWithUID(u"__sub_group_1__")
        yield record.addMembers([
            addrecord,
        ])

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(),
                                            "__top_group_1__")
        yield self.commit()

        self.assertEqual(
            set(TestNotifier.changedTokens),
            set([
                "__top_group_1__",
                "__sagen1__",
                "__cdaboo1__",
            ]))
        TestNotifier.changedTokens = []

    @inlineCallbacks
    def test_groupChangeCacheNotificationApplyExternalAssignments(self):
        """
        Verify applyExternalAssignments() triggers a cache notification for the
        delegator and delegates
        """
        class TestNotifier(object):
            changedTokens = []

            def changed(self, token):
                self.changedTokens.append(token)

        self.groupCacher.cacheNotifier = TestNotifier()

        yield self.groupCacher.applyExternalAssignments(
            self.transactionUnderTest(), "__dre1__", None, None)
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, ["__dre1__"])
        TestNotifier.changedTokens = []

        yield self.groupCacher.applyExternalAssignments(
            self.transactionUnderTest(), "__dre1__", "__top_group_1__",
            "__sub_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens,
                         ["__dre1__", "__top_group_1__", "__sub_group_1__"])
        TestNotifier.changedTokens = []
Ejemplo n.º 2
0
class GroupCacherTest(StoreTestCase):

    @inlineCallbacks
    def setUp(self):
        yield super(GroupCacherTest, self).setUp()
        self.groupCacher = GroupCacher(self.directory)


    @inlineCallbacks
    def test_multipleCalls(self):
        """
        Ensure multiple calls to groupByUID() don't raise an exception
        """

        store = self.storeUnderTest()
        txn = store.newTransaction()

        record = yield self.directory.recordWithUID(u"__top_group_1__")
        yield txn.groupByUID(record.uid)
        yield txn.groupByUID(record.uid)

        yield txn.commit()


    @inlineCallbacks
    def test_refreshGroup(self):
        """
        Verify refreshGroup() adds a group to the Groups table with the
        expected membership hash value and members
        """

        store = self.storeUnderTest()
        txn = store.newTransaction()

        record = yield self.directory.recordWithUID(u"__top_group_1__")
        yield self.groupCacher.refreshGroup(txn, record.uid)

        (
            groupID, _ignore_name, membershipHash, _ignore_modified,
            extant
        ) = (yield txn.groupByUID(record.uid))

        self.assertEquals(extant, True)
        self.assertEquals(membershipHash, "553eb54e3bbb26582198ee04541dbee4")

        groupUID, name, membershipHash, extant = (yield txn.groupByID(groupID))
        self.assertEquals(groupUID, record.uid)
        self.assertEquals(name, u"Top Group 1")
        self.assertEquals(membershipHash, "553eb54e3bbb26582198ee04541dbee4")
        self.assertEquals(extant, True)

        members = (yield txn.membersOfGroup(groupID))
        self.assertEquals(
            set([u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__']),
            members
        )

        records = (yield self.groupCacher.cachedMembers(txn, groupID))
        self.assertEquals(
            set([r.uid for r in records]),
            set([u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__'])
        )

        # sagen is in the top group, even though it's actually one level
        # removed
        record = yield self.directory.recordWithUID(u"__sagen1__")
        groups = (yield self.groupCacher.cachedGroupsFor(txn, record.uid))
        self.assertEquals(set([u"__top_group_1__"]), groups)

        yield txn.commit()


    @inlineCallbacks
    def test_synchronizeMembers(self):
        """
        After loading in a group via refreshGroup(), pass new member sets to
        synchronizeMembers() and verify members are added and removed as
        expected
        """

        store = self.storeUnderTest()
        txn = store.newTransaction()

        # Refresh the group so it's assigned a group_id
        uid = u"__top_group_1__"
        yield self.groupCacher.refreshGroup(txn, uid)
        (
            groupID, name, _ignore_membershipHash, _ignore_modified,
            _ignore_extant
        ) = yield txn.groupByUID(uid)

        # Remove two members, and add one member
        newSet = set()
        for name in (u"wsanchez1", u"cdaboo1", u"dre1"):
            record = (
                yield self.directory.recordWithShortName(
                    RecordType.user,
                    name
                )
            )
            newSet.add(record.uid)
        numAdded, numRemoved = (
            yield self.groupCacher.synchronizeMembers(
                txn, groupID, newSet
            )
        )
        self.assertEquals(numAdded, 1)
        self.assertEquals(numRemoved, 2)
        records = (yield self.groupCacher.cachedMembers(txn, groupID))
        self.assertEquals(
            set([r.shortNames[0] for r in records]),
            set(["wsanchez1", "cdaboo1", "dre1"])
        )

        # Remove all members
        numAdded, numRemoved = (
            yield self.groupCacher.synchronizeMembers(txn, groupID, set())
        )
        self.assertEquals(numAdded, 0)
        self.assertEquals(numRemoved, 3)
        records = (yield self.groupCacher.cachedMembers(txn, groupID))
        self.assertEquals(len(records), 0)

        yield txn.commit()


    @inlineCallbacks
    def test_groupByID(self):

        store = self.storeUnderTest()
        txn = store.newTransaction()

        # Non-existent groupID
        self.failUnlessFailure(txn.groupByID(42), NotFoundError)

        uid = u"__top_group_1__"
        hash = "553eb54e3bbb26582198ee04541dbee4"
        yield self.groupCacher.refreshGroup(txn, uid)
        (
            groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
            extant
        ) = yield txn.groupByUID(uid)
        results = yield txn.groupByID(groupID)
        self.assertEquals((uid, u"Top Group 1", hash, True), results)

        yield txn.commit()


    @inlineCallbacks
    def test_externalAssignments(self):

        store = self.storeUnderTest()
        txn = store.newTransaction()

        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(oldExternalAssignments, {})

        newAssignments = {
            u"__wsanchez1__": (None, u"__top_group_1__")
        }
        yield self.groupCacher.applyExternalAssignments(txn, newAssignments)
        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(
            oldExternalAssignments,
            {
                u"__wsanchez1__":
                (
                    None,
                    u"__top_group_1__"
                )
            }
        )

        newAssignments = {
            u"__cdaboo1__":
            (
                u"__sub_group_1__",
                None
            ),
            u"__wsanchez1__":
            (
                u"__sub_group_1__",
                u"__top_group_1__"
            ),
        }
        yield self.groupCacher.applyExternalAssignments(txn, newAssignments)
        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(
            oldExternalAssignments,
            {
                u"__wsanchez1__":
                (
                    u"__sub_group_1__",
                    u"__top_group_1__"
                ),
                u"__cdaboo1__":
                (
                    u"__sub_group_1__",
                    None
                )
            }
        )

        allGroupDelegates = (yield txn.allGroupDelegates())
        self.assertEquals(
            allGroupDelegates,
            set(
                [
                    u"__top_group_1__",
                    u"__sub_group_1__"
                ]
            )
        )

        # Fault in the read-only group
        yield self.groupCacher.refreshGroup(txn, u"__sub_group_1__")

        # Wilfredo should have Sagen and Daboo as read-only delegates
        delegates = (yield txn.delegates(
            u"__wsanchez1__", False, expanded=True)
        )
        self.assertEquals(
            delegates,
            set(
                [
                    u"__sagen1__",
                    u"__cdaboo1__"
                ]
            )
        )

        # Fault in the read-write group
        yield self.groupCacher.refreshGroup(txn, u"__top_group_1__")

        # Wilfredo should have 4 users as read-write delegates
        delegates = (yield txn.delegates(
            u"__wsanchez1__", True, expanded=True)
        )
        self.assertEquals(
            delegates,
            set(
                [
                    u"__wsanchez1__",
                    u"__sagen1__",
                    u"__cdaboo1__",
                    u"__glyph1__"
                ]
            )
        )

        #
        # Now, remove some external assignments
        #
        newAssignments = {
            u"__wsanchez1__":
            (
                u"__sub_group_1__",
                None
            ),
        }
        yield self.groupCacher.applyExternalAssignments(txn, newAssignments)
        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(
            oldExternalAssignments,
            {
                u"__wsanchez1__":
                (
                    u"__sub_group_1__",
                    None
                ),
            }
        )

        allGroupDelegates = (yield txn.allGroupDelegates())
        self.assertEquals(
            allGroupDelegates,
            set(
                [
                    u"__sub_group_1__"
                ]
            )
        )

        # Wilfredo should have Sagen and Daboo as read-only delegates
        delegates = (yield txn.delegates(
            u"__wsanchez1__", False, expanded=True)
        )
        self.assertEquals(
            delegates,
            set(
                [
                    u"__sagen1__",
                    u"__cdaboo1__"
                ]
            )
        )

        # Wilfredo should have no read-write delegates
        delegates = (yield txn.delegates(
            u"__wsanchez1__", True, expanded=True)
        )
        self.assertEquals(
            delegates,
            set([])
        )

        # Only 1 group as delegate now:
        allGroupDelegates = (yield txn.allGroupDelegates())
        self.assertEquals(
            allGroupDelegates,
            set(
                [
                    u"__sub_group_1__"
                ]
            )
        )

        yield txn.commit()


    def test_diffAssignments(self):
        """
        Ensure external proxy assignment diffing works
        """

        self.assertEquals(
            (
                # changed
                [],
                # removed
                [],
            ),
            diffAssignments(
                # old
                {},
                # new
                {}
            )
        )

        self.assertEquals(
            (
                # changed
                [],
                # removed
                [],
            ),
            diffAssignments(
                # old
                {"B": ("1", "2")},
                # new
                {"B": ("1", "2")},
            )
        )

        self.assertEquals(
            (
                # changed
                [("A", ("1", "2")), ("B", ("3", "4"))],
                # removed
                [],
            ),
            diffAssignments(
                # old
                {},
                # new
                {"A": ("1", "2"), "B": ("3", "4")}
            )
        )

        self.assertEquals(
            (
                # changed
                [],
                # removed
                ["A", "B"],
            ),
            diffAssignments(
                # old
                {"A": ("1", "2"), "B": ("3", "4")},
                # new
                {},
            )
        )

        self.assertEquals(
            (
                # changed
                [('C', ('4', '5')), ('D', ('7', '8'))],
                # removed
                ["B"],
            ),
            diffAssignments(
                # old
                {"A": ("1", "2"), "B": ("3", "4"), "C": ("5", "6")},
                # new
                {"D": ("7", "8"), "C": ("4", "5"), "A": ("1", "2")},
            )
        )
Ejemplo n.º 3
0
class GroupCacherTest(StoreTestCase):
    @inlineCallbacks
    def setUp(self):
        yield super(GroupCacherTest, self).setUp()
        self.groupCacher = GroupCacher(self.directory)

    @inlineCallbacks
    def test_multipleCalls(self):
        """
        Ensure multiple calls to groupByUID() don't raise an exception
        """

        store = self.storeUnderTest()
        txn = store.newTransaction()

        record = yield self.directory.recordWithUID(u"__top_group_1__")
        yield txn.groupByUID(record.uid)
        yield txn.groupByUID(record.uid)

        yield txn.commit()

    @inlineCallbacks
    def test_refreshGroup(self):
        """
        Verify refreshGroup() adds a group to the Groups table with the
        expected membership hash value and members
        """

        store = self.storeUnderTest()
        txn = store.newTransaction()

        record = yield self.directory.recordWithUID(u"__top_group_1__")
        yield self.groupCacher.refreshGroup(txn, record.uid)

        (groupID, _ignore_name, membershipHash, _ignore_modified,
         extant) = (yield txn.groupByUID(record.uid))

        self.assertEquals(extant, True)
        self.assertEquals(membershipHash, "553eb54e3bbb26582198ee04541dbee4")

        groupUID, name, membershipHash, extant = (yield txn.groupByID(groupID))
        self.assertEquals(groupUID, record.uid)
        self.assertEquals(name, u"Top Group 1")
        self.assertEquals(membershipHash, "553eb54e3bbb26582198ee04541dbee4")
        self.assertEquals(extant, True)

        members = (yield txn.membersOfGroup(groupID))
        self.assertEquals(
            set([
                u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__'
            ]), members)

        records = (yield self.groupCacher.cachedMembers(txn, groupID))
        self.assertEquals(
            set([r.uid for r in records]),
            set([
                u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__'
            ]))

        # sagen is in the top group, even though it's actually one level
        # removed
        record = yield self.directory.recordWithUID(u"__sagen1__")
        groups = (yield self.groupCacher.cachedGroupsFor(txn, record.uid))
        self.assertEquals(set([u"__top_group_1__"]), groups)

        yield txn.commit()

    @inlineCallbacks
    def test_synchronizeMembers(self):
        """
        After loading in a group via refreshGroup(), pass new member sets to
        synchronizeMembers() and verify members are added and removed as
        expected
        """

        store = self.storeUnderTest()
        txn = store.newTransaction()

        # Refresh the group so it's assigned a group_id
        uid = u"__top_group_1__"
        yield self.groupCacher.refreshGroup(txn, uid)
        (groupID, name, _ignore_membershipHash, _ignore_modified,
         _ignore_extant) = yield txn.groupByUID(uid)

        # Remove two members, and add one member
        newSet = set()
        for name in (u"wsanchez1", u"cdaboo1", u"dre1"):
            record = (yield self.directory.recordWithShortName(
                RecordType.user, name))
            newSet.add(record.uid)
        numAdded, numRemoved = (yield self.groupCacher.synchronizeMembers(
            txn, groupID, newSet))
        self.assertEquals(numAdded, 1)
        self.assertEquals(numRemoved, 2)
        records = (yield self.groupCacher.cachedMembers(txn, groupID))
        self.assertEquals(set([r.shortNames[0] for r in records]),
                          set(["wsanchez1", "cdaboo1", "dre1"]))

        # Remove all members
        numAdded, numRemoved = (yield self.groupCacher.synchronizeMembers(
            txn, groupID, set()))
        self.assertEquals(numAdded, 0)
        self.assertEquals(numRemoved, 3)
        records = (yield self.groupCacher.cachedMembers(txn, groupID))
        self.assertEquals(len(records), 0)

        yield txn.commit()

    @inlineCallbacks
    def test_groupByID(self):

        store = self.storeUnderTest()
        txn = store.newTransaction()

        # Non-existent groupID
        self.failUnlessFailure(txn.groupByID(42), NotFoundError)

        uid = u"__top_group_1__"
        hash = "553eb54e3bbb26582198ee04541dbee4"
        yield self.groupCacher.refreshGroup(txn, uid)
        (groupID, _ignore_name, _ignore_membershipHash, _ignore_modified,
         extant) = yield txn.groupByUID(uid)
        results = yield txn.groupByID(groupID)
        self.assertEquals((uid, u"Top Group 1", hash, True), results)

        yield txn.commit()

    @inlineCallbacks
    def test_externalAssignments(self):

        store = self.storeUnderTest()
        txn = store.newTransaction()

        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(oldExternalAssignments, {})

        newAssignments = {u"__wsanchez1__": (None, u"__top_group_1__")}
        yield self.groupCacher.applyExternalAssignments(txn, newAssignments)
        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(oldExternalAssignments,
                          {u"__wsanchez1__": (None, u"__top_group_1__")})

        newAssignments = {
            u"__cdaboo1__": (u"__sub_group_1__", None),
            u"__wsanchez1__": (u"__sub_group_1__", u"__top_group_1__"),
        }
        yield self.groupCacher.applyExternalAssignments(txn, newAssignments)
        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(
            oldExternalAssignments, {
                u"__wsanchez1__": (u"__sub_group_1__", u"__top_group_1__"),
                u"__cdaboo1__": (u"__sub_group_1__", None)
            })

        allGroupDelegates = (yield txn.allGroupDelegates())
        self.assertEquals(allGroupDelegates,
                          set([u"__top_group_1__", u"__sub_group_1__"]))

        # Fault in the read-only group
        yield self.groupCacher.refreshGroup(txn, u"__sub_group_1__")

        # Wilfredo should have Sagen and Daboo as read-only delegates
        delegates = (yield txn.delegates(u"__wsanchez1__",
                                         False,
                                         expanded=True))
        self.assertEquals(delegates, set([u"__sagen1__", u"__cdaboo1__"]))

        # Fault in the read-write group
        yield self.groupCacher.refreshGroup(txn, u"__top_group_1__")

        # Wilfredo should have 4 users as read-write delegates
        delegates = (yield txn.delegates(u"__wsanchez1__", True,
                                         expanded=True))
        self.assertEquals(
            delegates,
            set([
                u"__wsanchez1__", u"__sagen1__", u"__cdaboo1__", u"__glyph1__"
            ]))

        #
        # Now, remove some external assignments
        #
        newAssignments = {
            u"__wsanchez1__": (u"__sub_group_1__", None),
        }
        yield self.groupCacher.applyExternalAssignments(txn, newAssignments)
        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(oldExternalAssignments, {
            u"__wsanchez1__": (u"__sub_group_1__", None),
        })

        allGroupDelegates = (yield txn.allGroupDelegates())
        self.assertEquals(allGroupDelegates, set([u"__sub_group_1__"]))

        # Wilfredo should have Sagen and Daboo as read-only delegates
        delegates = (yield txn.delegates(u"__wsanchez1__",
                                         False,
                                         expanded=True))
        self.assertEquals(delegates, set([u"__sagen1__", u"__cdaboo1__"]))

        # Wilfredo should have no read-write delegates
        delegates = (yield txn.delegates(u"__wsanchez1__", True,
                                         expanded=True))
        self.assertEquals(delegates, set([]))

        # Only 1 group as delegate now:
        allGroupDelegates = (yield txn.allGroupDelegates())
        self.assertEquals(allGroupDelegates, set([u"__sub_group_1__"]))

        yield txn.commit()

    def test_diffAssignments(self):
        """
        Ensure external proxy assignment diffing works
        """

        self.assertEquals(
            (
                # changed
                [],
                # removed
                [],
            ),
            diffAssignments(
                # old
                {},
                # new
                {}))

        self.assertEquals(
            (
                # changed
                [],
                # removed
                [],
            ),
            diffAssignments(
                # old
                {"B": ("1", "2")},
                # new
                {"B": ("1", "2")},
            ))

        self.assertEquals(
            (
                # changed
                [("A", ("1", "2")), ("B", ("3", "4"))],
                # removed
                [],
            ),
            diffAssignments(
                # old
                {},
                # new
                {
                    "A": ("1", "2"),
                    "B": ("3", "4")
                }))

        self.assertEquals(
            (
                # changed
                [],
                # removed
                ["A", "B"],
            ),
            diffAssignments(
                # old
                {
                    "A": ("1", "2"),
                    "B": ("3", "4")
                },
                # new
                {},
            ))

        self.assertEquals(
            (
                # changed
                [('C', ('4', '5')), ('D', ('7', '8'))],
                # removed
                ["B"],
            ),
            diffAssignments(
                # old
                {
                    "A": ("1", "2"),
                    "B": ("3", "4"),
                    "C": ("5", "6")
                },
                # new
                {
                    "D": ("7", "8"),
                    "C": ("4", "5"),
                    "A": ("1", "2")
                },
            ))
Ejemplo n.º 4
0
class GroupCacherTest(StoreTestCase):

    @inlineCallbacks
    def setUp(self):
        yield super(GroupCacherTest, self).setUp()
        self.groupCacher = GroupCacher(self.directory)


    @inlineCallbacks
    def test_multipleCalls(self):
        """
        Ensure multiple calls to groupByUID() don't raise an exception
        """

        store = self.storeUnderTest()
        txn = store.newTransaction()

        record = yield self.directory.recordWithUID(u"__top_group_1__")
        yield txn.groupByUID(record.uid)
        yield txn.groupByUID(record.uid)

        yield txn.commit()


    @inlineCallbacks
    def test_refreshGroup(self):
        """
        Verify refreshGroup() adds a group to the Groups table with the
        expected membership hash value and members
        """

        store = self.storeUnderTest()
        txn = store.newTransaction()

        record = yield self.directory.recordWithUID(u"__top_group_1__")
        yield self.groupCacher.refreshGroup(txn, record.uid)

        group = (yield txn.groupByUID(record.uid))

        self.assertEquals(group.extant, True)
        self.assertEquals(group.membershipHash, "553eb54e3bbb26582198ee04541dbee4")

        group = yield txn.groupByID(group.groupID)
        self.assertEquals(group.groupUID, record.uid)
        self.assertEquals(group.name, u"Top Group 1")
        self.assertEquals(group.membershipHash, "553eb54e3bbb26582198ee04541dbee4")
        self.assertEquals(group.extant, True)

        members = (yield txn.groupMemberUIDs(group.groupID))
        self.assertEquals(
            set([u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__']),
            members
        )

        records = (yield self.groupCacher.cachedMembers(txn, group.groupID))
        self.assertEquals(
            set([r.uid for r in records]),
            set([u'__cdaboo1__', u'__glyph1__', u'__sagen1__', u'__wsanchez1__'])
        )

        # sagen is in the top group, even though it's actually one level
        # removed
        record = yield self.directory.recordWithUID(u"__sagen1__")
        groups = (yield self.groupCacher.cachedGroupsFor(txn, record.uid))
        self.assertEquals(set([u"__top_group_1__"]), groups)

        yield txn.commit()


    @inlineCallbacks
    def test_synchronizeMembers(self):
        """
        After loading in a group via refreshGroup(), pass new member sets to
        synchronizeMembers() and verify members are added and removed as
        expected
        """

        store = self.storeUnderTest()
        txn = store.newTransaction()

        # Refresh the group so it's assigned a group_id
        uid = u"__top_group_1__"
        yield self.groupCacher.refreshGroup(txn, uid)
        group = yield txn.groupByUID(uid)

        # Remove two members, and add one member
        newSet = set()
        for name in (u"wsanchez1", u"cdaboo1", u"dre1"):
            record = (
                yield self.directory.recordWithShortName(
                    RecordType.user,
                    name
                )
            )
            newSet.add(record.uid)
        added, removed = (
            yield self.groupCacher.synchronizeMembers(
                txn, group.groupID, newSet
            )
        )
        self.assertEquals(added, set(["__dre1__", ]))
        self.assertEquals(removed, set(["__glyph1__", "__sagen1__", ]))
        records = (yield self.groupCacher.cachedMembers(txn, group.groupID))
        self.assertEquals(
            set([r.shortNames[0] for r in records]),
            set(["wsanchez1", "cdaboo1", "dre1"])
        )

        # Remove all members
        added, removed = (
            yield self.groupCacher.synchronizeMembers(txn, group.groupID, set())
        )
        self.assertEquals(added, set())
        self.assertEquals(removed, set(["__wsanchez1__", "__cdaboo1__", "__dre1__", ]))
        records = (yield self.groupCacher.cachedMembers(txn, group.groupID))
        self.assertEquals(len(records), 0)

        yield txn.commit()


    @inlineCallbacks
    def test_groupByID(self):

        store = self.storeUnderTest()
        txn = store.newTransaction()

        # Non-existent groupID
        yield self.failUnlessFailure(txn.groupByID(42), NotFoundError)

        uid = u"__top_group_1__"
        hash = "553eb54e3bbb26582198ee04541dbee4"
        yield self.groupCacher.refreshGroup(txn, uid)
        group = yield txn.groupByUID(uid)
        group = yield txn.groupByID(group.groupID)
        self.assertEqual(group.groupUID, uid)
        self.assertEqual(group.name, u"Top Group 1")
        self.assertEqual(group.membershipHash, hash)
        self.assertEqual(group.extant, True)

        yield txn.commit()


    @inlineCallbacks
    def test_externalAssignments(self):

        store = self.storeUnderTest()
        txn = store.newTransaction()

        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(oldExternalAssignments, {})

        newAssignments = {
            u"__wsanchez1__": (None, u"__top_group_1__")
        }
        yield self.groupCacher.scheduleExternalAssignments(
            txn, newAssignments, immediately=True
        )
        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(
            oldExternalAssignments,
            {
                u"__wsanchez1__":
                (
                    None,
                    u"__top_group_1__"
                )
            }
        )

        newAssignments = {
            u"__cdaboo1__":
            (
                u"__sub_group_1__",
                None
            ),
            u"__wsanchez1__":
            (
                u"__sub_group_1__",
                u"__top_group_1__"
            ),
        }

        yield self.groupCacher.scheduleExternalAssignments(
            txn, newAssignments, immediately=True
        )
        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(
            oldExternalAssignments,
            {
                u"__wsanchez1__":
                (
                    u"__sub_group_1__",
                    u"__top_group_1__"
                ),
                u"__cdaboo1__":
                (
                    u"__sub_group_1__",
                    None
                )
            }
        )

        allGroupDelegates = (yield txn.allGroupDelegates())
        self.assertEquals(
            allGroupDelegates,
            set(
                [
                    u"__top_group_1__",
                    u"__sub_group_1__"
                ]
            )
        )

        # Fault in the read-only group
        yield self.groupCacher.refreshGroup(txn, u"__sub_group_1__")

        # Wilfredo should have Sagen and Daboo as read-only delegates
        delegates = (yield txn.delegates(
            u"__wsanchez1__", False, expanded=True)
        )
        self.assertEquals(
            delegates,
            set(
                [
                    u"__sagen1__",
                    u"__cdaboo1__"
                ]
            )
        )

        # Fault in the read-write group
        yield self.groupCacher.refreshGroup(txn, u"__top_group_1__")

        # Wilfredo should have 4 users as read-write delegates
        delegates = (yield txn.delegates(
            u"__wsanchez1__", True, expanded=True)
        )
        self.assertEquals(
            delegates,
            set(
                [
                    u"__sagen1__",
                    u"__cdaboo1__",
                    u"__glyph1__"
                ]
            )
        )

        #
        # Now, remove some external assignments
        #
        newAssignments = {
            u"__wsanchez1__":
            (
                u"__sub_group_1__",
                None
            ),
        }
        yield self.groupCacher.scheduleExternalAssignments(
            txn, newAssignments, immediately=True
        )
        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(
            oldExternalAssignments,
            {
                u"__wsanchez1__":
                (
                    u"__sub_group_1__",
                    None
                ),
            }
        )

        allGroupDelegates = (yield txn.allGroupDelegates())
        self.assertEquals(
            allGroupDelegates,
            set(
                [
                    u"__sub_group_1__"
                ]
            )
        )

        # Wilfredo should have Sagen and Daboo as read-only delegates
        delegates = (yield txn.delegates(
            u"__wsanchez1__", False, expanded=True)
        )
        self.assertEquals(
            delegates,
            set(
                [
                    u"__sagen1__",
                    u"__cdaboo1__"
                ]
            )
        )

        # Wilfredo should have no read-write delegates
        delegates = (yield txn.delegates(
            u"__wsanchez1__", True, expanded=True)
        )
        self.assertEquals(
            delegates,
            set([])
        )

        # Only 1 group as delegate now:
        allGroupDelegates = (yield txn.allGroupDelegates())
        self.assertEquals(
            allGroupDelegates,
            set(
                [
                    u"__sub_group_1__"
                ]
            )
        )

        #
        # Say somebody messed up and stuck a non-existent group UID in
        # as a delegate
        #
        newAssignments = {
            u"__wsanchez1__":
            (
                u"__sub_group_1__",
                u"__non_existent_group__",
            ),
        }
        yield self.groupCacher.scheduleExternalAssignments(
            txn, newAssignments, immediately=True
        )
        oldExternalAssignments = (yield txn.externalDelegates())
        self.assertEquals(
            oldExternalAssignments,
            {
                u"__wsanchez1__":
                (
                    u"__sub_group_1__",
                    None  # <--- (not __non_existent_group__)
                ),
            }
        )

        yield txn.commit()


    def test_diffAssignments(self):
        """
        Ensure external proxy assignment diffing works
        """

        self.assertEquals(
            (
                # changed
                [],
                # removed
                [],
            ),
            diffAssignments(
                # old
                {},
                # new
                {}
            )
        )

        self.assertEquals(
            (
                # changed
                [],
                # removed
                [],
            ),
            diffAssignments(
                # old
                {"B": ("1", "2")},
                # new
                {"B": ("1", "2")},
            )
        )

        self.assertEquals(
            map(set, (
                # changed
                [("A", ("1", "2")), ("B", ("3", "4"))],
                # removed
                [],
            )),
            map(set, diffAssignments(
                # old
                {},
                # new
                {"A": ("1", "2"), "B": ("3", "4")}
            ))
        )

        self.assertEquals(
            map(set, (
                # changed
                [],
                # removed
                ["A", "B"],
            )),
            map(set, diffAssignments(
                # old
                {"A": ("1", "2"), "B": ("3", "4")},
                # new
                {},
            ))
        )

        self.assertEquals(
            map(set, (
                # changed
                [('C', ('4', '5')), ('D', ('7', '8'))],
                # removed
                ["B"],
            )),
            map(set, diffAssignments(
                # old
                {"A": ("1", "2"), "B": ("3", "4"), "C": ("5", "6")},
                # new
                {"D": ("7", "8"), "C": ("4", "5"), "A": ("1", "2")},
            ))
        )


    @inlineCallbacks
    def test_recursiveGroup(self):
        """
        Verify refreshGroup() adds a group to the Groups table with the
        expected membership hash value and members
        """

        store = self.storeUnderTest()
        txn = store.newTransaction()

        record = yield self.directory.recordWithUID(u"recursive1_coasts")
        members = yield record.expandedMembers()
        self.assertEquals(
            set([r.uid for r in members]),
            set([u'6423F94A-6B76-4A3A-815B-D52CFD77935D', u'5A985493-EE2C-4665-94CF-4DFEA3A89500'])
        )

        yield txn.commit()


    @inlineCallbacks
    def test_groupChangeCacheNotification(self):
        """
        Verify refreshGroup() triggers a cache notification for the group and all
        members that are added or removed
        """

        class TestNotifier(object):
            changedTokens = []
            def changed(self, token):
                self.changedTokens.append(token)

        self.groupCacher.cacheNotifier = TestNotifier()

        # No change
        yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__")
        yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__sub_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, [])

        # Add member to group
        record = yield self.directory.recordWithUID(u"__top_group_1__")
        addrecord = yield self.directory.recordWithUID(u"__dre1__")
        yield record.addMembers([addrecord, ])

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, [
            "__top_group_1__",
            "__dre1__",
        ])
        TestNotifier.changedTokens = []

        # Remove member from group
        record = yield self.directory.recordWithUID(u"__top_group_1__")
        addrecord = yield self.directory.recordWithUID(u"__dre1__")
        yield record.removeMembers([addrecord, ])

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, [
            "__top_group_1__",
            "__dre1__",
        ])
        TestNotifier.changedTokens = []

        # Add member to sub-group
        record = yield self.directory.recordWithUID(u"__sub_group_1__")
        addrecord = yield self.directory.recordWithUID(u"__dre1__")
        yield record.addMembers([addrecord, ])

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, [
            "__top_group_1__",
            "__dre1__",
        ])
        TestNotifier.changedTokens = []

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__sub_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, [
            "__sub_group_1__",
            "__dre1__",
        ])
        TestNotifier.changedTokens = []

        # Remove member from sub-group
        record = yield self.directory.recordWithUID(u"__sub_group_1__")
        addrecord = yield self.directory.recordWithUID(u"__dre1__")
        yield record.removeMembers([addrecord, ])

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, [
            "__top_group_1__",
            "__dre1__",
        ])
        TestNotifier.changedTokens = []

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__sub_group_1__")
        yield self.commit()

        self.assertEqual(TestNotifier.changedTokens, [
            "__sub_group_1__",
            "__dre1__",
        ])
        TestNotifier.changedTokens = []

        # Remove sub-group member from group
        record = yield self.directory.recordWithUID(u"__top_group_1__")
        addrecord = yield self.directory.recordWithUID(u"__sub_group_1__")
        yield record.removeMembers([addrecord, ])

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__")
        yield self.commit()

        self.assertEqual(set(TestNotifier.changedTokens), set([
            "__top_group_1__",
            "__sagen1__",
            "__cdaboo1__",
        ]))
        TestNotifier.changedTokens = []

        # Add sub-group member to group
        record = yield self.directory.recordWithUID(u"__top_group_1__")
        addrecord = yield self.directory.recordWithUID(u"__sub_group_1__")
        yield record.addMembers([addrecord, ])

        yield self.groupCacher.refreshGroup(self.transactionUnderTest(), "__top_group_1__")
        yield self.commit()

        self.assertEqual(set(TestNotifier.changedTokens), set([
            "__top_group_1__",
            "__sagen1__",
            "__cdaboo1__",
        ]))
        TestNotifier.changedTokens = []