【问题标题】:Test Cases for Rego PolicesRego 策略的测试用例
【发布时间】:2020-09-17 05:42:51
【问题描述】:

对不起,我的新手问题。我已经编写了一个 rego 规则来检查 ASV 名称,现在我正在寻找相同的测试用例。我查看了示例测试用例,但没有成功编写我的策略(粘贴在下面)。想知道我如何才能获得以下规则的正面和失败案例。

asv_list = {"ASVONE","ASVXYZ"} 
check_asv := { resources[i]: Reason |
    resources:=[resource | data[j] ;
        list := {x| x:=asv_list[_]}
        not(list[data[j].ASV])
        resource:=data[j].Name]
        
    Reason := sprintf("THE ASV - %v being used is not a valid ASV", [data[j].ASV])
}

data = {resource |
    doc = input[i];
        key_ids := [k | doc[k]; startswith(k, "tag."); k != "tag.#"; endswith(k, ".key")]
    resource := {
        doc[k] : doc[replace(k, ".key", ".value")] | key_ids[_] == k
    }
}

【问题讨论】:

    标签: open-policy-agent rego


    【解决方案1】:

    如果您尝试测试check_asv,您的正面和负面测试用例将看起来相似:

    • 定义输入值
    • 定义期望值
    • 断言check_asv 在使用测试输入进行评估时等于预期值

    例如:

    test_check_asv_positive {
      expected := {"r1": "..."} 
      fake_input := [{"Name": "foo", ...}]
      check_asv == expected with input as fake_input
    }
    

    不过,在您开始为您的规则编写测试之前,我认为您应该澄清您试图表达的逻辑,因为有一些危险信号会跳出来。我试图分解我在下面的政策中看到的问题。最后,我写了一些示例测试用例。

    格式化文件以使其更易于阅读

    首先,只需对 Rego 进行格式化,使其更易于阅读。我将您的示例粘贴到文件中,添加了 package(使其成为有效的 .rego 文件),然后运行 ​​opa fmt file.rego

    asv_list = {"ASVONE", "ASVXYZ"}
    
    check_asv := {resources[i]: Reason |
            resources := [resource |
                    data[j]
                    list := {x | x := asv_list[_]}
                    not list[data[j].ASV]
                    resource := data[j].Name
            ]
    
            Reason := sprintf("THE ASV - %v being used is not a valid ASV", [data[j].ASV])
    }
    
    data = {resource |
            doc = input[i]
            key_ids := [k |
                    doc[k]
                    startswith(k, "tag.")
                    k != "tag.#"
                    endswith(k, ".key")
            ]
    
            resource := {doc[k]: doc[replace(k, ".key", ".value")] |
                    key_ids[_] == k
            }
    }
    

    这更容易阅读。

    不要命名规则data

    我建议不要将该规则命名为 data。 Rego 中的data 是一个特殊的全局变量,它引用缓存在策略引擎中的状态。来自 OPA 外部或由 OPA 内部规则生成的任何数据都可以在 data 下访问。定义一个名为data 的规则是允许的,但它会造成混淆。将 data 重命名为域中有意义的名称。例如,如果这些是虚拟机资源,您可以将它们命名为 vm_resources。在这种情况下,我没有太多工作要做,所以我将把它重命名为 input_docs,因为 doc 被用作变量名:

    check_asv := {resources[i]: Reason |
            resources := [resource |
                    input_docs[j]
                    list := {x | x := asv_list[_]}
                    not list[input_docs[j].ASV]
                    resource := input_docs[j].Name
            ]
    
            Reason := sprintf("THE ASV - %v being used is not a valid ASV", [input_docs[j].ASV])
    }
    
    input_docs = {resource |
            doc = input[i]
            key_ids := [k |
                    doc[k]
                    startswith(k, "tag.")
                    k != "tag.#"
                    endswith(k, ".key")
            ]
    
            resource := {doc[k]: doc[replace(k, ".key", ".value")] |
                    key_ids[_] == k
            }
    }
    

    简化input_docs 辅助规则(原称为data

    乍一看,这条规则做了很多工作,但实际上并非如此。第一行 (doc = input[i]) 只是遍历input 中的每个元素。规则的其余部分基于input 中的每个元素。第二个表达式key_ids := [... 从元素计算对象键数组。第三个表达式resources := {doc[k]: ...通过映射元素构造一个新对象。

    第一个表达式不能简化太多,但是,最好使用:= 而不是=,因为i 没有在其他任何地方引用,我们可以只使用_。第一个表达式变为:

    doc := input[_]
    

    第二个表达式计算一个键数组,但过滤有点多余:k 不能等于 tag.# AND 以 .key 结尾,因此可以删除 != 表达式:

    key_ids := [k |
      some k
      doc[k]
      startswith(k, "tag.")
      endswith(k, ".key")
    ]
    

    此时我们可以停下来,但值得注意的是,规则中的第二个和第三个表达式都只是在迭代 doc 对象。这里没有理由使用两个单独的表达式。为了简化这条规则,我们可以将它们组合起来:

    input_docs = {resource |        
            doc := input[_]
            resource := {v: x |
                    some k
                    v := doc[k]
                    startswith(k, "tag.")
                    endswith(k, ".key")
                    x := doc[replace(k, ".key", ".value")]
            }
    }
    

    简化check_asv 规则

    现在我们有了input_docs 规则的简化版本,它生成一组映射资源,我们可以专注于check_asv 规则。 check_asv 规则可以简化很多:

    1. 规则正文中有多个迭代器(ij),但应该只有一个。
    2. 无效资源列表的构造过于复杂。

    规则可以简化为:

    check_asv := {name: reason |
            r := input_docs[_]
            not asv_list[r.ASV]
            name := r.Name
            reason := sprintf("THE ASV - %v being used is not a valid ASV", [r.ASV])
    }
    
    • 无需重复input_docs 两次(实际上这是不正确的。)
    • 无需嵌套理解。
    • 无需从asv_list 构造集合,它已经是集合了。

    把它们放在一起

    此时的策略如下所示:

    asv_list = {"ASVONE", "ASVXYZ"}
    
    check_asv := {name: reason |
            r := input_docs[_]
            not asv_list[r.ASV]
            name := r.Name
            reason := sprintf("THE ASV - %v being used is not a valid ASV", [r.ASV])
    }
    
    input_docs = {resource |        
            doc := input[_]
            resource := {v: x |
                    some k
                    v := doc[k]
                    startswith(k, "tag.")
                    endswith(k, ".key")
                    x := doc[replace(k, ".key", ".value")]
            }
    }
    

    为了测试这个策略,我们可以很容易地编写几个测试用例:

    test_check_asv_positive {
        exp := {
            "dog": "THE ASV - ASVBAD being used is not a valid ASV"
        }
        
        inp := [
           {
               "tag.foo.key": "Name",
               "tag.foo.value": "dog",
               "tag.bar.key": "ASV",
               "tag.bar.value": "ASVBAD"
           }
       ]
        
        check_asv == exp with input as inp
    }
    
    test_check_asv_positive_multiple_resources {
        exp := {
            "dog": "THE ASV - ASVBAD being used is not a valid ASV",
            "horse": "THE ASV - ASVBAD2 being used is not a valid ASV"
        }
        
        inp := [
           {
               "tag.foo.key": "Name",
               "tag.foo.value": "dog",
               "tag.bar.key": "ASV",
               "tag.bar.value": "ASVBAD"
           },
           {
               "tag.foo.key": "Name",
               "tag.foo.value": "horse",
               "tag.bar.key": "ASV",
               "tag.bar.value": "ASVBAD2"
           }
       ]
        
        check_asv == exp with input as inp
    }
    
    
    test_check_asv_positive_multiple_resources_mixed {
        exp := {
            "horse": "THE ASV - ASVBAD2 being used is not a valid ASV"
        }
        
        inp := [
           {
               "tag.foo.key": "Name",
               "tag.foo.value": "cat",
               "tag.bar.key": "ASV",
               "tag.bar.value": "ASVONE"
           },
           {
               "tag.foo.key": "Name",
               "tag.foo.value": "horse",
               "tag.bar.key": "ASV",
               "tag.bar.value": "ASVBAD2"
           }
       ]
        
        check_asv == exp with input as inp
    }
    
    
    test_check_asv_negative {
        exp := {}
        
        inp := [
           {
               "tag.foo.key": "Name",
               "tag.foo.value": "cat",
               "tag.bar.key": "ASV",
               "tag.bar.value": "ASVONE"
           }
       ]
        
        check_asv == exp with input as inp
    }
    

    这里是操场上完整政策的链接:https://play.openpolicyagent.org/p/6w7aC9xWYH

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-20
      • 2023-03-18
      • 1970-01-01
      相关资源
      最近更新 更多