test: cover electronic invoicing permissions

This commit is contained in:
Julien Denizot
2026-06-21 15:33:55 +00:00
parent 0c3fa65bb0
commit 21acaec408
2 changed files with 360 additions and 1 deletions
+1 -1
View File
@@ -179,7 +179,7 @@ def require_purchase_invoice_create_access() -> None:
return
frappe.throw(
frappe._(
_(
"Vous n’êtes pas autorisé à créer "
"une facture dachat."
),
+359
View File
@@ -0,0 +1,359 @@
from __future__ import annotations
import unittest
from types import SimpleNamespace
from unittest.mock import Mock, patch
import frappe
from enuxia_einvoice import permissions
def raise_frappe_exception(
message,
exc=None,
**kwargs,
):
"""Reproduire frappe.throw sans contexte de site."""
exception = exc or RuntimeError
raise exception(str(message))
class PermissionTestCase(unittest.TestCase):
def setUp(self) -> None:
translation_patcher = patch.object(
permissions,
"_",
side_effect=(
lambda message, *args, **kwargs: message
),
)
translation_patcher.start()
self.addCleanup(
translation_patcher.stop
)
throw_patcher = patch.object(
permissions.frappe,
"throw",
side_effect=raise_frappe_exception,
)
throw_patcher.start()
self.addCleanup(
throw_patcher.stop
)
def get_access_level(
self,
*,
user: str,
roles: list[str],
) -> int:
with (
patch.object(
permissions.frappe,
"session",
SimpleNamespace(user=user),
),
patch.object(
permissions.frappe,
"get_roles",
return_value=roles,
),
):
return (
permissions.get_einvoice_access_level()
)
class TestElectronicInvoiceRoles(
PermissionTestCase
):
def test_role_hierarchy(self) -> None:
cases = [
([], 0),
(
[permissions.ROLE_READER],
1,
),
(
[permissions.ROLE_OPERATOR],
2,
),
(
[permissions.ROLE_MANAGER],
3,
),
(
[
permissions.ROLE_READER,
permissions.ROLE_MANAGER,
],
3,
),
]
for roles, expected in cases:
with self.subTest(roles=roles):
self.assertEqual(
self.get_access_level(
user="user@example.com",
roles=roles,
),
expected,
)
def test_administrator_bypass(self) -> None:
self.assertEqual(
self.get_access_level(
user="Administrator",
roles=[],
),
99,
)
def test_system_manager_bypass(self) -> None:
self.assertEqual(
self.get_access_level(
user="manager@example.com",
roles=["System Manager"],
),
99,
)
def test_require_role_accepts_level(
self,
) -> None:
with patch.object(
permissions,
"get_einvoice_access_level",
return_value=2,
):
permissions.require_einvoice_role(
permissions.ROLE_OPERATOR
)
def test_require_role_rejects_lower_level(
self,
) -> None:
with patch.object(
permissions,
"get_einvoice_access_level",
return_value=1,
):
with self.assertRaises(
frappe.PermissionError
):
permissions.require_einvoice_role(
permissions.ROLE_OPERATOR
)
def test_require_role_rejects_unknown_role(
self,
) -> None:
with self.assertRaises(ValueError):
permissions.require_einvoice_role(
"Rôle inconnu"
)
class TestCompanyAccess(
PermissionTestCase
):
def test_empty_company_is_rejected(
self,
) -> None:
self.assertFalse(
permissions.has_company_access("")
)
def test_privileged_user_bypasses_company_read(
self,
) -> None:
with (
patch.object(
permissions,
"get_einvoice_access_level",
return_value=99,
),
patch.object(
permissions.frappe,
"get_doc",
) as get_doc,
):
self.assertTrue(
permissions.has_company_access(
"Enuxia"
)
)
get_doc.assert_not_called()
def test_company_native_read_is_checked(
self,
) -> None:
company = Mock()
with (
patch.object(
permissions,
"get_einvoice_access_level",
return_value=2,
),
patch.object(
permissions.frappe,
"get_doc",
return_value=company,
),
):
self.assertTrue(
permissions.has_company_access(
"Enuxia"
)
)
company.check_permission.assert_called_once_with(
"read"
)
def test_company_permission_error_is_rejected(
self,
) -> None:
company = Mock()
company.check_permission.side_effect = (
frappe.PermissionError
)
with (
patch.object(
permissions,
"get_einvoice_access_level",
return_value=2,
),
patch.object(
permissions.frappe,
"get_doc",
return_value=company,
),
):
self.assertFalse(
permissions.has_company_access(
"Société interdite"
)
)
def test_require_company_access_raises(
self,
) -> None:
with patch.object(
permissions,
"has_company_access",
return_value=False,
):
with self.assertRaises(
frappe.PermissionError
):
permissions.require_company_access(
"Société interdite"
)
class TestDocumentAccess(
PermissionTestCase
):
def test_exchange_access_composes_controls(
self,
) -> None:
exchange = Mock()
exchange.company = "Enuxia"
with (
patch.object(
permissions,
"require_einvoice_role",
) as require_role,
patch.object(
permissions,
"require_company_access",
) as require_company,
):
permissions.require_exchange_access(
exchange
)
require_role.assert_called_once_with(
permissions.ROLE_OPERATOR
)
exchange.check_permission.assert_called_once_with(
"read"
)
require_company.assert_called_once_with(
"Enuxia"
)
def test_sales_invoice_access_composes_controls(
self,
) -> None:
invoice = Mock()
invoice.company = "Enuxia"
with (
patch.object(
permissions,
"require_einvoice_role",
) as require_role,
patch.object(
permissions,
"require_company_access",
) as require_company,
):
permissions.require_sales_invoice_access(
invoice,
permission="read",
minimum_role=(
permissions.ROLE_READER
),
)
require_role.assert_called_once_with(
permissions.ROLE_READER
)
invoice.check_permission.assert_called_once_with(
"read"
)
require_company.assert_called_once_with(
"Enuxia"
)
def test_purchase_invoice_create_allowed(
self,
) -> None:
with patch.object(
permissions.frappe,
"has_permission",
return_value=True,
) as has_permission:
permissions.require_purchase_invoice_create_access()
has_permission.assert_called_once_with(
"Purchase Invoice",
ptype="create",
)
def test_purchase_invoice_create_denied(
self,
) -> None:
with patch.object(
permissions.frappe,
"has_permission",
return_value=False,
):
with self.assertRaises(
frappe.PermissionError
):
permissions.require_purchase_invoice_create_access()
if __name__ == "__main__":
unittest.main()