问题描述
抱歉,我的新手问题。我已经编写了一个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
规则可以简化很多:
- 规则主体中的多个迭代器(
check_asv
和i
)应该只有一个。 - 无效资源列表的构建过于复杂。
该规则可以简化为以下内容:
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