diff options
Diffstat (limited to 'tests/unit/test_privileges_spec_parsing.py')
| -rw-r--r-- | tests/unit/test_privileges_spec_parsing.py | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/tests/unit/test_privileges_spec_parsing.py b/tests/unit/test_privileges_spec_parsing.py new file mode 100644 index 0000000..124f570 --- /dev/null +++ b/tests/unit/test_privileges_spec_parsing.py @@ -0,0 +1,174 @@ +"""Tests for parsing the privileges checks specification.""" +import pytest + +from gn_libs.privileges import parse, SpecificationValueError + + +## NOTE: Should we limit depth of nesting of checks, e.g. don't do more than +## 3 levels or so? + + +@pytest.mark.unit_test +@pytest.mark.parametrize( + "spec", + ("", + "(AND)", + "(AND (OR))", + "(OR (AND))", + "(OR (AND (OR (AND ))))")) +def test_empty_spec(spec): + """ + GIVEN: An effectively empty specification + WHEN: The specification is parsed + THEN: Raise a `SpecificationValueError` + """ + with pytest.raises(SpecificationValueError): + parse(spec) + + +@pytest.mark.unit_test +@pytest.mark.parametrize( + "spec,expected", + (("(AND priv1)", ("AND", ("priv1",))), + ("(AND priv1 priv2)", ("AND", ("priv1", "priv2"))), + ("(AND priv1 priv2 priv3)", ("AND", ("priv1", "priv2", "priv3"))), + ("(and priv1)", ("AND", ("priv1",))), + ("(and priv1 priv2)", ("AND", ("priv1", "priv2"))), + ("(and priv1 priv2 priv3)", ("AND", ("priv1", "priv2", "priv3"))), + ("(and priv1 priv2 (and priv3 priv4))", + ("AND", ("priv1", "priv2", "priv3", "priv4"))))) +def test_and(spec, expected): + """ + GIVEN: A simple 'AND' privileges check specification `spec` + WHEN: The specification is parsed + THEN: Verify the parsed output gives an 'AND' check object + """ + assert parse(spec) == expected + + +@pytest.mark.unit_test +@pytest.mark.parametrize( + "spec,expected", + (("(OR priv1)", ("OR", ("priv1",))), + ("(OR priv1 priv2)", ("OR", ("priv1", "priv2"))), + ("(OR priv1 priv2 priv3)", ("OR", ("priv1", "priv2", "priv3"))), + ("(or priv1)", ("OR", ("priv1",))), + ("(or priv1 priv2)", ("OR", ("priv1", "priv2"))), + ("(or priv1 priv2 priv3)", ("OR", ("priv1", "priv2", "priv3"))))) +def test_or(spec, expected): + """ + GIVEN: A simple 'OR' privileges check specification `spec` + WHEN: The specification is parsed + THEN: Verify the parsed output gives an 'OR' check object + """ + assert parse(spec) == expected + + +@pytest.mark.unit_test +@pytest.mark.parametrize( + "spec,expected", + (("(or priv1 priv2 (or priv3 priv4))", + ("OR", ("priv1", "priv2", "priv3", "priv4"))), + ("(and priv1 priv2 (and priv3 priv4))", + ("AND", ("priv1", "priv2", "priv3", "priv4"))))) +def test_merging(spec, expected): + """ + GIVEN: + - A nested specification where 2 or more of subsequent operators are + - the same + WHEN: The specification is parsed + THEN: Verify the parsed output merges the checks into a single object + """ + # NOTE: The "given-when-then" description above does not please me. + assert parse(spec) == expected + + +@pytest.mark.unit_test +@pytest.mark.parametrize( + "spec,expected", + (("(AND priv1 (or priv2 priv3))", + ("AND", ("priv1",), ("OR", ("priv2", "priv3")))),)) +def test_and_or(spec, expected): + """ + GIVEN: + - A specification beginning with an "AND" operator followed by an "OR" + - operator + WHEN: The specification is parsed + THEN: Verify the parsed output is correct + """ + assert parse(spec) == expected + + +@pytest.mark.unit_test +@pytest.mark.parametrize( + "spec,expected", + (("(OR priv1 priv2 priv3 (and priv4 priv5))", + ("OR", ("priv1", "priv2", "priv3"), ("AND", ("priv4", "priv5")))),)) +def test_or_and(spec, expected): + """ + GIVEN: + - A specification beginning with an "OR" operator followed by an "AND" + - operator + WHEN: The specification is parsed + THEN: Verify the parsed output is correct + """ + assert parse(spec) == expected + + +@pytest.mark.unit_test +@pytest.mark.parametrize( + "spec", + ("()", + "this is invalid", + "priv1 AND priv2", + "(priv1 AND priv2)", + "(AND priv1 priv2 priv3")) +def test_invalid(spec): + """ + GIVEN: An invalid specification + WHEN: The specification is parsed + THEN: Verify that the `SpecificationValueError` is raised + """ + # NOTE: Maybe use hypothesis to generate random strings? + with pytest.raises(SpecificationValueError): + assert parse(spec) + + +@pytest.mark.unit_test +@pytest.mark.parametrize( + "spec,expected", + (("(AND priv1 (or priv2 priv3) priv4 (and priv5 priv6))", + ("AND", + ("priv1", "priv4", "priv5", "priv6"), + ("OR", ("priv2", "priv3")))),)) +def test_complex(spec, expected): + """ + GIVEN: An valid, but more complex specification + WHEN: The specification is parsed + THEN: Verify that the specification parses correctly + """ + assert parse(spec) == expected + + +@pytest.mark.unit_test +@pytest.mark.parametrize( + "spec,expected", + ( + # -- We need to be careful about reduction -- + # -- Please revisit your boolean logic to verify -- + # -- how to reduce boolean statements. -- + # ("(AND priv1 (or priv2 priv3) priv4 (or priv5 priv6))", + # ("AND", + # ("priv1", "priv4"), + # ("OR", ("priv2", "priv3", "priv5", "priv6")))), + ("(OR priv1 (or priv2 priv3 (or priv4 priv5)) (or priv6 priv7))", + ("OR", ("priv1", "priv6", "priv7", "priv2", "priv3", "priv4", "priv5"))),)) +def test_reduction(spec, expected): + """ + GIVEN: A spec that can be reduced + WHEN: The specification is parsed + THEN: + - Verify that after parsing, it is reduced to the minimum number of levels + - possible. + """ + assert parse(spec) == expected |
