"""Tests for policy linting."""

from pyauthz import (
    Effect,
    PyAuthz,
    Role,
    lint_policies,
)
from pyauthz.models.policy import Policy


class TestLintPolicies:
    def test_no_issues(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))
        rp.add_policy(
            Policy(name="allow-admin", effect=Effect.ALLOW, roles=["admin"], permissions=["*:*"])
        )
        issues = lint_policies(rp)
        assert len(issues) == 0

    def test_undefined_role(self):
        rp = PyAuthz()
        rp.add_policy(
            Policy(
                name="allow-ghosts",
                effect=Effect.ALLOW,
                roles=["ghost"],
                permissions=["*:*"],
            )
        )
        issues = lint_policies(rp)
        assert any(i.code == "L001" for i in issues)
        assert any("ghost" in i.message for i in issues)

    def test_overly_broad_policy(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))
        rp.add_policy(Policy(name="allow-all", effect=Effect.ALLOW, permissions=["*:*"]))
        issues = lint_policies(rp)
        assert any(i.code == "L002" for i in issues)

    def test_contradictory_policies(self):
        rp = PyAuthz()
        rp.add_role(Role(name="editor", permissions=["documents:write"]))
        rp.add_policy(
            Policy(
                name="allow-write",
                effect=Effect.ALLOW,
                roles=["editor"],
                permissions=["documents:write"],
            )
        )
        rp.add_policy(
            Policy(
                name="deny-write",
                effect=Effect.DENY,
                roles=["editor"],
                permissions=["documents:write"],
            )
        )
        issues = lint_policies(rp)
        assert any(i.code == "L003" for i in issues)

    def test_disabled_policy(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))
        rp.add_policy(
            Policy(
                name="old-policy",
                effect=Effect.ALLOW,
                roles=["admin"],
                permissions=["*:*"],
                enabled=False,
            )
        )
        issues = lint_policies(rp)
        assert any(i.code == "L004" for i in issues)

    def test_orphan_role(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))
        rp.add_role(Role(name="orphan", permissions=["documents:read"]))
        rp.add_policy(
            Policy(
                name="allow-admin",
                effect=Effect.ALLOW,
                roles=["admin"],
                permissions=["*:*"],
            )
        )
        issues = lint_policies(rp)
        assert any(i.code == "L005" and "orphan" in i.message for i in issues)

    def test_undefined_parent(self):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"], parents=["superadmin"]))
        issues = lint_policies(rp)
        assert any(i.code == "L006" for i in issues)

    def test_severity_ordering(self):
        rp = PyAuthz()
        rp.add_policy(
            Policy(
                name="bad-policy",
                effect=Effect.ALLOW,
                roles=["ghost"],
                permissions=["*:*"],
                enabled=False,
            )
        )
        issues = lint_policies(rp)
        # Errors should come before info
        if len(issues) >= 2:
            severities = [i.severity for i in issues]
            assert severities == sorted(
                severities, key=lambda s: {"error": 0, "warning": 1, "info": 2}[s.value]
            )

    def test_issue_str(self):
        rp = PyAuthz()
        rp.add_policy(
            Policy(
                name="bad-policy",
                effect=Effect.ALLOW,
                roles=["ghost"],
                permissions=["*:*"],
            )
        )
        issues = lint_policies(rp)
        issue_str = str(issues[0])
        assert "[error]" in issue_str
        assert "L001" in issue_str
