Group-structure tests for Phase 1 permissions overhaul. Covers: - All 7 new res.groups records present (8th role "No" is implicit) - Owner transitively implies base.group_system + every old group - Manager forms the diamond (implies both Shop Manager and Sales Manager) - Sales and Shop branches remain orthogonal at the leaf (Tech != Sales Rep) - uid 1/2 auto-assigned to Owner - Sequence numbers unique (renders dropdown predictably) - New groups imply old for backward-compat (30-day rollback safety) - Cross-module backward-compat chain works (e.g., Owner -> CGP DO) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
105 lines
5.2 KiB
Python
105 lines
5.2 KiB
Python
from odoo.tests.common import TransactionCase, tagged
|
|
|
|
|
|
@tagged('-at_install', 'post_install', 'fp_perms')
|
|
class TestRoleGroupsStructure(TransactionCase):
|
|
"""Verify the 8 new roles exist with correct implied_ids chains.
|
|
|
|
Part of Phase 1 permissions overhaul. See:
|
|
docs/superpowers/specs/2026-05-23-permissions-overhaul-design.md
|
|
"""
|
|
|
|
def test_all_seven_groups_exist(self):
|
|
"""The 7 new res.groups records must all be defined. (The 8th role 'No'
|
|
is implicit — absence of any plating group.)"""
|
|
xmlids = {
|
|
'group_fp_technician', 'group_fp_sales_rep',
|
|
'group_fp_shop_manager_v2', 'group_fp_sales_manager',
|
|
'group_fp_manager', 'group_fp_quality_manager', 'group_fp_owner',
|
|
}
|
|
for xmlid in xmlids:
|
|
grp = self.env.ref(f'fusion_plating.{xmlid}', raise_if_not_found=False)
|
|
self.assertTrue(grp, f'Group {xmlid} not found')
|
|
|
|
def test_owner_implies_quality_manager(self):
|
|
owner = self.env.ref('fusion_plating.group_fp_owner')
|
|
qm = self.env.ref('fusion_plating.group_fp_quality_manager')
|
|
self.assertIn(qm, owner.implied_ids)
|
|
|
|
def test_owner_implies_system(self):
|
|
owner = self.env.ref('fusion_plating.group_fp_owner')
|
|
system = self.env.ref('base.group_system')
|
|
self.assertIn(system, owner.trans_implied_ids,
|
|
'Owner must transitively imply base.group_system')
|
|
|
|
def test_manager_implies_both_branches(self):
|
|
"""Manager is the diamond apex — must imply both Shop Manager and Sales Manager."""
|
|
mgr = self.env.ref('fusion_plating.group_fp_manager')
|
|
sm = self.env.ref('fusion_plating.group_fp_shop_manager_v2')
|
|
sales_mgr = self.env.ref('fusion_plating.group_fp_sales_manager')
|
|
self.assertIn(sm, mgr.implied_ids, 'Manager must imply Shop Manager (diamond)')
|
|
self.assertIn(sales_mgr, mgr.implied_ids, 'Manager must imply Sales Manager (diamond)')
|
|
|
|
def test_technician_does_not_imply_sales_rep(self):
|
|
"""Sales and Shop branches must remain orthogonal at the leaf."""
|
|
tech = self.env.ref('fusion_plating.group_fp_technician')
|
|
sales_rep = self.env.ref('fusion_plating.group_fp_sales_rep')
|
|
self.assertNotIn(sales_rep, tech.trans_implied_ids,
|
|
'Technician must NOT see Sales Rep menus')
|
|
|
|
def test_sales_rep_does_not_imply_technician(self):
|
|
sales_rep = self.env.ref('fusion_plating.group_fp_sales_rep')
|
|
tech = self.env.ref('fusion_plating.group_fp_technician')
|
|
self.assertNotIn(tech, sales_rep.trans_implied_ids,
|
|
'Sales Rep must NOT see Workstation')
|
|
|
|
def test_owner_auto_assigned_to_uid_1_and_2(self):
|
|
owner = self.env.ref('fusion_plating.group_fp_owner')
|
|
user_ids = owner.user_ids.ids
|
|
self.assertIn(1, user_ids, 'Owner must include uid 1 (__system__)')
|
|
self.assertIn(2, user_ids, 'Owner must include uid 2 (admin)')
|
|
|
|
def test_sequence_numbers_are_unique(self):
|
|
seqs = [
|
|
self.env.ref(f'fusion_plating.{x}').sequence
|
|
for x in ('group_fp_technician', 'group_fp_sales_rep',
|
|
'group_fp_shop_manager_v2', 'group_fp_sales_manager',
|
|
'group_fp_manager', 'group_fp_quality_manager', 'group_fp_owner')
|
|
]
|
|
self.assertEqual(len(seqs), len(set(seqs)),
|
|
f'All sequence numbers must be unique, got {seqs}')
|
|
|
|
def test_new_groups_imply_old_for_backward_compat(self):
|
|
"""During the 30-day rollback window, new groups must trigger old ACLs."""
|
|
tech = self.env.ref('fusion_plating.group_fp_technician')
|
|
old_op = self.env.ref('fusion_plating.group_fusion_plating_operator')
|
|
self.assertIn(old_op, tech.trans_implied_ids)
|
|
|
|
mgr = self.env.ref('fusion_plating.group_fp_manager')
|
|
old_mgr = self.env.ref('fusion_plating.group_fusion_plating_manager')
|
|
self.assertIn(old_mgr, mgr.trans_implied_ids)
|
|
|
|
def test_owner_implies_all_old_groups_via_cross_module_chain(self):
|
|
"""Owner must transitively reach every old group (admin, manager, supervisor,
|
|
operator, estimator, receiving, accounting, cgp_officer, cgp_designated_official)
|
|
via the implication chain spread across fusion_plating + 4 downstream module
|
|
security files."""
|
|
owner = self.env.ref('fusion_plating.group_fp_owner')
|
|
expected_old = [
|
|
'fusion_plating.group_fusion_plating_admin',
|
|
'fusion_plating.group_fusion_plating_manager',
|
|
'fusion_plating.group_fusion_plating_supervisor',
|
|
'fusion_plating.group_fusion_plating_operator',
|
|
'fusion_plating_configurator.group_fp_estimator',
|
|
'fusion_plating_receiving.group_fp_receiving',
|
|
'fusion_plating_invoicing.group_fp_accounting',
|
|
'fusion_plating_cgp.group_fusion_plating_cgp_officer',
|
|
'fusion_plating_cgp.group_fusion_plating_cgp_designated_official',
|
|
]
|
|
for xmlid in expected_old:
|
|
old_grp = self.env.ref(xmlid, raise_if_not_found=False)
|
|
if not old_grp:
|
|
continue # Module not installed
|
|
self.assertIn(old_grp, owner.trans_implied_ids,
|
|
f'Owner must transitively imply {xmlid} for backward-compat')
|