From e011f607d8e71408e5c7fa3d527aabeb2ef1b92a Mon Sep 17 00:00:00 2001 From: Aleksander Date: Fri, 3 May 2024 14:28:16 +0200 Subject: [PATCH] Use best practice package name in test examples (#6731) Best practice according to https://docs.styra.com/regal/rules/testing/test-outside-test-package is to have tests in a package with _test suffix, but this is not documented anywhere. Updating the examples is an easy way to teach readers the best practice Signed-off-by: Aleksander --- docs/content/policy-testing.md | 106 ++++++++++++++++++++------------- 1 file changed, 66 insertions(+), 40 deletions(-) diff --git a/docs/content/policy-testing.md b/docs/content/policy-testing.md index 24728dcb75..790e8b5849 100644 --- a/docs/content/policy-testing.md +++ b/docs/content/policy-testing.md @@ -32,6 +32,7 @@ profile. ```live:example:module:read_only,openable package authz + import rego.v1 allow if { @@ -50,23 +51,26 @@ To test this policy, we will create a separate Rego file that contains test case **example_test.rego**: ```live:example/test:module:read_only -package authz +package authz_test + import rego.v1 +import data.authz + test_post_allowed if { - allow with input as {"path": ["users"], "method": "POST"} + authz.allow with input as {"path": ["users"], "method": "POST"} } test_get_anonymous_denied if { - not allow with input as {"path": ["users"], "method": "GET"} + not authz.allow with input as {"path": ["users"], "method": "GET"} } test_get_user_allowed if { - allow with input as {"path": ["users", "bob"], "method": "GET", "user_id": "bob"} + authz.allow with input as {"path": ["users", "bob"], "method": "GET", "user_id": "bob"} } test_get_another_user_denied if { - not allow with input as {"path": ["users", "bob"], "method": "GET", "user_id": "alice"} + not authz.allow with input as {"path": ["users", "bob"], "method": "GET", "user_id": "alice"} } ``` @@ -81,10 +85,10 @@ To exercise the policy, run the `opa test` command in the directory containing t ```console $ opa test . -v -data.authz.test_post_allowed: PASS (1.417µs) -data.authz.test_get_anonymous_denied: PASS (426ns) -data.authz.test_get_user_allowed: PASS (367ns) -data.authz.test_get_another_user_denied: PASS (320ns) +data.authz_test.test_post_allowed: PASS (1.417µs) +data.authz_test.test_get_anonymous_denied: PASS (426ns) +data.authz_test.test_get_user_allowed: PASS (367ns) +data.authz_test.test_get_another_user_denied: PASS (320ns) -------------------------------------------------------------------------------- PASS: 4/4 ``` @@ -97,19 +101,19 @@ Try exercising the tests a bit more by removing the first rule in **example.rego $ opa test . -v FAILURES -------------------------------------------------------------------------------- -data.authz.test_post_allowed: FAIL (277.306µs) +data.authz_test.test_post_allowed: FAIL (277.306µs) - query:1 Enter data.authz.test_post_allowed = _ - example_test.rego:3 | Enter data.authz.test_post_allowed - example_test.rego:4 | | Fail data.authz.allow with input as {"method": "POST", "path": ["users"]} - query:1 | Fail data.authz.test_post_allowed = _ + query:1 Enter data.authz_test.test_post_allowed = _ + example_test.rego:3 | Enter data.authz_test.test_post_allowed + example_test.rego:4 | | Fail data.authz_test.allow with input as {"method": "POST", "path": ["users"]} + query:1 | Fail data.authz_test.test_post_allowed = _ SUMMARY -------------------------------------------------------------------------------- -data.authz.test_post_allowed: FAIL (277.306µs) -data.authz.test_get_anonymous_denied: PASS (124.287µs) -data.authz.test_get_user_allowed: PASS (242.2µs) -data.authz.test_get_another_user_denied: PASS (131.964µs) +data.authz_test.test_post_allowed: FAIL (277.306µs) +data.authz_test.test_get_anonymous_denied: PASS (124.287µs) +data.authz_test.test_get_user_allowed: PASS (242.2µs) +data.authz_test.test_get_another_user_denied: PASS (131.964µs) -------------------------------------------------------------------------------- PASS: 3/4 FAIL: 1/4 @@ -118,12 +122,15 @@ FAIL: 1/4 ## Test Format Tests are expressed as standard Rego rules with a convention that the rule -name is prefixed with `test_`. +name is prefixed with `test_`. It's a good practice for tests to be placed in a package suffixed with `_test`, but not a requirement. ```live:example_format:module:read_only -package mypackage +package mypackage_test + import rego.v1 +import data.mypackage + test_some_descriptive_name if { # test logic } @@ -153,9 +160,12 @@ by zero condition) the test result is marked as an `ERROR`. Tests prefixed with **pass_fail_error_test.rego**: ```live:example_results:module:read_only -package example +package example_test + import rego.v1 +import data.example + # This test will pass. test_ok if true @@ -167,7 +177,7 @@ test_error if 1 / 0 # This test will be skipped. todo_test_missing_implementation if { - allow with data.roles as ["not", "implemented"] + example.allow with data.roles as ["not", "implemented"] } ``` @@ -176,8 +186,8 @@ of the tests that failed or errored. ```console $ opa test pass_fail_error_test.rego -data.example.test_failure: FAIL (253ns) -data.example.test_error: ERROR (289ns) +data.example_test.test_failure: FAIL (253ns) +data.example_test.test_error: ERROR (289ns) pass_fail_error_test.rego:15: eval_builtin_error: div: divide by zero -------------------------------------------------------------------------------- PASS: 1/3 @@ -200,7 +210,7 @@ opa test --format=json pass_fail_error_test.rego "row": 4, "col": 1 }, - "package": "data.example", + "package": "data.example_test", "name": "test_ok", "duration": 618515 }, @@ -210,7 +220,7 @@ opa test --format=json pass_fail_error_test.rego "row": 9, "col": 1 }, - "package": "data.example", + "package": "data.example_test", "name": "test_failure", "fail": true, "duration": 322177 @@ -221,7 +231,7 @@ opa test --format=json pass_fail_error_test.rego "row": 14, "col": 1 }, - "package": "data.example", + "package": "data.example_test", "name": "test_error", "error": { "code": "eval_internal_error", @@ -255,6 +265,7 @@ Below is a simple policy that depends on the data document. ```live:with_keyword:module:read_only,openable package authz + import rego.v1 allow if { @@ -271,14 +282,17 @@ Below is the Rego file to test the above policy. **authz_test.rego**: ```live:with_keyword/tests:module:read_only -package authz +package authz_test + import rego.v1 +import data.authz + policies := [{"name": "test_policy"}] roles := {"admin": ["alice"]} test_allow_with_data if { - allow with input as {"user": "alice", "role": "admin"} + authz.allow with input as {"user": "alice", "role": "admin"} with data.policies as policies with data.roles as roles } @@ -288,7 +302,7 @@ To exercise the policy, run the `opa test` command. ```console $ opa test -v authz.rego authz_test.rego -data.authz.test_allow_with_data: PASS (697ns) +data.authz_test.test_allow_with_data: PASS (697ns) -------------------------------------------------------------------------------- PASS: 1/1 ``` @@ -299,6 +313,7 @@ Below is an example to replace a **rule without arguments**. ```live:with_keyword_rules:module:read_only package authz + import rego.v1 allow1 if allow2 @@ -309,17 +324,20 @@ allow2 if 2 == 1 **authz_test.rego**: ```live:with_keyword_rules/tests:module:read_only -package authz +package authz_test + import rego.v1 +import data.authz + test_replace_rule if { - allow1 with allow2 as true + authz.allow1 with authz.allow2 as true } ``` ```console $ opa test -v authz.rego authz_test.rego -data.authz.test_replace_rule: PASS (328ns) +data.authz_test.test_replace_rule: PASS (328ns) -------------------------------------------------------------------------------- PASS: 1/1 ``` @@ -330,6 +348,7 @@ Here is an example to replace a rule's **built-in function** with a user-defined ```live:with_keyword_builtins:module:read_only package authz + import rego.v1 import data.jwks.cert @@ -342,14 +361,17 @@ allow if { **authz_test.rego**: ```live:with_keyword_builtins/tests:module:read_only -package authz +package authz_test + import rego.v1 +import data.authz + mock_decode_verify("my-jwt", _) := [true, {}, {}] mock_decode_verify(x, _) := [false, {}, {}] if x != "my-jwt" test_allow if { - allow with input.headers["x-token"] as "my-jwt" + authz.allow with input.headers["x-token"] as "my-jwt" with data.jwks.cert as "mock-cert" with io.jwt.decode_verify as mock_decode_verify } @@ -357,7 +379,7 @@ test_allow if { ```console $ opa test -v authz.rego authz_test.rego -data.authz.test_allow: PASS (458.752µs) +data.authz_test.test_allow: PASS (458.752µs) -------------------------------------------------------------------------------- PASS: 1/1 ``` @@ -366,7 +388,7 @@ In simple cases, a function can also be replaced with a value, as in ```live:with_keyword_builtins/tests/value:module:read_only test_allow_value if { - allow + authz.allow with input.headers["x-token"] as "my-jwt" with data.jwks.cert as "mock-cert" with io.jwt.decode_verify as [true, {}, {}] @@ -383,6 +405,7 @@ function by a built-in function. ```live:with_keyword_funcs:module:read_only package authz + import rego.v1 replace_rule if { @@ -397,17 +420,20 @@ replace(label) if { **authz_test.rego**: ```live:with_keyword_funcs/tests:module:read_only -package authz +package authz_test + import rego.v1 +import data.authz + test_replace_rule if { - replace_rule with input.label as "does-not-matter" with replace as true + authz.replace_rule with input.label as "does-not-matter" with replace as true } ``` ```console $ opa test -v authz.rego authz_test.rego -data.authz.test_replace_rule: PASS (648.314µs) +data.authz_test.test_replace_rule: PASS (648.314µs) -------------------------------------------------------------------------------- PASS: 1/1 ```