test/unittest_rqlrewrite.py
brancholdstable
changeset 8462 a14b6562082b
parent 8342 7a5271182ef0
child 9168 0fb4b67bde58
--- a/test/unittest_rqlrewrite.py	Tue Feb 14 15:14:22 2012 +0100
+++ b/test/unittest_rqlrewrite.py	Tue Jul 10 15:07:23 2012 +0200
@@ -1,4 +1,4 @@
-# copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
+# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
 #
 # This file is part of CubicWeb.
@@ -110,10 +110,10 @@
                            'P name "read", P require_group G')
         rqlst = parse('Card C')
         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             u"Any C WHERE C is Card, B eid %(D)s, "
-                             "EXISTS(C in_state A, B in_group E, F require_state A, "
-                             "F name 'read', F require_group E, A is State, E is CWGroup, F is CWPermission)")
+        self.assertEqual(rqlst.as_string(),
+                         u"Any C WHERE C is Card, B eid %(D)s, "
+                         "EXISTS(C in_state A, B in_group E, F require_state A, "
+                         "F name 'read', F require_group E, A is State, E is CWGroup, F is CWPermission)")
 
     def test_multiple_var(self):
         card_constraint = ('X in_state S, U in_group G, P require_state S,'
@@ -123,55 +123,56 @@
         rqlst = parse('Any S WHERE S documented_by C, C eid %(u)s')
         rewrite(rqlst, {('C', 'X'): (card_constraint,), ('S', 'X'): affaire_constraints},
                 kwargs)
-        self.assertMultiLineEqual(rqlst.as_string(),
-                             "Any S WHERE S documented_by C, C eid %(u)s, B eid %(D)s, "
-                             "EXISTS(C in_state A, B in_group E, F require_state A, "
-                             "F name 'read', F require_group E, A is State, E is CWGroup, F is CWPermission), "
-                             "(EXISTS(S ref LIKE 'PUBLIC%')) OR (EXISTS(B in_group G, G name 'public', G is CWGroup)), "
-                             "S is Affaire")
-        self.failUnless('D' in kwargs)
+        self.assertMultiLineEqual(
+            rqlst.as_string(),
+            "Any S WHERE S documented_by C, C eid %(u)s, B eid %(D)s, "
+            "EXISTS(C in_state A, B in_group E, F require_state A, "
+            "F name 'read', F require_group E, A is State, E is CWGroup, F is CWPermission), "
+            "(EXISTS(S ref LIKE 'PUBLIC%')) OR (EXISTS(B in_group G, G name 'public', G is CWGroup)), "
+            "S is Affaire")
+        self.assertTrue('D' in kwargs)
 
     def test_or(self):
         constraint = '(X identity U) OR (X in_state ST, CL identity U, CL in_state ST, ST name "subscribed")'
         rqlst = parse('Any S WHERE S owned_by C, C eid %(u)s, S is in (CWUser, CWGroup)')
         rewrite(rqlst, {('C', 'X'): (constraint,)}, {'u':1})
-        self.failUnlessEqual(rqlst.as_string(),
-                             "Any S WHERE S owned_by C, C eid %(u)s, S is IN(CWUser, CWGroup), A eid %(B)s, "
-                             "EXISTS((C identity A) OR (C in_state D, E identity A, "
-                             "E in_state D, D name 'subscribed'), D is State, E is CWUser)")
+        self.assertEqual(rqlst.as_string(),
+                         "Any S WHERE S owned_by C, C eid %(u)s, S is IN(CWUser, CWGroup), A eid %(B)s, "
+                         "EXISTS((C identity A) OR (C in_state D, E identity A, "
+                         "E in_state D, D name 'subscribed'), D is State, E is CWUser)")
 
     def test_simplified_rqlst(self):
         constraint = ('X in_state S, U in_group G, P require_state S,'
                            'P name "read", P require_group G')
         rqlst = parse('Any 2') # this is the simplified rql st for Any X WHERE X eid 12
         rewrite(rqlst, {('2', 'X'): (constraint,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             u"Any 2 WHERE B eid %(C)s, "
-                             "EXISTS(2 in_state A, B in_group D, E require_state A, "
-                             "E name 'read', E require_group D, A is State, D is CWGroup, E is CWPermission)")
+        self.assertEqual(rqlst.as_string(),
+                         u"Any 2 WHERE B eid %(C)s, "
+                         "EXISTS(2 in_state A, B in_group D, E require_state A, "
+                         "E name 'read', E require_group D, A is State, D is CWGroup, E is CWPermission)")
 
     def test_optional_var_1(self):
         constraint = ('X in_state S, U in_group G, P require_state S,'
                            'P name "read", P require_group G')
         rqlst = parse('Any A,C WHERE A documented_by C?')
         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             "Any A,C WHERE A documented_by C?, A is Affaire "
-                             "WITH C BEING "
-                             "(Any C WHERE EXISTS(C in_state B, D in_group F, G require_state B, G name 'read', "
-                             "G require_group F), D eid %(A)s, C is Card)")
+        self.assertEqual(rqlst.as_string(),
+                         "Any A,C WHERE A documented_by C?, A is Affaire "
+                         "WITH C BEING "
+                         "(Any C WHERE EXISTS(C in_state B, D in_group F, G require_state B, G name 'read', "
+                         "G require_group F), D eid %(A)s, C is Card)")
 
     def test_optional_var_2(self):
         constraint = ('X in_state S, U in_group G, P require_state S,'
                            'P name "read", P require_group G')
         rqlst = parse('Any A,C,T WHERE A documented_by C?, C title T')
         rewrite(rqlst, {('C', 'X'): (constraint,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             "Any A,C,T WHERE A documented_by C?, A is Affaire "
-                             "WITH C,T BEING "
-                             "(Any C,T WHERE C title T, EXISTS(C in_state B, D in_group F, "
-                             "G require_state B, G name 'read', G require_group F), "
-                             "D eid %(A)s, C is Card)")
+        self.assertEqual(rqlst.as_string(),
+                         "Any A,C,T WHERE A documented_by C?, A is Affaire "
+                         "WITH C,T BEING "
+                         "(Any C,T WHERE C title T, EXISTS(C in_state B, D in_group F, "
+                         "G require_state B, G name 'read', G require_group F), "
+                         "D eid %(A)s, C is Card)")
 
     def test_optional_var_3(self):
         constraint1 = ('X in_state S, U in_group G, P require_state S,'
@@ -179,12 +180,12 @@
         constraint2 = 'X in_state S, S name "public"'
         rqlst = parse('Any A,C,T WHERE A documented_by C?, C title T')
         rewrite(rqlst, {('C', 'X'): (constraint1, constraint2)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             "Any A,C,T WHERE A documented_by C?, A is Affaire "
-                             "WITH C,T BEING (Any C,T WHERE C title T, "
-                             "EXISTS(C in_state B, D in_group F, G require_state B, G name 'read', G require_group F), "
-                             "D eid %(A)s, C is Card, "
-                             "EXISTS(C in_state E, E name 'public'))")
+        self.assertEqual(rqlst.as_string(),
+                         "Any A,C,T WHERE A documented_by C?, A is Affaire "
+                         "WITH C,T BEING (Any C,T WHERE C title T, "
+                         "(EXISTS(C in_state B, D in_group F, G require_state B, G name 'read', G require_group F)) "
+                         "OR (EXISTS(C in_state E, E name 'public')), "
+                         "D eid %(A)s, C is Card)")
 
     def test_optional_var_4(self):
         constraint1 = 'A created_by U, X documented_by A'
@@ -194,12 +195,12 @@
         rewrite(rqlst, {('LA', 'X'): (constraint1, constraint2),
                         ('X', 'X'): (constraint3,),
                         ('Y', 'X'): (constraint3,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
+        self.assertEqual(rqlst.as_string(),
                              u'Any X,LA,Y WHERE LA? documented_by X, LA concerne Y, B eid %(C)s, '
                              'EXISTS(X created_by B), EXISTS(Y created_by B), '
                              'X is Card, Y is IN(Division, Note, Societe) '
-                             'WITH LA BEING (Any LA WHERE EXISTS(A created_by B, LA documented_by A), '
-                             'B eid %(D)s, LA is Affaire, EXISTS(E created_by B, LA concerne E))')
+                             'WITH LA BEING (Any LA WHERE (EXISTS(A created_by B, LA documented_by A)) OR (EXISTS(E created_by B, LA concerne E)), '
+                             'B eid %(D)s, LA is Affaire)')
 
     def test_optional_var_inlined(self):
         c1 = ('X require_permission P')
@@ -209,12 +210,12 @@
                         ('A', 'X'): (c2,),
                         }, {})
         # XXX suboptimal
-        self.failUnlessEqual(rqlst.as_string(),
-                             "Any C,A,R WITH A,C,R BEING "
-                             "(Any A,C,R WHERE A? inlined_card C, A ref R, "
-                             "(A is NULL) OR (EXISTS(A inlined_card B, B require_permission D, "
-                             "B is Card, D is CWPermission)), "
-                             "A is Affaire, C is Card, EXISTS(C require_permission E, E is CWPermission))")
+        self.assertEqual(rqlst.as_string(),
+                         "Any C,A,R WITH A,C,R BEING "
+                         "(Any A,C,R WHERE A? inlined_card C, A ref R, "
+                         "(A is NULL) OR (EXISTS(A inlined_card B, B require_permission D, "
+                         "B is Card, D is CWPermission)), "
+                         "A is Affaire, C is Card, EXISTS(C require_permission E, E is CWPermission))")
 
     # def test_optional_var_inlined_has_perm(self):
     #     c1 = ('X require_permission P')
@@ -223,7 +224,7 @@
     #     rewrite(rqlst, {('C', 'X'): (c1,),
     #                     ('A', 'X'): (c2,),
     #                     }, {})
-    #     self.failUnlessEqual(rqlst.as_string(),
+    #     self.assertEqual(rqlst.as_string(),
     #                          "")
 
     def test_optional_var_inlined_imbricated_error(self):
@@ -242,11 +243,11 @@
         rqlst = parse('Any A,W WHERE A inlined_card C?, C inlined_note N, '
                       'N inlined_affaire W')
         rewrite(rqlst, {('C', 'X'): (c1,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             'Any A,W WHERE A inlined_card C?, A is Affaire '
-                             'WITH C,N,W BEING (Any C,N,W WHERE C inlined_note N, '
-                             'N inlined_affaire W, EXISTS(C require_permission B), '
-                             'C is Card, N is Note, W is Affaire)')
+        self.assertEqual(rqlst.as_string(),
+                         'Any A,W WHERE A inlined_card C?, A is Affaire '
+                         'WITH C,N,W BEING (Any C,N,W WHERE C inlined_note N, '
+                         'N inlined_affaire W, EXISTS(C require_permission B), '
+                         'C is Card, N is Note, W is Affaire)')
 
     def test_relation_optimization_1_lhs(self):
         # since Card in_state State as monovalued cardinality, the in_state
@@ -255,66 +256,66 @@
         snippet = ('X in_state S, S name "hop"')
         rqlst = parse('Card C WHERE C in_state STATE')
         rewrite(rqlst, {('C', 'X'): (snippet,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             "Any C WHERE C in_state STATE, C is Card, "
-                             "EXISTS(STATE name 'hop'), STATE is State")
+        self.assertEqual(rqlst.as_string(),
+                         "Any C WHERE C in_state STATE, C is Card, "
+                         "EXISTS(STATE name 'hop'), STATE is State")
 
     def test_relation_optimization_1_rhs(self):
         snippet = ('TW subworkflow_exit X, TW name "hop"')
         rqlst = parse('WorkflowTransition C WHERE C subworkflow_exit EXIT')
         rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             "Any C WHERE C subworkflow_exit EXIT, C is WorkflowTransition, "
-                             "EXISTS(C name 'hop'), EXIT is SubWorkflowExitPoint")
+        self.assertEqual(rqlst.as_string(),
+                         "Any C WHERE C subworkflow_exit EXIT, C is WorkflowTransition, "
+                         "EXISTS(C name 'hop'), EXIT is SubWorkflowExitPoint")
 
     def test_relation_optimization_2_lhs(self):
         # optional relation can be shared if also optional in the snippet
         snippet = ('X in_state S?, S name "hop"')
         rqlst = parse('Card C WHERE C in_state STATE?')
         rewrite(rqlst, {('C', 'X'): (snippet,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             "Any C WHERE C in_state STATE?, C is Card, "
-                             "EXISTS(STATE name 'hop'), STATE is State")
+        self.assertEqual(rqlst.as_string(),
+                         "Any C WHERE C in_state STATE?, C is Card, "
+                         "EXISTS(STATE name 'hop'), STATE is State")
     def test_relation_optimization_2_rhs(self):
         snippet = ('TW? subworkflow_exit X, TW name "hop"')
         rqlst = parse('SubWorkflowExitPoint EXIT WHERE C? subworkflow_exit EXIT')
         rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             "Any EXIT WHERE C? subworkflow_exit EXIT, EXIT is SubWorkflowExitPoint, "
-                             "EXISTS(C name 'hop'), C is WorkflowTransition")
+        self.assertEqual(rqlst.as_string(),
+                         "Any EXIT WHERE C? subworkflow_exit EXIT, EXIT is SubWorkflowExitPoint, "
+                         "EXISTS(C name 'hop'), C is WorkflowTransition")
 
     def test_relation_optimization_3_lhs(self):
         # optional relation in the snippet but not in the orig tree can be shared
         snippet = ('X in_state S?, S name "hop"')
         rqlst = parse('Card C WHERE C in_state STATE')
         rewrite(rqlst, {('C', 'X'): (snippet,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             "Any C WHERE C in_state STATE, C is Card, "
-                             "EXISTS(STATE name 'hop'), STATE is State")
+        self.assertEqual(rqlst.as_string(),
+                         "Any C WHERE C in_state STATE, C is Card, "
+                         "EXISTS(STATE name 'hop'), STATE is State")
     def test_relation_optimization_3_rhs(self):
         snippet = ('TW? subworkflow_exit X, TW name "hop"')
         rqlst = parse('WorkflowTransition C WHERE C subworkflow_exit EXIT')
         rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             "Any C WHERE C subworkflow_exit EXIT, C is WorkflowTransition, "
-                             "EXISTS(C name 'hop'), EXIT is SubWorkflowExitPoint")
+        self.assertEqual(rqlst.as_string(),
+                         "Any C WHERE C subworkflow_exit EXIT, C is WorkflowTransition, "
+                         "EXISTS(C name 'hop'), EXIT is SubWorkflowExitPoint")
 
     def test_relation_non_optimization_1_lhs(self):
         # but optional relation in the orig tree but not in the snippet can't be shared
         snippet = ('X in_state S, S name "hop"')
         rqlst = parse('Card C WHERE C in_state STATE?')
         rewrite(rqlst, {('C', 'X'): (snippet,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             "Any C WHERE C in_state STATE?, C is Card, "
-                             "EXISTS(C in_state A, A name 'hop', A is State), STATE is State")
+        self.assertEqual(rqlst.as_string(),
+                         "Any C WHERE C in_state STATE?, C is Card, "
+                         "EXISTS(C in_state A, A name 'hop', A is State), STATE is State")
     def test_relation_non_optimization_1_rhs(self):
         snippet = ('TW subworkflow_exit X, TW name "hop"')
         rqlst = parse('SubWorkflowExitPoint EXIT WHERE C? subworkflow_exit EXIT')
         rewrite(rqlst, {('EXIT', 'X'): (snippet,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             "Any EXIT WHERE C? subworkflow_exit EXIT, EXIT is SubWorkflowExitPoint, "
-                             "EXISTS(A subworkflow_exit EXIT, A name 'hop', A is WorkflowTransition), "
-                             "C is WorkflowTransition")
+        self.assertEqual(rqlst.as_string(),
+                         "Any EXIT WHERE C? subworkflow_exit EXIT, EXIT is SubWorkflowExitPoint, "
+                         "EXISTS(A subworkflow_exit EXIT, A name 'hop', A is WorkflowTransition), "
+                         "C is WorkflowTransition")
 
     def test_unsupported_constraint_1(self):
         # CWUser doesn't have require_permission
@@ -326,110 +327,137 @@
         trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"')
         rqlst = parse('Any U,T WHERE U is CWUser, T wf_info_for U')
         rewrite(rqlst, {('T', 'X'): (trinfo_constraint, 'X wf_info_for Y, Y in_group G, G name "managers"')}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             u"Any U,T WHERE U is CWUser, T wf_info_for U, "
-                             "EXISTS(U in_group B, B name 'managers', B is CWGroup), T is TrInfo")
+        self.assertEqual(rqlst.as_string(),
+                         u"Any U,T WHERE U is CWUser, T wf_info_for U, "
+                         "EXISTS(U in_group B, B name 'managers', B is CWGroup), T is TrInfo")
 
     def test_unsupported_constraint_3(self):
         self.skipTest('raise unauthorized for now')
         trinfo_constraint = ('X wf_info_for Y, Y require_permission P, P name "read"')
         rqlst = parse('Any T WHERE T wf_info_for X')
         rewrite(rqlst, {('T', 'X'): (trinfo_constraint, 'X in_group G, G name "managers"')}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             u'XXX dunno what should be generated')
+        self.assertEqual(rqlst.as_string(),
+                         u'XXX dunno what should be generated')
 
     def test_add_ambiguity_exists(self):
         constraint = ('X concerne Y')
         rqlst = parse('Affaire X')
         rewrite(rqlst, {('X', 'X'): (constraint,)}, {})
-        self.failUnlessEqual(rqlst.as_string(),
-                             u"Any X WHERE X is Affaire, ((EXISTS(X concerne A, A is Division)) OR (EXISTS(X concerne C, C is Societe))) OR (EXISTS(X concerne B, B is Note))")
+        self.assertEqual(rqlst.as_string(),
+                         u"Any X WHERE X is Affaire, ((EXISTS(X concerne A, A is Division)) OR (EXISTS(X concerne C, C is Societe))) OR (EXISTS(X concerne B, B is Note))")
 
     def test_add_ambiguity_outerjoin(self):
         constraint = ('X concerne Y')
         rqlst = parse('Any X,C WHERE X? documented_by C')
         rewrite(rqlst, {('X', 'X'): (constraint,)}, {})
         # ambiguity are kept in the sub-query, no need to be resolved using OR
-        self.failUnlessEqual(rqlst.as_string(),
-                             u"Any X,C WHERE X? documented_by C, C is Card WITH X BEING (Any X WHERE EXISTS(X concerne A), X is Affaire)")
+        self.assertEqual(rqlst.as_string(),
+                         u"Any X,C WHERE X? documented_by C, C is Card WITH X BEING (Any X WHERE EXISTS(X concerne A), X is Affaire)")
 
 
     def test_rrqlexpr_nonexistant_subject_1(self):
         constraint = RRQLExpression('S owned_by U')
         rqlst = parse('Card C')
         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
-        self.failUnlessEqual(rqlst.as_string(),
-                             u"Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A)")
+        self.assertEqual(rqlst.as_string(),
+                         u"Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A)")
         rqlst = parse('Card C')
         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'OU')
-        self.failUnlessEqual(rqlst.as_string(),
-                             u"Any C WHERE C is Card")
+        self.assertEqual(rqlst.as_string(),
+                         u"Any C WHERE C is Card")
         rqlst = parse('Card C')
         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SOU')
-        self.failUnlessEqual(rqlst.as_string(),
-                             u"Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A)")
+        self.assertEqual(rqlst.as_string(),
+                         u"Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A)")
 
     def test_rrqlexpr_nonexistant_subject_2(self):
         constraint = RRQLExpression('S owned_by U, O owned_by U, O is Card')
         rqlst = parse('Card C')
         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
-        self.failUnlessEqual(rqlst.as_string(),
-                             'Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A)')
+        self.assertEqual(rqlst.as_string(),
+                         'Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A)')
         rqlst = parse('Card C')
         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'OU')
-        self.failUnlessEqual(rqlst.as_string(),
-                             'Any C WHERE C is Card, B eid %(D)s, EXISTS(A owned_by B, A is Card)')
+        self.assertEqual(rqlst.as_string(),
+                         'Any C WHERE C is Card, B eid %(D)s, EXISTS(A owned_by B, A is Card)')
         rqlst = parse('Card C')
         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SOU')
-        self.failUnlessEqual(rqlst.as_string(),
-                             'Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A, D owned_by A, D is Card)')
+        self.assertEqual(rqlst.as_string(),
+                         'Any C WHERE C is Card, A eid %(B)s, EXISTS(C owned_by A, D owned_by A, D is Card)')
 
     def test_rrqlexpr_nonexistant_subject_3(self):
         constraint = RRQLExpression('U in_group G, G name "users"')
         rqlst = parse('Card C')
         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
-        self.failUnlessEqual(rqlst.as_string(),
-                             u'Any C WHERE C is Card, A eid %(B)s, EXISTS(A in_group D, D name "users", D is CWGroup)')
+        self.assertEqual(rqlst.as_string(),
+                         u'Any C WHERE C is Card, A eid %(B)s, EXISTS(A in_group D, D name "users", D is CWGroup)')
 
     def test_rrqlexpr_nonexistant_subject_4(self):
         constraint = RRQLExpression('U in_group G, G name "users", S owned_by U')
         rqlst = parse('Card C')
         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'SU')
-        self.failUnlessEqual(rqlst.as_string(),
-                             u'Any C WHERE C is Card, A eid %(B)s, EXISTS(A in_group D, D name "users", C owned_by A, D is CWGroup)')
+        self.assertEqual(rqlst.as_string(),
+                         u'Any C WHERE C is Card, A eid %(B)s, EXISTS(A in_group D, D name "users", C owned_by A, D is CWGroup)')
         rqlst = parse('Card C')
         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'OU')
-        self.failUnlessEqual(rqlst.as_string(),
-                             u'Any C WHERE C is Card, A eid %(B)s, EXISTS(A in_group D, D name "users", D is CWGroup)')
+        self.assertEqual(rqlst.as_string(),
+                         u'Any C WHERE C is Card, A eid %(B)s, EXISTS(A in_group D, D name "users", D is CWGroup)')
 
     def test_rrqlexpr_nonexistant_subject_5(self):
         constraint = RRQLExpression('S owned_by Z, O owned_by Z, O is Card')
         rqlst = parse('Card C')
         rewrite(rqlst, {('C', 'S'): (constraint,)}, {}, 'S')
-        self.failUnlessEqual(rqlst.as_string(),
-                             u"Any C WHERE C is Card, EXISTS(C owned_by A, A is CWUser)")
+        self.assertEqual(rqlst.as_string(),
+                         u"Any C WHERE C is Card, EXISTS(C owned_by A, A is CWUser)")
 
     def test_rqlexpr_not_relation_1_1(self):
         constraint = RRQLExpression('X owned_by Z, Z login "hop"', 'X')
         rqlst = parse('Affaire A WHERE NOT EXISTS(A documented_by C)')
         rewrite(rqlst, {('C', 'X'): (constraint,)}, {}, 'X')
-        self.failUnlessEqual(rqlst.as_string(),
-                             u'Any A WHERE NOT EXISTS(A documented_by C, EXISTS(C owned_by B, B login "hop", B is CWUser), C is Card), A is Affaire')
+        self.assertEqual(rqlst.as_string(),
+                         u'Any A WHERE NOT EXISTS(A documented_by C, EXISTS(C owned_by B, B login "hop", B is CWUser), C is Card), A is Affaire')
 
     def test_rqlexpr_not_relation_1_2(self):
         constraint = RRQLExpression('X owned_by Z, Z login "hop"', 'X')
         rqlst = parse('Affaire A WHERE NOT EXISTS(A documented_by C)')
         rewrite(rqlst, {('A', 'X'): (constraint,)}, {}, 'X')
-        self.failUnlessEqual(rqlst.as_string(),
-                             u'Any A WHERE NOT EXISTS(A documented_by C, C is Card), A is Affaire, EXISTS(A owned_by B, B login "hop", B is CWUser)')
+        self.assertEqual(rqlst.as_string(),
+                         u'Any A WHERE NOT EXISTS(A documented_by C, C is Card), A is Affaire, EXISTS(A owned_by B, B login "hop", B is CWUser)')
 
     def test_rqlexpr_not_relation_2(self):
         constraint = RRQLExpression('X owned_by Z, Z login "hop"', 'X')
         rqlst = rqlhelper.parse('Affaire A WHERE NOT A documented_by C', annotate=False)
         rewrite(rqlst, {('C', 'X'): (constraint,)}, {}, 'X')
-        self.failUnlessEqual(rqlst.as_string(),
-                             u'Any A WHERE NOT EXISTS(A documented_by C, EXISTS(C owned_by B, B login "hop", B is CWUser), C is Card), A is Affaire')
+        self.assertEqual(rqlst.as_string(),
+                         u'Any A WHERE NOT EXISTS(A documented_by C, EXISTS(C owned_by B, B login "hop", B is CWUser), C is Card), A is Affaire')
+
+    def test_rqlexpr_multiexpr_outerjoin(self):
+        c1 = RRQLExpression('X owned_by Z, Z login "hop"', 'X')
+        c2 = RRQLExpression('X owned_by Z, Z login "hip"', 'X')
+        c3 = RRQLExpression('X owned_by Z, Z login "momo"', 'X')
+        rqlst = rqlhelper.parse('Any A WHERE A documented_by C?', annotate=False)
+        rewrite(rqlst, {('C', 'X'): (c1, c2, c3)}, {}, 'X')
+        self.assertEqual(rqlst.as_string(),
+                         u'Any A WHERE A documented_by C?, A is Affaire '
+                         'WITH C BEING (Any C WHERE ((EXISTS(C owned_by B, B login "hop")) '
+                         'OR (EXISTS(C owned_by D, D login "momo"))) '
+                         'OR (EXISTS(C owned_by A, A login "hip")), C is Card)')
 
+    def test_multiple_erql_one_bad(self):
+        #: reproduce bug #2236985
+        #: (rqlrewrite fails to remove rewritten entry for unsupported constraint and then crash)
+        #:
+        #: This check a very rare code path triggered by the four condition below
+
+        # 1. c_ok introduce an ambiguity
+        c_ok = ERQLExpression('X concerne R')
+        # 2. c_bad is just plain wrong and won't be kept
+        # 3. but it declare a new variable
+        # 4. this variable require a rewrite
+        c_bad = ERQLExpression('X documented_by R, A in_state R')
+
+        rqlst = parse('Any A, R WHERE A ref R, S is Affaire')
+        rewrite(rqlst, {('A', 'X'): (c_ok, c_bad)}, {})
 
 if __name__ == '__main__':
     unittest_main()