雷哥警察的测试用例

问题描述

抱歉,我的新手问题。我已经编写了一个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
    }
}

解决方法

如果您要测试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是一个特殊的全局变量,它引用策略引擎内部缓存的状态。在data下可以访问来自OPA外部或OPA内部规则生成的任何数据。允许定义名为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,".value")] |
                key_ids[_] == k
        }
}

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

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

第一个表达式不能简化很多,但是,最好使用:=而不是=,并且由于i在其他任何地方都没有引用,我们只能使用{{1 }}。第一个表达式变为:

_

第二个表达式计算键数组,但是过滤有点多余:doc := input[_] 不能等于k并以tag.#结尾,因此.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,".value")] } } 规则

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

  1. 规则主体中的多个迭代器(check_asvi)应该只有一个。
  2. 无效资源列表的构建过于复杂。

该规则可以简化为以下内容:

j
  • 无需重复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,".value")]
        }
}

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