packages feed

launchdarkly-server-sdk-4.3.0: test/Spec/Evaluate.hs

module Spec.Evaluate (allTests) where

import Data.Aeson (Value (..))
import Data.Function ((&))
import Data.Generics.Product (getField)
import qualified Data.HashSet as HS
import Test.HUnit

import LaunchDarkly.Server.Client
import LaunchDarkly.Server.Config
import LaunchDarkly.Server.Context
import LaunchDarkly.Server.Evaluate
import LaunchDarkly.Server.Features
import LaunchDarkly.Server.Operators
import LaunchDarkly.Server.Store.Internal

import LaunchDarkly.Server.Reference (makeLiteral, makeReference)
import Util.Features

makeEmptyStore :: IO (StoreHandle IO)
makeEmptyStore = do
    handle <- makeStoreIO Nothing 0
    initializeStore handle mempty mempty
    pure handle

testFlagReturnsOffVariationIfFlagIsOff :: Test
testFlagReturnsOffVariationIfFlagIsOff = TestCase $ do
    store <- makeEmptyStore
    x <- evaluateDetail flag context HS.empty store
    assertEqual "test" expected x
  where
    expected =
        ( EvaluationDetail
            { value = String "off"
            , variationIndex = pure 1
            , reason = EvaluationReasonOff
            }
        , []
        , Nothing
        )

    context = makeContext "x" "user"

    flag =
        Flag
            { key = "feature"
            , version = 1
            , on = False
            , trackEvents = False
            , trackEventsFallthrough = False
            , deleted = False
            , prerequisites = []
            , salt = ""
            , targets = []
            , contextTargets = []
            , rules = []
            , fallthrough =
                VariationOrRollout
                    { variation = Just 0
                    , rollout = Nothing
                    }
            , offVariation = Just 1
            , variations = [String "fall", String "off", String "on"]
            , debugEventsUntilDate = Nothing
            , clientSideAvailability = ClientSideAvailability {usingEnvironmentId = True, usingMobileKey = False, explicit = True}
            }

testFlagReturnsFallthroughIfFlagIsOnAndThereAreNoRules :: Test
testFlagReturnsFallthroughIfFlagIsOnAndThereAreNoRules = TestCase $ do
    store <- makeEmptyStore
    x <- evaluateDetail flag context HS.empty store
    assertEqual "test" expected x
  where
    expected =
        ( EvaluationDetail
            { value = String "fall"
            , variationIndex = pure 0
            , reason =
                EvaluationReasonFallthrough
                    { inExperiment = False
                    }
            }
        , []
        , Nothing
        )

    context = makeContext "x" "user"

    flag =
        Flag
            { key = "feature"
            , version = 1
            , on = True
            , trackEvents = False
            , trackEventsFallthrough = False
            , deleted = False
            , prerequisites = []
            , salt = ""
            , targets = []
            , contextTargets = []
            , rules = []
            , fallthrough =
                VariationOrRollout
                    { variation = Just 0
                    , rollout = Nothing
                    }
            , offVariation = Just 1
            , variations = [String "fall", String "off", String "on"]
            , debugEventsUntilDate = Nothing
            , clientSideAvailability = ClientSideAvailability {usingEnvironmentId = True, usingMobileKey = False, explicit = True}
            }

testFlagReturnsErrorIfFallthroughHasTooHighVariation :: Test
testFlagReturnsErrorIfFallthroughHasTooHighVariation = TestCase $ do
    client <- makeTestClient
    insertFlag (getField @"store" client) flag >>= (pure () @=?)
    stringVariationDetail client "a" (makeContext "b" "user") "default" >>= (expected @=?)
  where
    flag =
        (makeTestFlag "a" 52)
            { on = True
            , offVariation = Nothing
            , fallthrough =
                VariationOrRollout
                    { variation = Just 999
                    , rollout = Nothing
                    }
            , variations =
                [ String "abc"
                , String "123"
                , String "456"
                ]
            }
    expected =
        EvaluationDetail
            { value = "default"
            , variationIndex = Nothing
            , reason = EvaluationReasonError EvalErrorKindMalformedFlag
            }

testFlagReturnsErrorIfFallthroughHasNeitherVariationNorRollout :: Test
testFlagReturnsErrorIfFallthroughHasNeitherVariationNorRollout = TestCase $ do
    client <- makeTestClient
    insertFlag (getField @"store" client) flag >>= (pure () @=?)
    stringVariationDetail client "a" (makeContext "b" "user") "default" >>= (expected @=?)
  where
    flag =
        (makeTestFlag "a" 52)
            { on = True
            , offVariation = Nothing
            , fallthrough =
                VariationOrRollout
                    { variation = Nothing
                    , rollout = Nothing
                    }
            , variations = [String "abc"]
            }
    expected =
        EvaluationDetail
            { value = "default"
            , variationIndex = Nothing
            , reason = EvaluationReasonError EvalErrorKindMalformedFlag
            }

testFlagReturnsErrorIfFallthroughHasEmptyRolloutVariationList :: Test
testFlagReturnsErrorIfFallthroughHasEmptyRolloutVariationList = TestCase $ do
    client <- makeTestClient
    insertFlag (getField @"store" client) flag >>= (pure () @=?)
    stringVariationDetail client "a" (makeContext "b" "user") "default" >>= (expected @=?)
  where
    flag =
        (makeTestFlag "a" 52)
            { on = True
            , offVariation = Nothing
            , fallthrough =
                VariationOrRollout
                    { variation = Nothing
                    , rollout =
                        pure
                            Rollout
                                { variations = []
                                , seed = Nothing
                                , kind = RolloutKindRollout
                                , bucketBy = pure "key"
                                , contextKind = Just "user"
                                }
                    }
            , variations = [String "abc"]
            }
    expected =
        EvaluationDetail
            { value = "default"
            , variationIndex = Nothing
            , reason = EvaluationReasonError EvalErrorKindMalformedFlag
            }

testFlagReturnsErrorIfThereIsAPrerequisiteCycle :: Test
testFlagReturnsErrorIfThereIsAPrerequisiteCycle = TestCase $ do
    client <- makeTestClient
    insertFlag (getField @"store" client) flag0 >>= (pure () @=?)
    insertFlag (getField @"store" client) flag1 >>= (pure () @=?)
    stringVariationDetail client "feature0" (makeContext "b" "user") "default" >>= (expected @=?)
  where
    flag0 =
        (makeTestFlag "feature0" 52)
            { on = True
            , offVariation = pure 1
            , fallthrough =
                VariationOrRollout
                    { variation = pure 0
                    , rollout = Nothing
                    }
            , variations = [String "fall", String "off", String "on"]
            , prerequisites =
                [ Prerequisite
                    { key = "feature1"
                    , variation = 1
                    }
                ]
            }
    flag1 =
        (makeTestFlag "feature1" 52)
            { on = True
            , offVariation = pure 1
            , fallthrough =
                VariationOrRollout
                    { variation = pure 0
                    , rollout = Nothing
                    }
            , variations = [String "nogo", String "go"]
            , prerequisites =
                [ Prerequisite
                    { key = "feature0"
                    , variation = 1
                    }
                ]
            }
    expected =
        EvaluationDetail
            { value = "default"
            , variationIndex = Nothing
            , reason = EvaluationReasonError EvalErrorKindMalformedFlag
            }

testFlagReturnsOffVariationIfPrerequisiteIsNotFound :: Test
testFlagReturnsOffVariationIfPrerequisiteIsNotFound = TestCase $ do
    client <- makeTestClient
    insertFlag (getField @"store" client) flag >>= (pure () @=?)
    stringVariationDetail client "a" (makeContext "b" "user") "default" >>= (expected @=?)
  where
    flag =
        (makeTestFlag "a" 52)
            { on = True
            , offVariation = pure 1
            , fallthrough =
                VariationOrRollout
                    { variation = pure 0
                    , rollout = Nothing
                    }
            , variations = [String "fall", String "off", String "on"]
            , prerequisites =
                [ Prerequisite
                    { key = "feature1"
                    , variation = 1
                    }
                ]
            }
    expected =
        EvaluationDetail
            { value = "off"
            , variationIndex = pure 1
            , reason = EvaluationReasonPrerequisiteFailed "feature1"
            }

testFlagReturnsOffVariationIfPrerequisiteIsOff :: Test
testFlagReturnsOffVariationIfPrerequisiteIsOff = TestCase $ do
    client <- makeTestClient
    insertFlag (getField @"store" client) flag0 >>= (pure () @=?)
    insertFlag (getField @"store" client) flag1 >>= (pure () @=?)
    stringVariationDetail client "feature0" (makeContext "b" "user") "default" >>= (expected @=?)
  where
    flag0 =
        (makeTestFlag "feature0" 52)
            { on = True
            , offVariation = pure 1
            , fallthrough =
                VariationOrRollout
                    { variation = pure 0
                    , rollout = Nothing
                    }
            , variations = [String "fall", String "off", String "on"]
            , prerequisites =
                [ Prerequisite
                    { key = "feature1"
                    , variation = 1
                    }
                ]
            }
    flag1 =
        (makeTestFlag "feature1" 52)
            { on = False
            , offVariation = pure 1
            , fallthrough =
                VariationOrRollout
                    { variation = pure 0
                    , rollout = Nothing
                    }
            , variations = [String "nogo", String "go"]
            }
    expected =
        EvaluationDetail
            { value = "off"
            , variationIndex = pure 1
            , reason = EvaluationReasonPrerequisiteFailed "feature1"
            }

testFlagReturnsOffVariationIfPrerequisiteIsNotMet :: Test
testFlagReturnsOffVariationIfPrerequisiteIsNotMet = TestCase $ do
    client <- makeTestClient
    insertFlag (getField @"store" client) flag0 >>= (pure () @=?)
    insertFlag (getField @"store" client) flag1 >>= (pure () @=?)
    stringVariationDetail client "feature0" (makeContext "b" "user") "default" >>= (expected @=?)
  where
    flag0 =
        (makeTestFlag "feature0" 52)
            { on = True
            , offVariation = pure 1
            , fallthrough =
                VariationOrRollout
                    { variation = pure 0
                    , rollout = Nothing
                    }
            , variations = [String "fall", String "off", String "on"]
            , prerequisites =
                [ Prerequisite
                    { key = "feature1"
                    , variation = 1
                    }
                ]
            }
    flag1 =
        (makeTestFlag "feature1" 52)
            { on = True
            , offVariation = pure 1
            , fallthrough =
                VariationOrRollout
                    { variation = pure 0
                    , rollout = Nothing
                    }
            , variations = [String "nogo", String "go"]
            }
    expected =
        EvaluationDetail
            { value = "off"
            , variationIndex = pure 1
            , reason = EvaluationReasonPrerequisiteFailed "feature1"
            }

testFlagReturnsFallthroughVariationIfPrerequisiteIsMetAndThereAreNoRules :: Test
testFlagReturnsFallthroughVariationIfPrerequisiteIsMetAndThereAreNoRules = TestCase $ do
    client <- makeTestClient
    insertFlag (getField @"store" client) flag0 >>= (pure () @=?)
    insertFlag (getField @"store" client) flag1 >>= (pure () @=?)
    stringVariationDetail client "feature0" (makeContext "b" "user") "default" >>= (expected @=?)
  where
    flag0 =
        (makeTestFlag "feature0" 52)
            { on = True
            , offVariation = pure 1
            , fallthrough =
                VariationOrRollout
                    { variation = pure 0
                    , rollout = Nothing
                    }
            , variations = [String "fall", String "off", String "on"]
            , prerequisites =
                [ Prerequisite
                    { key = "feature1"
                    , variation = 1
                    }
                ]
            }
    flag1 =
        (makeTestFlag "feature1" 52)
            { on = True
            , offVariation = pure 1
            , fallthrough =
                VariationOrRollout
                    { variation = pure 1
                    , rollout = Nothing
                    }
            , variations = [String "nogo", String "go"]
            }
    expected =
        EvaluationDetail
            { value = "fall"
            , variationIndex = pure 0
            , reason =
                EvaluationReasonFallthrough
                    { inExperiment = False
                    }
            }

testFlagCanTargetUserKeys :: Test
testFlagCanTargetUserKeys = TestCase $ do
    client <- makeTestClient
    insertFlag (getField @"store" client) flag >>= (pure () @=?)
    boolVariationDetail client "feature0" (makeContext "user-key" "user") False >>= (expected @=?)
  where
    flag :: Flag =
        (makeTestFlag "feature0" 52)
            { on = True
            , targets = [Target {values = HS.singleton "user-key", variation = 1, contextKind = "user"}]
            , variations = [Bool False, Bool True]
            , fallthrough =
                VariationOrRollout
                    { variation = Just 0
                    , rollout = Nothing
                    }
            }
    expected :: EvaluationDetail Bool =
        EvaluationDetail
            { value = True
            , variationIndex = pure 1
            , reason = EvaluationReasonTargetMatch
            }

testFlagCanTargetContextKeys :: Test
testFlagCanTargetContextKeys = TestCase $ do
    client <- makeTestClient
    insertFlag (getField @"store" client) flag >>= (pure () @=?)
    boolVariationDetail client "feature0" (makeContext "match-key" "user") False >>= (fallthrough @=?)
    boolVariationDetail client "feature0" (makeContext "match-key" "org") False >>= (match @=?)
  where
    flag :: Flag =
        (makeTestFlag "feature0" 52)
            { on = True
            , contextTargets = [Target {values = HS.singleton "match-key", variation = 1, contextKind = "org"}]
            , variations = [Bool False, Bool True]
            , fallthrough =
                VariationOrRollout
                    { variation = Just 0
                    , rollout = Nothing
                    }
            }
    match :: EvaluationDetail Bool =
        EvaluationDetail
            { value = True
            , variationIndex = pure 1
            , reason = EvaluationReasonTargetMatch
            }
    fallthrough :: EvaluationDetail Bool =
        EvaluationDetail
            { value = False
            , variationIndex = pure 0
            , reason = EvaluationReasonFallthrough {inExperiment = False}
            }

testFlagCanTargetContextFallsbackToUserTargets :: Test
testFlagCanTargetContextFallsbackToUserTargets = TestCase $ do
    client <- makeTestClient
    insertFlag (getField @"store" client) flag >>= (pure () @=?)
    boolVariationDetail client "feature0" (makeContext "match-key" "user") False >>= (match @=?)
  where
    flag :: Flag =
        (makeTestFlag "feature0" 52)
            { on = True
            , targets = [Target {values = HS.singleton "match-key", variation = 1, contextKind = "user"}]
            , contextTargets = [Target {values = HS.empty, variation = 1, contextKind = "user"}]
            , variations = [Bool False, Bool True]
            , fallthrough =
                VariationOrRollout
                    { variation = Just 0
                    , rollout = Nothing
                    }
            }
    match :: EvaluationDetail Bool =
        EvaluationDetail
            { value = True
            , variationIndex = pure 1
            , reason = EvaluationReasonTargetMatch
            }

testFlagChecksTargetsBeforeRules :: Test
testFlagChecksTargetsBeforeRules = TestCase $ do
    client <- makeTestClient
    insertFlag (getField @"store" client) flag >>= (pure () @=?)
    boolVariationDetail client "feature0" (makeContext "match-key" "user") False >>= (match @=?)
  where
    flag :: Flag =
        (makeTestFlag "feature0" 52)
            { on = True
            , targets = [Target {values = HS.singleton "match-key", variation = 0, contextKind = "user"}]
            , rules =
                [ Rule
                    { clauses =
                        [ Clause
                            { attribute = makeLiteral "kind"
                            , contextKind = "user"
                            , op = OpIn
                            , values = [String "user"]
                            , negate = False
                            }
                        ]
                    , variationOrRollout =
                        VariationOrRollout
                            { variation = Just 1
                            , rollout = Nothing
                            }
                    , id = "clause"
                    , trackEvents = False
                    }
                ]
            , variations = [Bool False, Bool True]
            , fallthrough =
                VariationOrRollout
                    { variation = Just 1
                    , rollout = Nothing
                    }
            }
    match :: EvaluationDetail Bool =
        EvaluationDetail
            { value = False
            , variationIndex = pure 0
            , reason = EvaluationReasonTargetMatch
            }

testClauseCanMatchOnKind :: Test
testClauseCanMatchOnKind = TestCase $ do
    store <- makeStoreIO Nothing 0
    orgDetail <- evaluateDetail flag orgContext HS.empty store
    userDetail <- evaluateDetail flag userContext HS.empty store
    multiDetail <- evaluateDetail flag multiContext HS.empty store

    assertEqual "test" expectedMatch orgDetail
    assertEqual "test" expectedFailure userDetail
    assertEqual "test" expectedMatch multiDetail
  where
    expectedMatch =
        ( EvaluationDetail
            { value = Bool True
            , variationIndex = pure 1
            , reason =
                EvaluationReasonRuleMatch
                    { ruleIndex = 0
                    , ruleId = "clause"
                    , inExperiment = False
                    }
            }
        , []
        , Nothing
        )

    expectedFailure =
        ( EvaluationDetail
            { value = Bool False
            , variationIndex = pure 0
            , reason = EvaluationReasonFallthrough {inExperiment = False}
            }
        , []
        , Nothing
        )

    orgContext = makeContext "x" "org"
    userContext = makeContext "x" "user"
    multiContext = makeMultiContext [orgContext, userContext]

    flag =
        Flag
            { key = "feature"
            , version = 1
            , on = True
            , trackEvents = False
            , trackEventsFallthrough = False
            , deleted = False
            , prerequisites = []
            , salt = ""
            , targets = []
            , contextTargets = []
            , rules =
                [ Rule
                    { clauses =
                        [ Clause
                            { attribute = makeLiteral "kind"
                            , contextKind = "user"
                            , op = OpIn
                            , values = [String "org"]
                            , negate = False
                            }
                        ]
                    , variationOrRollout =
                        VariationOrRollout
                            { variation = Just 1
                            , rollout = Nothing
                            }
                    , id = "clause"
                    , trackEvents = False
                    }
                ]
            , fallthrough =
                VariationOrRollout
                    { variation = Just 0
                    , rollout = Nothing
                    }
            , offVariation = Just 0
            , variations = [Bool False, Bool True]
            , debugEventsUntilDate = Nothing
            , clientSideAvailability = ClientSideAvailability {usingEnvironmentId = True, usingMobileKey = False, explicit = True}
            }

testClauseCanMatchCustomAttribute :: Test
testClauseCanMatchCustomAttribute = TestCase $ do
    store <- makeStoreIO Nothing 0
    userDetail <- evaluateDetail flag userContext HS.empty store
    orgDetail <- evaluateDetail flag orgContext HS.empty store
    assertEqual "test" expectedMatch userDetail
    assertEqual "test" expectedFailure orgDetail
  where
    expectedMatch =
        ( EvaluationDetail
            { value = Bool True
            , variationIndex = pure 1
            , reason =
                EvaluationReasonRuleMatch
                    { ruleIndex = 0
                    , ruleId = "clause"
                    , inExperiment = False
                    }
            }
        , []
        , Nothing
        )

    expectedFailure =
        ( EvaluationDetail
            { value = Bool False
            , variationIndex = pure 0
            , reason = EvaluationReasonFallthrough {inExperiment = False}
            }
        , []
        , Nothing
        )

    userContext = makeContext "x" "user" & withAttribute "legs" (Number 4)
    orgContext = makeContext "x" "org" & withAttribute "legs" (Number 4)

    flag =
        Flag
            { key = "feature"
            , version = 1
            , on = True
            , trackEvents = False
            , trackEventsFallthrough = False
            , deleted = False
            , prerequisites = []
            , salt = ""
            , targets = []
            , contextTargets = []
            , rules =
                [ Rule
                    { clauses =
                        [ Clause
                            { attribute = makeLiteral "legs"
                            , contextKind = "user"
                            , op = OpIn
                            , values = [Number 4]
                            , negate = False
                            }
                        ]
                    , variationOrRollout =
                        VariationOrRollout
                            { variation = Just 1
                            , rollout = Nothing
                            }
                    , id = "clause"
                    , trackEvents = False
                    }
                ]
            , fallthrough =
                VariationOrRollout
                    { variation = Just 0
                    , rollout = Nothing
                    }
            , offVariation = Just 0
            , variations = [Bool False, Bool True]
            , debugEventsUntilDate = Nothing
            , clientSideAvailability = ClientSideAvailability {usingEnvironmentId = True, usingMobileKey = False, explicit = True}
            }

testClauseCanMatchCustomAttributeReference :: Test
testClauseCanMatchCustomAttributeReference = TestCase $ do
    store <- makeStoreIO Nothing 0
    userDetail <- evaluateDetail flag userContext HS.empty store
    orgDetail <- evaluateDetail flag orgContext HS.empty store
    assertEqual "test" expectedMatch userDetail
  where
    -- assertEqual "test" expectedFailure orgDetail

    expectedMatch =
        ( EvaluationDetail
            { value = Bool True
            , variationIndex = pure 1
            , reason =
                EvaluationReasonRuleMatch
                    { ruleIndex = 0
                    , ruleId = "clause"
                    , inExperiment = False
                    }
            }
        , []
        , Nothing
        )

    expectedFailure =
        ( EvaluationDetail
            { value = Bool False
            , variationIndex = pure 0
            , reason = EvaluationReasonFallthrough {inExperiment = False}
            }
        , []
        , Nothing
        )

    userContext = makeContext "x" "user" & withAttribute "attr~1a" (String "right")
    orgContext = makeContext "x" "org" & withAttribute "attr~1a" (String "right")

    flag =
        Flag
            { key = "feature"
            , version = 1
            , on = True
            , trackEvents = False
            , trackEventsFallthrough = False
            , deleted = False
            , prerequisites = []
            , salt = ""
            , targets = []
            , contextTargets = []
            , rules =
                [ Rule
                    { clauses =
                        [ Clause
                            { attribute = makeReference "/attr~01a"
                            , contextKind = "user"
                            , op = OpIn
                            , values = [String "right"]
                            , negate = False
                            }
                        ]
                    , variationOrRollout =
                        VariationOrRollout
                            { variation = Just 1
                            , rollout = Nothing
                            }
                    , id = "clause"
                    , trackEvents = False
                    }
                ]
            , fallthrough =
                VariationOrRollout
                    { variation = Just 0
                    , rollout = Nothing
                    }
            , offVariation = Just 0
            , variations = [Bool False, Bool True]
            , debugEventsUntilDate = Nothing
            , clientSideAvailability = ClientSideAvailability {usingEnvironmentId = True, usingMobileKey = False, explicit = True}
            }

makeTestClient :: IO Client
makeTestClient = do
    client <- makeClient $ (makeConfig "") & configSetOffline True
    initializeStore (getField @"store" client) mempty mempty
    pure client

testEvaluatingUnknownFlagReturnsDefault :: Test
testEvaluatingUnknownFlagReturnsDefault = TestCase $ do
    client <- makeTestClient
    boolVariation client "a" (makeContext "b" "user") False >>= (False @=?)

testEvaluatingUnknownFlagReturnsDefaultWithDetail :: Test
testEvaluatingUnknownFlagReturnsDefaultWithDetail = TestCase $ do
    client <- makeTestClient
    boolVariationDetail client "a" (makeContext "b" "user") False >>= (expected @=?)
  where
    expected =
        EvaluationDetail
            { value = False
            , variationIndex = Nothing
            , reason = EvaluationReasonError EvalErrorFlagNotFound
            }

testDefaultIsReturnedIfFlagEvaluatesToNil :: Test
testDefaultIsReturnedIfFlagEvaluatesToNil = TestCase $ do
    client <- makeTestClient
    insertFlag (getField @"store" client) flag >>= (pure () @=?)
    boolVariation client "a" (makeContext "b" "user") False >>= (False @=?)
  where
    flag =
        (makeTestFlag "a" 52)
            { on = False
            , offVariation = Nothing
            }

testDefaultIsReturnedIfFlagEvaluatesToNilWithDetail :: Test
testDefaultIsReturnedIfFlagEvaluatesToNilWithDetail = TestCase $ do
    client <- makeTestClient
    insertFlag (getField @"store" client) flag >>= (pure () @=?)
    boolVariationDetail client "a" (makeContext "b" "user") False >>= (expected @=?)
  where
    flag =
        (makeTestFlag "a" 52)
            { on = False
            , offVariation = Nothing
            }
    expected =
        EvaluationDetail
            { value = False
            , variationIndex = Nothing
            , reason = EvaluationReasonOff
            }

allTests :: Test
allTests =
    TestList
        [ testFlagReturnsOffVariationIfFlagIsOff
        , testFlagReturnsFallthroughIfFlagIsOnAndThereAreNoRules
        , testFlagReturnsErrorIfFallthroughHasTooHighVariation
        , testFlagReturnsErrorIfFallthroughHasNeitherVariationNorRollout
        , testFlagReturnsErrorIfFallthroughHasEmptyRolloutVariationList
        , testFlagReturnsErrorIfThereIsAPrerequisiteCycle
        , testFlagReturnsOffVariationIfPrerequisiteIsNotFound
        , testFlagReturnsOffVariationIfPrerequisiteIsOff
        , testFlagReturnsOffVariationIfPrerequisiteIsNotMet
        , testFlagReturnsFallthroughVariationIfPrerequisiteIsMetAndThereAreNoRules
        , testFlagCanTargetUserKeys
        , testFlagCanTargetContextKeys
        , testFlagCanTargetContextFallsbackToUserTargets
        , testFlagChecksTargetsBeforeRules
        , testClauseCanMatchCustomAttribute
        , testClauseCanMatchCustomAttributeReference
        , testClauseCanMatchOnKind
        , testEvaluatingUnknownFlagReturnsDefault
        , testEvaluatingUnknownFlagReturnsDefaultWithDetail
        , testDefaultIsReturnedIfFlagEvaluatesToNil
        , testDefaultIsReturnedIfFlagEvaluatesToNilWithDetail
        ]