示例#1
0
    def check_inject(self):

        ms = MockStorage()
        ms.store("Package")
        ms.store("Content", data="I'm content", boxID=1)
        ms.store("Content", data="I'm mal content", boxID=1)

        schema = Schema({Content: "content", Content.box: "boxID", Package: "package"})

        clerk = Clerk(ms, schema)

        pak = Package()
        pak.refs << Content(data="I'm content", box=pak)
        pak.refs << Content(data="I'm malcontent", box=pak)
        pak = clerk.store(pak)

        # @TODO: should be able to add to the index without
        # triggering load (for performance reasons)
        # -- so long as any other use DOES trigger load --

        clerk.cache.clear()
        pak = clerk.fetch(Package, ID=1)

        # asking for .refs will trigger the load:
        assert len(pak.private.refs) == 0, pak.private.refs
        assert len(pak.refs) == 2

        # make sure it works with << on a fresh load too:
        newClerk = Clerk(clerk.storage, clerk.schema)
        pak = newClerk.fetch(Package, ID=1)
        assert len(pak.private.refs) == 0
        pak.refs << Content(data="I'm malcontent", box=pak)
        assert len(pak.refs) == 3
示例#2
0
    def check_inject(self):

        ms = MockStorage()
        ms.store("Package")
        ms.store("Content", data="I'm content", boxID=1)
        ms.store("Content", data="I'm mal content", boxID=1)

        schema = Schema({
            Content: "content",
            Content.box: "boxID",
            Package: "package",
        })

        clerk = Clerk(ms, schema)

        pak = Package()
        pak.refs << Content(data="I'm content", box=pak)
        pak.refs << Content(data="I'm malcontent", box=pak)
        pak = clerk.store(pak)

        # @TODO: should be able to add to the index without
        # triggering load (for performance reasons)
        # -- so long as any other use DOES trigger load --


        clerk.cache.clear()
        pak = clerk.fetch(Package, ID=1)
        
        # asking for .refs will trigger the load:
        assert len(pak.private.refs) == 0, pak.private.refs
        assert len(pak.refs) == 2

        # make sure it works with << on a fresh load too:
        newClerk = Clerk(clerk.storage, clerk.schema)
        pak = newClerk.fetch(Package, ID=1)
        assert len(pak.private.refs) == 0
        pak.refs << Content(data="I'm malcontent",  box=pak)
        assert len(pak.refs) == 3
示例#3
0
class ClerkTest(unittest.TestCase):
    def setUp(self):
        self.storage = MockStorage()
        schema = Schema({
            Node: "Node",
            Node.parent: "parentID",
            Record: "Record",
            Record.next: "nextID",
        })
        self.clerk = Clerk(self.storage, schema)

    def test_store(self):
        self.clerk.store(Record())
        actual = self.storage.match("Record")
        assert actual == [{"ID": 1, "val": "", "nextID": 0}], actual
        r = self.clerk.fetch(Record, 1)
        assert r.next is None

    def test_store_again(self):
        self.clerk.store(Record())
        r = self.clerk.fetch(Record, 1)
        r.val = "abc"
        self.clerk.store(r)

    def test_store_link(self):
        r = Record(val="a")
        r.next = Record(val="b")

        self.clerk.store(r)
        del r
        r = self.clerk.match(Record, val="a")[0]
        assert r.ID == 2, "didn't save links first!"
        assert r.next is not None, "didn't store the link"
        assert r.next.val == "b", "didn't store link correctly"

        r.next = None
        self.clerk.store(r)
        r = self.clerk.match(Record, val="a")[0]
        assert r.next is None, "didn't delete link!"

        r = Record(val="noNext")
        self.clerk.store(r)
        r = self.clerk.fetch(Record, val="noNext")
        assert r.next is None

    def test_store_memo(self):
        rb = self.clerk.store(Record(val="b"))
        ra = self.clerk.store(Record(val="a", next=rb))

        a, b = self.clerk.match(Record, orderBy="val")
        assert a is ra
        assert b is rb

    def test_store_linksets(self):
        n1 = Node(data="a")
        n1.kids << Node(data="aa")
        n1.kids << Node(data="ab")
        n1.kids[1].kids << Node(data="aba")
        self.clerk.store(n1)
        assert len(n1.kids) == 2, [(k.ID, k.data) for k in n1.kids]

        n = self.clerk.fetch(Node, data="a")
        assert len(
            n1.kids) == 2, "fetch corrupted kids: %s" % [(k.ID, k.data)
                                                         for k in n1.kids]

        assert n.ID == 1, "didn't save parent of linkset first!"
        assert len(
            n.kids) == 2, "didn't store the linkset: %s" % [(k.ID, k.data)
                                                            for k in n.kids]
        assert n.kids[0].data == "aa", "didn't store link correctly"
        assert n.kids[1].data == "ab", "didn't store link correctly"
        assert n.kids[1].kids[0].data == "aba", "didn't store link correctly"
        assert n.kids[0].parent is n
        assert n.kids[1].parent is n

        n.kids[1].parent = None
        n.kids.remove(n.kids[1])
        self.clerk.store(n)
        n = self.clerk.match(Node, data="a")[0]
        assert len(n.kids) == 1

    def test_fetch(self):
        self.clerk.store(Record(val="howdy"))

        # we can pass in an ID:
        obj = self.clerk.fetch(Record, 1)
        assert obj.val == "howdy"

        # or we can use keywords:
        obj = self.clerk.fetch(Record, val="howdy")
        assert obj.val == "howdy"

    def test_delete(self):
        self.test_fetch()
        self.clerk.delete(Record, 1)
        assert self.storage.match("Record") == []

    def test_link_injection(self):
        self.storage.store("Record", val="a", nextID=2)
        self.storage.store("Record", val="b", nextID=3)
        self.storage.store("Record", val="c", nextID=None)

        a = self.clerk.fetch(Record, 1)

        assert a.val == "a"
        assert a.next.val == "b"
        assert a.next.next.val == "c"
        assert a.next.next.next is None

    def test_linkset_injection(self):
        self.storage.store("Node", data="top", parentID=None)
        self.storage.store("Node", data="a", parentID=1)
        self.storage.store("Node", data="a.a", parentID=2)
        self.storage.store("Node", data="b", parentID=1)

        top = self.clerk.fetch(Node, 1)
        assert top.kids[0].data == "a"
        assert top.kids[1].data == "b"
        assert top.kids[1].kids == []
        assert top.kids[0].kids[0].data == "a.a"

    def test_fetch_from_wide_table(self):
        """
        Supose a strongbox has 1 slot, but the table has 2+ columns.
        We can't just jam those columns into the strongbox,
        because strongbox is *designed* to blow up if you try
        to add new attributes.

        But on the other hand, a DBA should be able to add columns
        to the databaes without breaking the code and causing
        AttributeErrors all over the place.

        Instead, Clerk should only use the columns that have
        matching attributes, and simply ignore the others.

        This sorta violates the concept of OnceAndOnlyOnce,
        because now the tables can be out of sync with the
        data model, but I think it's better than the alternative,
        and this is the sort of thing one could check with
        an automated tool.

        #@TODO: write tool to compare DB and object models :)
        """
        try:
            self.storage.store("Record", val="a", extra_column="EEK!")
            a = self.clerk.fetch(Record, 1)
            a.val = "aa"
            self.clerk.store(a)
        except AttributeError:
            self.fail("shouldn't die when columns outnumber attributes")

    def test_dirt(self):
        # dirty by default (already tested in strongbox)
        r = Record()
        assert r.private.isDirty

        # but not after a store:
        r = self.clerk.store(r)
        assert not r.private.isDirty

        # and not after a fetch:
        r = self.clerk.fetch(Record, ID=1)
        assert not r.private.isDirty

        # or a match:
        r = self.clerk.match(Record)[0]
        assert not r.private.isDirty

    def test_recursion(self):
        r = Record()
        r.next = Record()
        r.next.next = r
        assert r.private.isDirty
        assert r.next.private.isDirty
        r = self.clerk.store(r)
        assert r.ID == 2
        assert r.next.ID == 1

        r = self.clerk.fetch(Record, 2)
        assert not r.private.isDirty
        assert not r.next.private.isDirty

        ## and the same thing for linksets:
        n = Node()
        n.kids << Node()
        n.kids[0].kids << n
        assert n.private.isDirty
        assert n.kids[0].private.isDirty
        n = self.clerk.store(n)

    def test_identity(self):
        self.clerk.store(Record(val="one"))
        rec1a = self.clerk.fetch(Record, 1)
        rec1b = self.clerk.fetch(Record, 1)
        assert rec1a is rec1b

        n = Record()
        r = Record(next=n)
        assert self.clerk.store(r) is r
        assert self.clerk.cache[(Record, r.ID)] is r
        assert self.clerk.cache[(Record, n.ID)] is n
        assert self.clerk.cache[(Record, n.ID)] is r.next

    def test_stub(self):
        self.clerk.store(Record(val="a", next=Record(val="b")))
        self.clerk.cache.clear()
        recA = self.clerk.fetch(Record, val="a")
        recB = self.clerk.fetch(Record, val="b")
        assert recA.next.ID == recB.ID
        assert recA.next is recB

    def test_match(self):
        self.clerk.store(Record(val="one"))
        self.clerk.store(Record(val="two"))
        self.clerk.store(Record(val="two"))
        assert len(self.clerk.match(Record, val="zero")) == 0
        assert len(self.clerk.match(Record, val="one")) == 1
        assert len(self.clerk.match(Record, val="two")) == 2

    def test_matchOne(self):
        self.clerk.store(Record(val="one"))
        self.clerk.store(Record(val="two"))
        self.clerk.store(Record(val="two"))

        try:
            self.clerk.matchOne(Record, val="zero")
            self.fail("should have failed for not matching")
        except LookupError:
            pass

        assert isinstance(self.clerk.matchOne(Record, val="one"), Record)

        try:
            self.clerk.matchOne(Record, val="two")
            self.fail("should have failed for matching two")
        except LookupError:
            pass
示例#4
0
    def test_complex_recursion(self):
        """
        test case from cornerhost that exposed a bug.
        this is probably redundant given test_recursion
        but it doesn't hurt to keep it around. :)

        This test is complicated. Basically it sets up
        several classes that refer to each other in a loop
        and makes sure it's possible to save them without
        infinite recursion.
        
        @TODO: isInstance(LinkSetInjector) in Clerk.py need tests
        It ought to do some kind of polymorphism magic anyway.
        (huh??)
        """

        class User(Strongbox):
            ID = attr(long)
            username = attr(str)
            domains = linkset(forward,"user")
            sites = linkset(forward,"user")
        class Domain(Strongbox):
            ID = attr(long)
            user = link(User)
            name = attr(str)
            site = link(forward)            
        class Site(Strongbox):
            ID = attr(long)
            user = link(User)
            domain = link(Domain)
        User.domains.type = Domain
        User.sites.type = Site
        Domain.site.type = Site
        dbMap = Schema({
            User:"******",
            Domain:"domain",
            Domain.user: "******",
            Domain.site: "siteID",
            Site:"site",
            Site.user: "******",
            Site.domain: "domainID",
        })
       
        clerk = Clerk(MockStorage(), dbMap)
        u = clerk.store(User(username="******"))
        u = clerk.match(User,username="******")[0]
        d = clerk.store(Domain(name="ftempy.com", user=u))
        assert d.user, "didn't follow link before fetch"
        d = clerk.match(Domain, name="ftempy.com")[0]

        # the bug was here: it only happened if User had .domains
        # I think because it was a linkset, and the linkset had
        # an injector. Fixed by inlining the injector test into
        # Clekr.store:
        assert d.user, "didn't follow link after fetch"
        assert d.user.ID == u.ID

        # ah, but then we had an infinite recursion problem
        # with site, but I fixed that with private.isDirty:
        d.site = clerk.store(Site(domain=d))
        d = clerk.store(d)
        assert d.site.domain.name == "ftempy.com"

        # and again here:
        d = clerk.fetch(Domain, 1)
        assert not d.private.isDirty
        assert not d.site.private.isDirty # this failed.
        clerk.store(d)                    # so this would recurse forever
示例#5
0
    def test_disappearing_events(self):
        """
        This bug came from duckbill. A subscription
        would post events to its account, and then
        when it showed the statements the new events
        would be the only ones to show up - even though
        there were still others in the database.

        In other words, the injector wasn't working.

        Turns out the problem was that the sub.account
        stub didn't have injectors on ITS dependent
        objects. That's why I now replace .private
        in LinkInjector.inject()
        """
        class Evt(Strongbox):
            ID = attr(long)
            evt = attr(str)
            acc = link(forward)
        class Sub(Strongbox):
            ID = attr(long)
            acc = link(forward)
        class Acc(Strongbox):
            ID = attr(long)
            subs = linkset(Sub, "acc")
            evts = linkset(Evt, "acc")
        Evt.acc.type = Acc
        Sub.acc.type = Acc
        schema = Schema({
            Evt:"evt",
            Sub:"sub",
            Acc:"acc",
            Evt.acc: "accID",
            Sub.acc: "accID",
        })
        st = MockStorage()
        c1 = Clerk(st, schema)

        # store an account with two events and one sub:
        a = Acc()
        a.evts << Evt(evt="1")
        a.evts << Evt(evt="2")
        assert a.private.isDirty
        a.subs << Sub()
        c1.DEBUG = 1
        c1.store(a)

        # new clerk, new cache:
        c2 = Clerk(st, schema)

        # add more events while s.acc is a stub
        s = c2.fetch(Sub, ID=1)
        assert not s.private.isDirty
        #@TODO: maybe len() should trigger the lazyload...
        assert len(s.acc.evts) == 0, [e.evt for e in s.acc.evts]
        s.acc.evts << Evt(evt="3")
        #assert len(s.acc.evts) == 1, [e.evt for e in s.acc.evts]
        assert len(s.acc.evts) == 3, [e.evt for e in s.acc.evts]
        c2.DEBUG = 0
        c2.store(s)
        a2 = c2.fetch(Acc, ID=a.ID)

        assert a is not a2

        # we should now have all three events,
        # but we were getting only the third one:
        assert len(a2.evts) == 3, [e.evt for e in a2.evts]
示例#6
0
class ClerkTest(unittest.TestCase):

    def setUp(self):
        self.storage = MockStorage()
        schema = Schema({
            Node: "Node",
            Node.parent: "parentID",
            Record: "Record",
            Record.next: "nextID",
        })
        self.clerk = Clerk(self.storage, schema)


    def test_store(self):
        self.clerk.store(Record())
        actual = self.storage.match("Record")
        assert actual == [{"ID":1, "val":"", "nextID":0}], actual
        r = self.clerk.fetch(Record, 1)
        assert r.next is None
        

    def test_store_again(self):
        self.clerk.store(Record())
        r = self.clerk.fetch(Record, 1)
        r.val = "abc"
        self.clerk.store(r)

    def test_store_link(self):
        r = Record(val="a")
        r.next = Record(val="b")

        self.clerk.store(r)
        del r
        r = self.clerk.match(Record, val="a")[0]
        assert r.ID == 2, "didn't save links first!"
        assert r.next is not None, "didn't store the link"
        assert r.next.val=="b", "didn't store link correctly"

        r.next = None
        self.clerk.store(r)
        r = self.clerk.match(Record, val="a")[0]
        assert r.next is None, "didn't delete link!"

        r = Record(val="noNext")
        self.clerk.store(r)
        r = self.clerk.fetch(Record, val="noNext")
        assert r.next is None


    def test_store_memo(self):
        rb = self.clerk.store(Record(val="b"))
        ra = self.clerk.store(Record(val="a", next=rb))

        a,b = self.clerk.match(Record, orderBy="val")
        assert a is ra
        assert b is rb


    def test_store_linksets(self):
        n1 = Node(data="a")
        n1.kids << Node(data="aa")
        n1.kids << Node(data="ab")
        n1.kids[1].kids << Node(data="aba")
        self.clerk.store(n1)
        assert len(n1.kids)== 2, [(k.ID, k.data) for k in n1.kids]        
        
        n = self.clerk.fetch(Node, data="a")
        assert len(n1.kids)== 2, "fetch corrupted kids: %s" % [(k.ID, k.data) for k in n1.kids]
        
        assert n.ID == 1, "didn't save parent of linkset first!"
        assert len(n.kids)== 2, "didn't store the linkset: %s" % [(k.ID, k.data) for k in n.kids]
        assert n.kids[0].data=="aa", "didn't store link correctly"
        assert n.kids[1].data=="ab", "didn't store link correctly"
        assert n.kids[1].kids[0].data=="aba", "didn't store link correctly"
        assert n.kids[0].parent is n
        assert n.kids[1].parent is n

        n.kids[1].parent=None
        n.kids.remove(n.kids[1])
        self.clerk.store(n)
        n = self.clerk.match(Node, data="a")[0]
        assert len(n.kids) == 1

        
        
    def test_fetch(self):
        self.clerk.store(Record(val="howdy"))

        # we can pass in an ID:
        obj = self.clerk.fetch(Record, 1)
        assert obj.val == "howdy"

        # or we can use keywords:
        obj = self.clerk.fetch(Record, val="howdy")
        assert obj.val == "howdy"


    def test_delete(self):
        self.test_fetch()
        self.clerk.delete(Record, 1)
        assert self.storage.match("Record") == []


    def test_link_injection(self):
        self.storage.store("Record", val="a", nextID=2)
        self.storage.store("Record", val="b", nextID=3)
        self.storage.store("Record", val="c", nextID=None)

        a = self.clerk.fetch(Record, 1)
        
        assert a.val == "a"
        assert a.next.val == "b"
        assert a.next.next.val == "c"
        assert a.next.next.next is None


    def test_linkset_injection(self):
        self.storage.store("Node", data="top", parentID=None)
        self.storage.store("Node", data="a",   parentID=1)
        self.storage.store("Node", data="a.a", parentID=2)
        self.storage.store("Node", data="b",   parentID=1)
        
        top = self.clerk.fetch(Node, 1)
        assert top.kids[0].data == "a"
        assert top.kids[1].data == "b"
        assert top.kids[1].kids == []
        assert top.kids[0].kids[0].data == "a.a"

        

    def test_fetch_from_wide_table(self):
        """
        Supose a strongbox has 1 slot, but the table has 2+ columns.
        We can't just jam those columns into the strongbox,
        because strongbox is *designed* to blow up if you try
        to add new attributes.

        But on the other hand, a DBA should be able to add columns
        to the databaes without breaking the code and causing
        AttributeErrors all over the place.

        Instead, Clerk should only use the columns that have
        matching attributes, and simply ignore the others.

        This sorta violates the concept of OnceAndOnlyOnce,
        because now the tables can be out of sync with the
        data model, but I think it's better than the alternative,
        and this is the sort of thing one could check with
        an automated tool.

        #@TODO: write tool to compare DB and object models :)
        """
        try:
            self.storage.store("Record", val="a", extra_column="EEK!")
            a = self.clerk.fetch(Record, 1)
            a.val="aa"
            self.clerk.store(a)
        except AttributeError:
            self.fail("shouldn't die when columns outnumber attributes")

    def test_dirt(self):
        # dirty by default (already tested in strongbox)
        r = Record()
        assert r.private.isDirty

        # but not after a store:
        r = self.clerk.store(r)
        assert not r.private.isDirty

        # and not after a fetch:
        r = self.clerk.fetch(Record, ID=1)
        assert not r.private.isDirty

        # or a match:
        r = self.clerk.match(Record)[0]
        assert not r.private.isDirty


    def test_recursion(self):
        r = Record()
        r.next = Record()
        r.next.next = r
        assert r.private.isDirty
        assert r.next.private.isDirty
        r = self.clerk.store(r)
        assert r.ID == 2
        assert r.next.ID == 1

        r = self.clerk.fetch(Record, 2)
        assert not r.private.isDirty
        assert not r.next.private.isDirty


        ## and the same thing for linksets:
        n = Node()
        n.kids << Node()
        n.kids[0].kids << n
        assert n.private.isDirty
        assert n.kids[0].private.isDirty
        n = self.clerk.store(n)
        
        
    def test_identity(self):
        self.clerk.store(Record(val="one"))
        rec1a = self.clerk.fetch(Record, 1)
        rec1b = self.clerk.fetch(Record, 1)
        assert rec1a is rec1b

        n = Record()
        r = Record(next=n)        
        assert self.clerk.store(r) is r
        assert self.clerk.cache[(Record, r.ID)] is r
        assert self.clerk.cache[(Record, n.ID)] is n
        assert self.clerk.cache[(Record, n.ID)] is r.next

    def test_stub(self):
        self.clerk.store(Record(val="a", next=Record(val="b")))
        self.clerk.cache.clear()
        recA = self.clerk.fetch(Record, val="a")
        recB = self.clerk.fetch(Record, val="b")
        assert recA.next.ID == recB.ID
        assert recA.next is recB

    def test_match(self):
        self.clerk.store(Record(val="one"))
        self.clerk.store(Record(val="two"))
        self.clerk.store(Record(val="two"))
        assert len(self.clerk.match(Record, val="zero")) == 0
        assert len(self.clerk.match(Record, val="one")) == 1
        assert len(self.clerk.match(Record, val="two")) == 2
        
    def test_matchOne(self):
        self.clerk.store(Record(val="one"))
        self.clerk.store(Record(val="two"))
        self.clerk.store(Record(val="two"))
        
        try:
            self.clerk.matchOne(Record, val="zero")
            self.fail("should have failed for not matching")
        except LookupError: pass

        assert isinstance(self.clerk.matchOne(Record, val="one"),
                          Record)

        try:
            self.clerk.matchOne(Record, val="two")
            self.fail("should have failed for matching two")
        except LookupError: pass