@@ -294,6 +294,84 @@ def test_translate_segment__rule_with_no_conditions_or_nested_rules__matches_all
294294 assert translate_segment (seg , _ctx ()) == "(TRUE)"
295295
296296
297+ def test_translate_segment__rule_with_conditions_and_nested_rule__ands_the_two_groups () -> None :
298+ # Given a rule carrying both a condition and a (translatable) nested rule
299+ seg : SegmentContext = {
300+ "key" : "11" ,
301+ "name" : "s" ,
302+ "rules" : [
303+ {
304+ "type" : "ALL" ,
305+ "conditions" : [{"operator" : "EQUAL" , "property" : "plan" , "value" : "growth" }],
306+ "rules" : [
307+ {
308+ "type" : "ALL" ,
309+ "conditions" : [{"operator" : "EQUAL" , "property" : "country" , "value" : "GB" }],
310+ }
311+ ],
312+ }
313+ ],
314+ }
315+
316+ # When translated
317+ sql = translate_segment (seg , _ctx ())
318+
319+ # Then the condition group and the nested-rule group are AND-ed together
320+ # (mirroring the engine: matches_conditions AND matches_rules)
321+ assert sql == (
322+ "(((("
323+ "if(i.traits.`plan` IS NULL, NULL, toString(i.traits.`plan`)) IS NOT NULL"
324+ " AND ((toString(i.traits.`plan`) = 'growth') OR (i.traits.`plan`.:Bool = true))"
325+ "))) AND (((("
326+ "if(i.traits.`country` IS NULL, NULL, toString(i.traits.`country`)) IS NOT NULL"
327+ " AND ((toString(i.traits.`country`) = 'GB') OR (i.traits.`country`.:Bool = true))"
328+ ")))))"
329+ )
330+
331+
332+ def test_translate_segment__any_rule_with_conditions_and_nested_rule__ors_within_ands_across () -> (
333+ None
334+ ):
335+ # Given an ANY rule with two conditions and a nested rule
336+ seg : SegmentContext = {
337+ "key" : "12" ,
338+ "name" : "s" ,
339+ "rules" : [
340+ {
341+ "type" : "ANY" ,
342+ "conditions" : [
343+ {"operator" : "EQUAL" , "property" : "plan" , "value" : "growth" },
344+ {"operator" : "EQUAL" , "property" : "plan" , "value" : "scale" },
345+ ],
346+ "rules" : [
347+ {
348+ "type" : "ALL" ,
349+ "conditions" : [{"operator" : "EQUAL" , "property" : "country" , "value" : "GB" }],
350+ }
351+ ],
352+ }
353+ ],
354+ }
355+
356+ # When
357+ sql = translate_segment (seg , _ctx ())
358+
359+ # Then
360+ # interpreted as `any(conditions) AND any(rules)`
361+ assert sql == (
362+ "(((("
363+ "if(i.traits.`plan` IS NULL, NULL, toString(i.traits.`plan`)) IS NOT NULL"
364+ " AND ((toString(i.traits.`plan`) = 'growth') OR (i.traits.`plan`.:Bool = true))"
365+ ")) OR (("
366+ "if(i.traits.`plan` IS NULL, NULL, toString(i.traits.`plan`)) IS NOT NULL"
367+ " AND ((toString(i.traits.`plan`) = 'scale') OR (i.traits.`plan`.:Bool = true))"
368+ "))) AND (((("
369+ "if(i.traits.`country` IS NULL, NULL, toString(i.traits.`country`)) IS NOT NULL"
370+ " AND ((toString(i.traits.`country`) = 'GB') OR (i.traits.`country`.:Bool = true))"
371+ ")))))"
372+ )
373+
374+
297375def test_translate_segment__trait_key_with_hyphens__quotes_subcolumn_path () -> None :
298376 # Given a trait key with a hyphen (illegal as an unquoted SQL identifier)
299377 seg : SegmentContext = {
0 commit comments