diff test/db_test_base.py @ 6148:8497bf3f23a1

Allow to define reverse Multilinks Now it's possible to specify a rev_multilink parameter when creating Link or Multilink properties. The parameter takes a property name to be inserted into the linked-to class. It allows to navigate from the other side of the link as if it where a forward Multilink using the existing data structures.
author Ralf Schlatterbeck <rsc@runtux.com>
date Wed, 29 Apr 2020 16:30:27 +0200
parents e6073c2291c6
children ff059afae50a
line wrap: on
line diff
--- a/test/db_test_base.py	Sun Apr 12 21:03:55 2020 +0100
+++ b/test/db_test_base.py	Wed Apr 29 16:30:27 2020 +0200
@@ -108,13 +108,14 @@
     file_nidx = module.FileClass(db, "file_nidx", content=String(indexme='no'))
 
     # initialize quiet mode a second way without using Multilink("user", quiet=True)
-    mynosy = Multilink("user")
+    mynosy = Multilink("user", rev_multilink='nosy_issues')
     mynosy.quiet = True
     issue = module.IssueClass(db, "issue", title=String(indexme="yes"),
         status=Link("status"), nosy=mynosy, deadline=Date(quiet=True),
         foo=Interval(quiet=True, default_value=date.Interval('-1w')),
-        files=Multilink("file"), assignedto=Link('user', quiet=True),
-        priority=Link('priority'), spam=Multilink('msg'), feedback=Link('msg'))
+        files=Multilink("file"), assignedto=Link('user', quiet=True,
+        rev_multilink='issues'), priority=Link('priority'),
+        spam=Multilink('msg'), feedback=Link('msg'))
     stuff = module.Class(db, "stuff", stuff=String())
     session = module.Class(db, 'session', title=String())
     msg = module.FileClass(db, "msg", date=Date(),
@@ -1065,12 +1066,12 @@
         result=self.db.user.history(new_user, skipquiet=False)
         '''
         [('3', <Date 2017-04-14.02:12:20.922>, '1', 'create', {}),
-         ('3', <Date 2017-04-14.02:12:20.922>, '1', 'set', 
-           {'username': 'pete', 'assignable': False, 
+         ('3', <Date 2017-04-14.02:12:20.922>, '1', 'set',
+           {'username': 'pete', 'assignable': False,
             'supervisor': None, 'realname': None, 'rating': None,
             'age': 10, 'password': None})]
         '''
-        expected = {'username': 'pete', 'assignable': False, 
+        expected = {'username': 'pete', 'assignable': False,
             'supervisor': None, 'realname': None, 'rating': None,
             'age': 10, 'password': None}
 
@@ -1206,10 +1207,10 @@
                 {'username': 'pete', 'assignable': False,
                  'supervisor': None, 'realname': None,
                   'rating': None, 'age': 10, 'password': None}),
-          ('3', <Date 2017-04-15.02:06:11.482>, '1', 'link', 
+          ('3', <Date 2017-04-15.02:06:11.482>, '1', 'link',
                 ('issue', '1', 'nosy')),
           ('3', <Date 2017-04-15.02:06:11.482>, '1', 'unlink',
-                ('issue', '1', 'nosy')), 
+                ('issue', '1', 'nosy')),
           ('3', <Date 2017-04-15.02:06:11.482>, '1', 'set',
              {'roles': None})]
         '''
@@ -1241,13 +1242,13 @@
         result.sort()
         ''' result should look like
         [('3', <Date 2017-04-15.01:43:26.911>, '1', 'create', {}),
-        ('3', <Date 2017-04-15.01:43:26.911>, '1', 'set', 
-            {'username': 'pete', 'assignable': False, 
+        ('3', <Date 2017-04-15.01:43:26.911>, '1', 'set',
+            {'username': 'pete', 'assignable': False,
               'supervisor': None, 'age': 10})]
         '''
         # analyze last item
         (id, tx_date, user, action, args) = result[-1]
-        expected= {'username': 'pete', 'assignable': False, 
+        expected= {'username': 'pete', 'assignable': False,
                    'supervisor': None}
 
         self.assertEqual('3', id)
@@ -1582,6 +1583,12 @@
         l = self.db.issue.find(status={'1':1, '3':1})
         l.sort()
         self.assertEqual(l, [one, three, four])
+        l = self.db.issue.find(status=('1', '3'))
+        l.sort()
+        self.assertEqual(l, [one, three, four])
+        l = self.db.issue.find(status=['1', '3'])
+        l.sort()
+        self.assertEqual(l, [one, three, four])
         l = self.db.issue.find(assignedto={None:1, '1':1})
         l.sort()
         self.assertEqual(l, [one, three, four])
@@ -1810,6 +1817,25 @@
             ae(filt(None, {a: ['-1', None]}, ('+','id'), grp), ['3','4'])
             ae(filt(None, {a: ['1', None]}, ('+','id'), grp), ['1', '3','4'])
 
+    def testFilteringRevLink(self):
+        ae, filter, filter_iter = self.filteringSetupTransitiveSearch('user')
+        # We have
+        # issue assignedto
+        # 1:    6
+        # 2:    6
+        # 3:    7
+        # 4:    8
+        # 5:    9
+        # 6:    10
+        # 7:    10
+        # 8:    10
+        for filt in filter, filter_iter:
+            ae(filt(None, {'issues': ['3', '4']}), ['7', '8'])
+            ae(filt(None, {'issues': ['1', '4', '8']}), ['6', '8', '10'])
+            ae(filt(None, {'issues.title': ['ts2']}), ['6'])
+            ae(filt(None, {'issues': ['-1']}), ['1', '2', '3', '4', '5'])
+            ae(filt(None, {'issues': '-1'}), ['1', '2', '3', '4', '5'])
+
     def testFilteringLinkSortSearchMultilink(self):
         ae, filter, filter_iter = self.filteringSetup()
         a = 'assignedto'
@@ -1841,6 +1867,27 @@
             ae(filt(None, {'nosy': ['1','2']}, ('+', 'status'),
                 ('-', 'deadline')), ['4', '3'])
 
+    def testFilteringRevMultilink(self):
+        ae, filter, filter_iter = self.filteringSetupTransitiveSearch('user')
+        ni = 'nosy_issues'
+        self.db.issue.set('6', nosy=['3', '4', '5'])
+        self.db.issue.set('7', nosy=['5'])
+        # After this setup we have the following values for nosy:
+        # issue   nosy
+        # 1:      4
+        # 2:      5
+        # 3:
+        # 4:
+        # 5:
+        # 6:      3, 4, 5
+        # 7:      5
+        # 8:
+        for filt in filter, filter_iter:
+            ae(filt(None, {ni: ['1', '2']}), ['4', '5'])
+            ae(filt(None, {ni: ['6','7']}), ['3', '4', '5'])
+            ae(filt(None, {ni: ['-1']}), ['1', '2', '6', '7', '8', '9', '10'])
+            ae(filt(None, {ni: '-1'}), ['1', '2', '6', '7', '8', '9', '10'])
+
     def testFilteringMany(self):
         ae, filter, filter_iter = self.filteringSetup()
         for f in filter, filter_iter:
@@ -2517,7 +2564,7 @@
             self.assertRaises(UsageError, tool.props_from_args, "fullname") # invalid propname
 
             self.assertEqual(tool.props_from_args("="), {'': None}) # not sure this desired, I'd expect UsageError
-            
+
             props = tool.props_from_args(["fullname=robert", "friends=+rouilj,+other", "key="])
             self.assertEqual(props, {'fullname': 'robert', 'friends': '+rouilj,+other', 'key': None})
 
@@ -2527,7 +2574,7 @@
             # This writes to stdout, need to figure out how to redirect to a variable.
             # classhandle = tool.get_class("user") # valid class
             # FIXME there should be some test here
- 
+
             issue_class_spec = tool.do_specification(["issue"])
             self.assertEqual(sorted (soutput),
                              ['assignedto: <roundup.hyperdb.Link to "user">\n',
@@ -3265,7 +3312,7 @@
     # end class Request
 
     def setUp(self):
-        super(HTMLItemTest, self).setUp()    
+        super(HTMLItemTest, self).setUp()
         self.tracker = tracker = setupTracker(self.dirname, self.backend)
         db = self.db = tracker.open('admin')
         req = self.Request()

Roundup Issue Tracker: http://roundup-tracker.org/