Search code examples
testingopen-policy-agentrego

Rego testing: how to test "not deny"?


I'm exploring testing my Rego policies using opa test starting with this trivial rule:

deny["Must be allowed"] {
  input.allowed == "no"
}

I'm able to successfully test this against a case where this is denied:

test_denied_example {
  deny with input as {"allowed":"no"}
}

However, when I try testing it against a case where it should be allowed like so:

test_allowed_example {
  not deny with input as {"allowed":"yes"}
}

I get an error:

data.example.test_allowed_example: FAIL (330.534µs)

  Enter data.example.test_allowed_example = _
  | Enter data.example.test_allowed_example
  | | Fail not data.example.deny with input as {"allowed": "yes"}
  | Fail data.example.test_allowed_example = _

I can't really parse this error message except for knowing that test_allowed_example was the failed test.

How do I properly test cases where the input is allowed (not denied)?


Solution

  • TLDR; you can say any of these:

    • count(deny) == 0 with input as {"allowed":"yes"}
    • deny == set() with input as {"allowed":"yes"}
    • not deny["Must be allowed"] with input as {"allowed":"yes"}

    The last one checks if the set does not contain a specific message. This is a good idea if you have multiple deny rules inside the same package.


    The not deny with input as ... statement fails because the not keyword only inverts undefined/false statements (making them true). In this case, deny refers to a set of values. The set can be empty but it's never undefined/false.

    In OPA/Rego, all rules are just IF-THEN statements that assign values to variables. If the "IF" portion is the logic in the rule body. The "THEN" portion is the assignment in the rule head. There are two kinds of IF-THEN statements (aka rules):

    1. Complete rules, e.g., deny = true { input.allowed == "no" }
    2. Partial rules, e.g., deny[msg] { input.allowed == "no" }

    Complete rules assign a SINGLE value to a variable. If you omit the value it defaults to true (e.g., deny = true { ... } and deny { ... } mean the same thing.) When the "IF" portion of the rule is true/satisfied, the variable is assigned the value. When the "IF" portion of the rule is false/not satisfied, the variable is undefined. This is slightly different from false but in most cases that's not important.

    Partial rules assign MULTIPLE values to a variable. In other words, they define a SET of values. When the "IF" portion of the rule is true/satisfied, the value defined in the rule head is added to the set, otherwise the value is NOT added. If no values are added to the set, it's still defined--it's just empty.

    This is covered in the introduction: https://www.openpolicyagent.org/docs/latest/#rules

    For more examples and information on rules see https://www.openpolicyagent.org/docs/latest/policy-language/#rules