def test_q(self): version = Version(build=EXCHANGE_2007) account = mock_account(version=version, protocol=mock_protocol(version=version, service_endpoint='example.com')) root = Root(account=account) tz = zoneinfo.ZoneInfo('Europe/Copenhagen') start = datetime.datetime(2020, 9, 26, 8, 0, 0, tzinfo=tz) end = datetime.datetime(2020, 9, 26, 11, 0, 0, tzinfo=tz) result = '''\ <m:Restriction xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"> <t:And xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Or> <t:Contains ContainmentMode="Substring" ContainmentComparison="Exact"> <t:FieldURI FieldURI="item:Categories"/> <t:Constant Value="FOO"/> </t:Contains> <t:Contains ContainmentMode="Substring" ContainmentComparison="Exact"> <t:FieldURI FieldURI="item:Categories"/> <t:Constant Value="BAR"/> </t:Contains> </t:Or> <t:IsGreaterThan> <t:FieldURI FieldURI="calendar:End"/> <t:FieldURIOrConstant> <t:Constant Value="2020-09-26T08:00:00+02:00"/> </t:FieldURIOrConstant> </t:IsGreaterThan> <t:IsLessThan> <t:FieldURI FieldURI="calendar:Start"/> <t:FieldURIOrConstant> <t:Constant Value="2020-09-26T11:00:00+02:00"/> </t:FieldURIOrConstant> </t:IsLessThan> </t:And> </m:Restriction>''' q = Q(Q(categories__contains='FOO') | Q(categories__contains='BAR'), start__lt=end, end__gt=start) r = Restriction(q, folders=[Calendar(root=root)], applies_to=Restriction.ITEMS) self.assertEqual(str(r), ''.join(s.lstrip() for s in result.split('\n'))) # Test empty Q q = Q() self.assertEqual(q.to_xml(folders=[Calendar()], version=version, applies_to=Restriction.ITEMS), None) with self.assertRaises(ValueError): Restriction(q, folders=[Calendar(root=root)], applies_to=Restriction.ITEMS) # Test validation with self.assertRaises(ValueError): Q(datetime_created__range=(1,)) # Must have exactly 2 args with self.assertRaises(ValueError): Q(datetime_created__range=(1, 2, 3)) # Must have exactly 2 args with self.assertRaises(TypeError): Q(datetime_created=Build(15, 1)).clean(version=Version(build=EXCHANGE_2007)) # Must be serializable with self.assertRaises(ValueError): # Must be a timezone-aware datetime Q(datetime_created=datetime.datetime(2017, 1, 1)).clean(version=Version(build=EXCHANGE_2007)) with self.assertRaises(ValueError): Q(categories__contains=[[1, 2], [3, 4]]).clean(version=Version(build=EXCHANGE_2007)) # Must be single value
def test_q_inversion(self): version = Version(build=EXCHANGE_2007) account = mock_account(version=version, protocol=mock_protocol( version=version, service_endpoint='example.com')) root = Root(account=account) self.assertEqual((~Q(foo=5)).op, Q.NE) self.assertEqual((~Q(foo__not=5)).op, Q.EQ) self.assertEqual((~Q(foo__lt=5)).op, Q.GTE) self.assertEqual((~Q(foo__lte=5)).op, Q.GT) self.assertEqual((~Q(foo__gt=5)).op, Q.LTE) self.assertEqual((~Q(foo__gte=5)).op, Q.LT) # Test not not Q on a non-leaf self.assertEqual(Q(foo__contains=('bar', 'baz')).conn_type, Q.AND) self.assertEqual((~Q(foo__contains=('bar', 'baz'))).conn_type, Q.NOT) self.assertEqual((~~Q(foo__contains=('bar', 'baz'))).conn_type, Q.AND) self.assertEqual(Q(foo__contains=('bar', 'baz')), ~~Q(foo__contains=('bar', 'baz'))) # Test generated XML of 'Not' statement when there is only one child. Skip 't:And' between 't:Not' and 't:Or'. result = '''\ <m:Restriction xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages"> <t:Not xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types"> <t:Or> <t:IsEqualTo> <t:FieldURI FieldURI="item:Subject"/> <t:FieldURIOrConstant> <t:Constant Value="bar"/> </t:FieldURIOrConstant> </t:IsEqualTo> <t:IsEqualTo> <t:FieldURI FieldURI="item:Subject"/> <t:FieldURIOrConstant> <t:Constant Value="baz"/> </t:FieldURIOrConstant> </t:IsEqualTo> </t:Or> </t:Not> </m:Restriction>''' q = ~(Q(subject='bar') | Q(subject='baz')) self.assertEqual( xml_to_str( q.to_xml(folders=[Calendar(root=root)], version=version, applies_to=Restriction.ITEMS)), ''.join(s.lstrip() for s in result.split('\n')))