@@ -1867,20 +1867,23 @@ def test_multi_level_multivariate_dependency_chain(self, patch_get, patch_flags)
18671867 {
18681868 "key" : "email" ,
18691869 "type" : "person" ,
1870- 1870+ 18711871 "operator" : "exact" ,
18721872 }
18731873 ],
18741874 "rollout_percentage" : 100 ,
18751875 "variant" : "test" ,
18761876 },
1877- {"rollout_percentage" : 50 , "variant" : "control" }, # Default fallback
1877+ {
1878+ "rollout_percentage" : 50 ,
1879+ "variant" : "control" ,
1880+ }, # Default fallback
18781881 ],
18791882 "multivariate" : {
18801883 "variants" : [
18811884 {
18821885 "key" : "control" ,
1883- "name" : "Control" ,
1886+ "name" : "Control" ,
18841887 "rollout_percentage" : 50 ,
18851888 },
18861889 {"key" : "test" , "name" : "Test" , "rollout_percentage" : 50 },
@@ -1911,7 +1914,7 @@ def test_multi_level_multivariate_dependency_chain(self, patch_get, patch_flags)
19111914 "type" : "person" ,
19121915 "value" : "blue" ,
19131916 "operator" : "exact" ,
1914- }
1917+ },
19151918 ],
19161919 "rollout_percentage" : 100 ,
19171920 "variant" : "blue" ,
@@ -1930,11 +1933,11 @@ def test_multi_level_multivariate_dependency_chain(self, patch_get, patch_flags)
19301933 "type" : "person" ,
19311934 "value" : "green" ,
19321935 "operator" : "exact" ,
1933- }
1936+ },
19341937 ],
19351938 "rollout_percentage" : 100 ,
19361939 "variant" : "green" ,
1937- }
1940+ },
19381941 ],
19391942 "multivariate" : {
19401943 "variants" : [
@@ -2001,7 +2004,7 @@ def test_multi_level_multivariate_dependency_chain(self, patch_get, patch_flags)
20012004 "any-user" ,
20022005 person_properties = {
200320062004- "variant_type" : "blue"
2007+ "variant_type" : "blue" ,
20052008 },
20062009 ),
20072010 )
@@ -2013,7 +2016,7 @@ def test_multi_level_multivariate_dependency_chain(self, patch_get, patch_flags)
20132016 "any-user" ,
20142017 person_properties = {
201520182016- "variant_type" : "green"
2019+ "variant_type" : "green" ,
20172020 },
20182021 ),
20192022 )
@@ -2026,7 +2029,7 @@ def test_multi_level_multivariate_dependency_chain(self, patch_get, patch_flags)
20262029 "any-user" ,
20272030 person_properties = {
20282031 "email" :
"[email protected] " ,
# This makes leaf-flag="test", breaking dependency 2029- "variant_type" : "blue"
2032+ "variant_type" : "blue" ,
20302033 },
20312034 ),
20322035 )
@@ -2039,7 +2042,7 @@ def test_multi_level_multivariate_dependency_chain(self, patch_get, patch_flags)
20392042 "any-user" ,
20402043 person_properties = {
204120442042- "variant_type" : "blue"
2045+ "variant_type" : "blue" ,
20432046 },
20442047 ),
20452048 )
@@ -2052,7 +2055,7 @@ def test_multi_level_multivariate_dependency_chain(self, patch_get, patch_flags)
20522055 "any-user" ,
20532056 person_properties = {
205420572055- "variant_type" : "green"
2058+ "variant_type" : "green" ,
20562059 },
20572060 ),
20582061 )
@@ -2063,10 +2066,7 @@ def test_multi_level_multivariate_dependency_chain(self, patch_get, patch_flags)
20632066 client .get_feature_flag (
20642067 "dependent-flag" ,
20652068 "any-user" ,
2066- person_properties = {
2067- 2068- "variant_type" : "blue"
2069- },
2069+ person_properties = {
"email" :
"[email protected] " ,
"variant_type" :
"blue" },
20702070 ),
20712071 )
20722072
@@ -2100,6 +2100,251 @@ def test_matches_dependency_value(self):
21002100 self .assertFalse (matches_dependency_value (123 , "control" ))
21012101 self .assertFalse (matches_dependency_value ("control" , True ))
21022102
2103+ @mock .patch ("posthog.client.flags" )
2104+ @mock .patch ("posthog.client.get" )
2105+ def test_production_style_multivariate_dependency_chain (
2106+ self , patch_get , patch_flags
2107+ ):
2108+ """Test production-style multivariate dependency chain: multivariate-root-flag -> multivariate-intermediate-flag -> multivariate-leaf-flag"""
2109+ client = Client (FAKE_TEST_API_KEY , personal_api_key = FAKE_TEST_API_KEY )
2110+ client .feature_flags = [
2111+ # Leaf flag: multivariate with fruit variants
2112+ {
2113+ "id" : 451 ,
2114+ "name" : "Multivariate Leaf Flag (Base)" ,
2115+ "key" : "multivariate-leaf-flag" ,
2116+ "active" : True ,
2117+ "rollout_percentage" : 100 ,
2118+ "filters" : {
2119+ "groups" : [
2120+ {
2121+ "properties" : [
2122+ {
2123+ "key" : "email" ,
2124+ "type" : "person" ,
2125+ 2126+ "operator" : "exact" ,
2127+ }
2128+ ],
2129+ "rollout_percentage" : 100 ,
2130+ "variant" : "pineapple" ,
2131+ },
2132+ {
2133+ "properties" : [
2134+ {
2135+ "key" : "email" ,
2136+ "type" : "person" ,
2137+ 2138+ "operator" : "exact" ,
2139+ }
2140+ ],
2141+ "rollout_percentage" : 100 ,
2142+ "variant" : "mango" ,
2143+ },
2144+ {
2145+ "properties" : [
2146+ {
2147+ "key" : "email" ,
2148+ "type" : "person" ,
2149+ 2150+ "operator" : "exact" ,
2151+ }
2152+ ],
2153+ "rollout_percentage" : 100 ,
2154+ "variant" : "papaya" ,
2155+ },
2156+ {
2157+ "properties" : [
2158+ {
2159+ "key" : "email" ,
2160+ "type" : "person" ,
2161+ 2162+ "operator" : "exact" ,
2163+ }
2164+ ],
2165+ "rollout_percentage" : 100 ,
2166+ "variant" : "kiwi" ,
2167+ },
2168+ {
2169+ "properties" : [],
2170+ "rollout_percentage" : 0 , # Force default to false for unknown emails
2171+ },
2172+ ],
2173+ "multivariate" : {
2174+ "variants" : [
2175+ {"key" : "pineapple" , "rollout_percentage" : 25 },
2176+ {"key" : "mango" , "rollout_percentage" : 25 },
2177+ {"key" : "papaya" , "rollout_percentage" : 25 },
2178+ {"key" : "kiwi" , "rollout_percentage" : 25 },
2179+ ]
2180+ },
2181+ },
2182+ },
2183+ # Intermediate flag: multivariate with color variants, depends on fruit
2184+ {
2185+ "id" : 467 ,
2186+ "name" : "Multivariate Intermediate Flag (Depends on fruit)" ,
2187+ "key" : "multivariate-intermediate-flag" ,
2188+ "active" : True ,
2189+ "rollout_percentage" : 100 ,
2190+ "filters" : {
2191+ "groups" : [
2192+ {
2193+ "properties" : [
2194+ {
2195+ "key" : "multivariate-leaf-flag" ,
2196+ "type" : "flag" ,
2197+ "value" : "pineapple" ,
2198+ "operator" : "flag_evaluates_to" ,
2199+ "dependency_chain" : ["multivariate-leaf-flag" ],
2200+ }
2201+ ],
2202+ "rollout_percentage" : 100 ,
2203+ "variant" : "blue" ,
2204+ },
2205+ {
2206+ "properties" : [
2207+ {
2208+ "key" : "multivariate-leaf-flag" ,
2209+ "type" : "flag" ,
2210+ "value" : "mango" ,
2211+ "operator" : "flag_evaluates_to" ,
2212+ "dependency_chain" : ["multivariate-leaf-flag" ],
2213+ }
2214+ ],
2215+ "rollout_percentage" : 100 ,
2216+ "variant" : "red" ,
2217+ },
2218+ ],
2219+ "multivariate" : {
2220+ "variants" : [
2221+ {"key" : "blue" , "rollout_percentage" : 100 },
2222+ {"key" : "red" , "rollout_percentage" : 0 },
2223+ {"key" : "green" , "rollout_percentage" : 0 },
2224+ {"key" : "black" , "rollout_percentage" : 0 },
2225+ ]
2226+ },
2227+ },
2228+ },
2229+ # Root flag: multivariate with show variants, depends on color
2230+ {
2231+ "id" : 468 ,
2232+ "name" : "Multivariate Root Flag (Depends on color)" ,
2233+ "key" : "multivariate-root-flag" ,
2234+ "active" : True ,
2235+ "rollout_percentage" : 100 ,
2236+ "filters" : {
2237+ "groups" : [
2238+ {
2239+ "properties" : [
2240+ {
2241+ "key" : "multivariate-intermediate-flag" ,
2242+ "type" : "flag" ,
2243+ "value" : "blue" ,
2244+ "operator" : "flag_evaluates_to" ,
2245+ "dependency_chain" : [
2246+ "multivariate-leaf-flag" ,
2247+ "multivariate-intermediate-flag" ,
2248+ ],
2249+ }
2250+ ],
2251+ "rollout_percentage" : 100 ,
2252+ "variant" : "breaking-bad" ,
2253+ },
2254+ {
2255+ "properties" : [
2256+ {
2257+ "key" : "multivariate-intermediate-flag" ,
2258+ "type" : "flag" ,
2259+ "value" : "red" ,
2260+ "operator" : "flag_evaluates_to" ,
2261+ "dependency_chain" : [
2262+ "multivariate-leaf-flag" ,
2263+ "multivariate-intermediate-flag" ,
2264+ ],
2265+ }
2266+ ],
2267+ "rollout_percentage" : 100 ,
2268+ "variant" : "the-wire" ,
2269+ },
2270+ ],
2271+ "multivariate" : {
2272+ "variants" : [
2273+ {"key" : "breaking-bad" , "rollout_percentage" : 100 },
2274+ {"key" : "the-wire" , "rollout_percentage" : 0 },
2275+ {"key" : "game-of-thrones" , "rollout_percentage" : 0 },
2276+ {"key" : "the-expanse" , "rollout_percentage" : 0 },
2277+ ]
2278+ },
2279+ },
2280+ },
2281+ ]
2282+
2283+ # Test successful pineapple -> blue -> breaking-bad chain
2284+ leaf_result = client .get_feature_flag (
2285+ "multivariate-leaf-flag" ,
2286+ "test-user" ,
2287+ person_properties = {
"email" :
"[email protected] " },
2288+ )
2289+ intermediate_result = client .get_feature_flag (
2290+ "multivariate-intermediate-flag" ,
2291+ "test-user" ,
2292+ person_properties = {
"email" :
"[email protected] " },
2293+ )
2294+ root_result = client .get_feature_flag (
2295+ "multivariate-root-flag" ,
2296+ "test-user" ,
2297+ person_properties = {
"email" :
"[email protected] " },
2298+ )
2299+
2300+ self .assertEqual (leaf_result , "pineapple" )
2301+ self .assertEqual (intermediate_result , "blue" )
2302+ self .assertEqual (root_result , "breaking-bad" )
2303+
2304+ # Test successful mango -> red -> the-wire chain
2305+ mango_leaf_result = client .get_feature_flag (
2306+ "multivariate-leaf-flag" ,
2307+ "test-user" ,
2308+ person_properties = {
"email" :
"[email protected] " },
2309+ )
2310+ mango_intermediate_result = client .get_feature_flag (
2311+ "multivariate-intermediate-flag" ,
2312+ "test-user" ,
2313+ person_properties = {
"email" :
"[email protected] " },
2314+ )
2315+ mango_root_result = client .get_feature_flag (
2316+ "multivariate-root-flag" ,
2317+ "test-user" ,
2318+ person_properties = {
"email" :
"[email protected] " },
2319+ )
2320+
2321+ self .assertEqual (mango_leaf_result , "mango" )
2322+ self .assertEqual (mango_intermediate_result , "red" )
2323+ self .assertEqual (mango_root_result , "the-wire" )
2324+
2325+ # Test broken chain - user without matching email gets default/false results
2326+ unknown_leaf_result = client .get_feature_flag (
2327+ "multivariate-leaf-flag" ,
2328+ "test-user" ,
2329+ person_properties = {
"email" :
"[email protected] " },
2330+ )
2331+ unknown_intermediate_result = client .get_feature_flag (
2332+ "multivariate-intermediate-flag" ,
2333+ "test-user" ,
2334+ person_properties = {
"email" :
"[email protected] " },
2335+ )
2336+ unknown_root_result = client .get_feature_flag (
2337+ "multivariate-root-flag" ,
2338+ "test-user" ,
2339+ person_properties = {
"email" :
"[email protected] " },
2340+ )
2341+
2342+ self .assertEqual (
2343+ unknown_leaf_result , False
2344+ ) # No matching email -> null variant -> false
2345+ self .assertEqual (unknown_intermediate_result , False ) # Dependency not satisfied
2346+ self .assertEqual (unknown_root_result , False ) # Chain broken
2347+
21032348 @mock .patch ("posthog.client.Poller" )
21042349 @mock .patch ("posthog.client.get" )
21052350 def test_load_feature_flags (self , patch_get , patch_poll ):
0 commit comments