@@ -1176,6 +1176,81 @@ def test_org_detail_shows_change_tier_button_for_multi_type_category(self) -> No
11761176 # Keep deterministic with --keepdb: only same-category sponsorship tiers.
11771177 MembershipType .objects .update (enabled = False )
11781178
1179+ MembershipType .objects .update_or_create (
1180+ code = "platinum" ,
1181+ defaults = {
1182+ "name" : "Platinum Sponsor Member" ,
1183+ "category_id" : "sponsorship" ,
1184+ "sort_order" : 10 ,
1185+ "enabled" : True ,
1186+ "group_cn" : "almalinux-platinum" ,
1187+ },
1188+ )
1189+ MembershipType .objects .update_or_create (
1190+ code = "gold" ,
1191+ defaults = {
1192+ "name" : "Gold Sponsor Member" ,
1193+ "category_id" : "sponsorship" ,
1194+ "sort_order" : 20 ,
1195+ "enabled" : True ,
1196+ "group_cn" : "almalinux-gold" ,
1197+ },
1198+ )
1199+
1200+ MembershipType .objects .update_or_create (
1201+ code = "silver" ,
1202+ defaults = {
1203+ "name" : "Silver Sponsor Member" ,
1204+ "category_id" : "sponsorship" ,
1205+ "sort_order" : 30 ,
1206+ "enabled" : True ,
1207+ "group_cn" : "almalinux-silver" ,
1208+ },
1209+ )
1210+ MembershipType .objects .update_or_create (
1211+ code = "ruby" ,
1212+ defaults = {
1213+ "name" : "Ruby Sponsor Member" ,
1214+ "category_id" : "sponsorship" ,
1215+ "sort_order" : 40 ,
1216+ "enabled" : True ,
1217+ "group_cn" : "almalinux-ruby" ,
1218+ },
1219+ )
1220+
1221+ org = Organization .objects .create (name = "Tiered Org" , representative = "bob" )
1222+ Membership .objects .create (target_organization = org , membership_type_id = "silver" )
1223+
1224+ bob = FreeIPAUser ("bob" , {"uid" : ["bob" ], "memberof_group" : [], "c" : ["US" ]})
1225+ self ._login_as_freeipa_user ("bob" )
1226+
1227+ with patch ("core.backends.FreeIPAUser.get" , return_value = bob ):
1228+ resp = self .client .get (reverse ("organization-detail" , args = [org .pk ]))
1229+
1230+ self .assertEqual (resp .status_code , 200 )
1231+ self .assertContains (resp , "Change tier" )
1232+ tier_change_url = reverse ("organization-membership-request" , args = [org .pk ]) + "?membership_type=gold"
1233+ self .assertContains (resp , tier_change_url )
1234+ self .assertNotContains (
1235+ resp ,
1236+ reverse ("organization-membership-request" , args = [org .pk ]) + "?membership_type=ruby" ,
1237+ )
1238+
1239+ with (
1240+ patch ("core.backends.FreeIPAUser.get" , return_value = bob ),
1241+ patch ("core.views_membership.block_action_without_coc" , return_value = None ),
1242+ ):
1243+ tier_change_resp = self .client .get (tier_change_url )
1244+
1245+ self .assertEqual (tier_change_resp .status_code , 200 )
1246+ self .assertEqual (tier_change_resp .context ["form" ].initial .get ("membership_type" ), "gold" )
1247+ self .assertNotContains (resp , "Request membership" )
1248+
1249+ def test_org_detail_change_tier_uses_previous_tier_when_current_is_highest (self ) -> None :
1250+ from core .models import MembershipType , Organization
1251+
1252+ MembershipType .objects .update (enabled = False )
1253+
11791254 MembershipType .objects .update_or_create (
11801255 code = "silver" ,
11811256 defaults = {
@@ -1197,8 +1272,116 @@ def test_org_detail_shows_change_tier_button_for_multi_type_category(self) -> No
11971272 },
11981273 )
11991274
1200- org = Organization .objects .create (name = "Tiered Org" , representative = "bob" )
1201- Membership .objects .create (target_organization = org , membership_type_id = "silver" )
1275+ org = Organization .objects .create (name = "Top Tier Org" , representative = "bob" )
1276+ Membership .objects .create (target_organization = org , membership_type_id = "gold" )
1277+
1278+ bob = FreeIPAUser ("bob" , {"uid" : ["bob" ], "memberof_group" : [], "c" : ["US" ]})
1279+ self ._login_as_freeipa_user ("bob" )
1280+
1281+ with patch ("core.backends.FreeIPAUser.get" , return_value = bob ):
1282+ resp = self .client .get (reverse ("organization-detail" , args = [org .pk ]))
1283+
1284+ self .assertEqual (resp .status_code , 200 )
1285+ self .assertContains (resp , "Change tier" )
1286+ self .assertContains (
1287+ resp ,
1288+ reverse ("organization-membership-request" , args = [org .pk ]) + "?membership_type=silver" ,
1289+ )
1290+
1291+ def test_org_detail_change_tier_prefers_higher_ranked_tier_for_gold (self ) -> None :
1292+ from core .models import MembershipType , Organization
1293+
1294+ MembershipType .objects .update (enabled = False )
1295+
1296+ MembershipType .objects .update_or_create (
1297+ code = "platinum" ,
1298+ defaults = {
1299+ "name" : "Platinum Sponsor Member" ,
1300+ "category_id" : "sponsorship" ,
1301+ "sort_order" : 10 ,
1302+ "enabled" : True ,
1303+ "group_cn" : "almalinux-platinum" ,
1304+ },
1305+ )
1306+ MembershipType .objects .update_or_create (
1307+ code = "gold" ,
1308+ defaults = {
1309+ "name" : "Gold Sponsor Member" ,
1310+ "category_id" : "sponsorship" ,
1311+ "sort_order" : 20 ,
1312+ "enabled" : True ,
1313+ "group_cn" : "almalinux-gold" ,
1314+ },
1315+ )
1316+ MembershipType .objects .update_or_create (
1317+ code = "silver" ,
1318+ defaults = {
1319+ "name" : "Silver Sponsor Member" ,
1320+ "category_id" : "sponsorship" ,
1321+ "sort_order" : 30 ,
1322+ "enabled" : True ,
1323+ "group_cn" : "almalinux-silver" ,
1324+ },
1325+ )
1326+
1327+ org = Organization .objects .create (name = "Gold Tier Org" , representative = "bob" )
1328+ Membership .objects .create (target_organization = org , membership_type_id = "gold" )
1329+
1330+ bob = FreeIPAUser ("bob" , {"uid" : ["bob" ], "memberof_group" : [], "c" : ["US" ]})
1331+ self ._login_as_freeipa_user ("bob" )
1332+
1333+ with patch ("core.backends.FreeIPAUser.get" , return_value = bob ):
1334+ resp = self .client .get (reverse ("organization-detail" , args = [org .pk ]))
1335+
1336+ self .assertEqual (resp .status_code , 200 )
1337+ self .assertContains (resp , "Change tier" )
1338+ self .assertContains (
1339+ resp ,
1340+ reverse ("organization-membership-request" , args = [org .pk ]) + "?membership_type=platinum" ,
1341+ )
1342+ self .assertNotContains (
1343+ resp ,
1344+ reverse ("organization-membership-request" , args = [org .pk ]) + "?membership_type=silver" ,
1345+ )
1346+
1347+ def test_org_detail_change_tier_for_ruby_suggests_silver (self ) -> None :
1348+ from core .models import MembershipType , Organization
1349+
1350+ MembershipType .objects .update (enabled = False )
1351+
1352+ MembershipType .objects .update_or_create (
1353+ code = "gold" ,
1354+ defaults = {
1355+ "name" : "Gold Sponsor Member" ,
1356+ "category_id" : "sponsorship" ,
1357+ "sort_order" : 20 ,
1358+ "enabled" : True ,
1359+ "group_cn" : "almalinux-gold" ,
1360+ },
1361+ )
1362+ MembershipType .objects .update_or_create (
1363+ code = "silver" ,
1364+ defaults = {
1365+ "name" : "Silver Sponsor Member" ,
1366+ "category_id" : "sponsorship" ,
1367+ "sort_order" : 30 ,
1368+ "enabled" : True ,
1369+ "group_cn" : "almalinux-silver" ,
1370+ },
1371+ )
1372+ MembershipType .objects .update_or_create (
1373+ code = "ruby" ,
1374+ defaults = {
1375+ "name" : "Ruby Sponsor Member" ,
1376+ "category_id" : "sponsorship" ,
1377+ "sort_order" : 40 ,
1378+ "enabled" : True ,
1379+ "group_cn" : "almalinux-ruby" ,
1380+ },
1381+ )
1382+
1383+ org = Organization .objects .create (name = "Ruby Tier Org" , representative = "bob" )
1384+ Membership .objects .create (target_organization = org , membership_type_id = "ruby" )
12021385
12031386 bob = FreeIPAUser ("bob" , {"uid" : ["bob" ], "memberof_group" : [], "c" : ["US" ]})
12041387 self ._login_as_freeipa_user ("bob" )
@@ -1212,7 +1395,6 @@ def test_org_detail_shows_change_tier_button_for_multi_type_category(self) -> No
12121395 resp ,
12131396 reverse ("organization-membership-request" , args = [org .pk ]) + "?membership_type=silver" ,
12141397 )
1215- self .assertNotContains (resp , "Request membership" )
12161398
12171399 def test_org_detail_hides_request_membership_button_when_no_more_categories_available (self ) -> None :
12181400 from core .models import MembershipType , Organization
@@ -1574,6 +1756,107 @@ def test_org_membership_request_requires_representative(self) -> None:
15741756 resp = self .client .get (reverse ("organization-membership-request" , args = [org .pk ]))
15751757 self .assertEqual (resp .status_code , 200 )
15761758
1759+ def test_org_membership_request_country_check_uses_committee_requester_profile (self ) -> None :
1760+ from core .models import FreeIPAPermissionGrant , MembershipType , Organization
1761+
1762+ MembershipType .objects .update_or_create (
1763+ code = "gold" ,
1764+ defaults = {
1765+ "name" : "Gold Sponsor Member" ,
1766+ "category_id" : "sponsorship" ,
1767+ "sort_order" : 2 ,
1768+ "enabled" : True ,
1769+ },
1770+ )
1771+
1772+ org = Organization .objects .create (name = "Committee Country Check Org" , representative = "bob" )
1773+
1774+ FreeIPAPermissionGrant .objects .create (
1775+ permission = ASTRA_ADD_MEMBERSHIP ,
1776+ principal_type = FreeIPAPermissionGrant .PrincipalType .user ,
1777+ principal_name = "reviewer" ,
1778+ )
1779+
1780+ reviewer = FreeIPAUser ("reviewer" , {"uid" : ["reviewer" ], "memberof_group" : [], "c" : ["US" ]})
1781+ representative = FreeIPAUser ("bob" , {"uid" : ["bob" ], "memberof_group" : [], "c" : ["DE" ]})
1782+
1783+ def _get_user (username : str ) -> FreeIPAUser | None :
1784+ if username == "reviewer" :
1785+ return reviewer
1786+ if username == "bob" :
1787+ return representative
1788+ return None
1789+
1790+ self ._login_as_freeipa_user ("reviewer" )
1791+
1792+ with (
1793+ patch ("core.backends.FreeIPAUser.get" , side_effect = _get_user ),
1794+ patch ("core.views_membership.block_action_without_coc" , return_value = None ),
1795+ patch ("core.views_membership.block_action_without_country_code" , return_value = None ) as country_mock ,
1796+ ):
1797+ resp = self .client .get (reverse ("organization-membership-request" , args = [org .pk ]))
1798+
1799+ self .assertEqual (resp .status_code , 200 )
1800+ self .assertEqual (country_mock .call_count , 1 )
1801+ self .assertIs (country_mock .call_args .kwargs ["user_data" ], reviewer ._user_data )
1802+
1803+ def test_org_membership_request_country_check_uses_representative_profile (self ) -> None :
1804+ from core .models import MembershipType , Organization
1805+
1806+ MembershipType .objects .update_or_create (
1807+ code = "gold" ,
1808+ defaults = {
1809+ "name" : "Gold Sponsor Member" ,
1810+ "category_id" : "sponsorship" ,
1811+ "sort_order" : 2 ,
1812+ "enabled" : True ,
1813+ },
1814+ )
1815+
1816+ org = Organization .objects .create (name = "Representative Country Check Org" , representative = "bob" )
1817+
1818+ representative = FreeIPAUser ("bob" , {"uid" : ["bob" ], "memberof_group" : [], "c" : ["DE" ]})
1819+ self ._login_as_freeipa_user ("bob" )
1820+
1821+ with (
1822+ patch ("core.backends.FreeIPAUser.get" , return_value = representative ),
1823+ patch ("core.views_membership.block_action_without_coc" , return_value = None ),
1824+ patch ("core.views_membership.block_action_without_country_code" , return_value = None ) as country_mock ,
1825+ ):
1826+ resp = self .client .get (reverse ("organization-membership-request" , args = [org .pk ]))
1827+
1828+ self .assertEqual (resp .status_code , 200 )
1829+ self .assertEqual (country_mock .call_count , 1 )
1830+ self .assertIs (country_mock .call_args .kwargs ["user_data" ], representative ._user_data )
1831+
1832+ def test_user_membership_request_country_check_uses_requester_profile (self ) -> None :
1833+ from core .models import MembershipType
1834+
1835+ MembershipType .objects .update_or_create (
1836+ code = "individual" ,
1837+ defaults = {
1838+ "name" : "Individual" ,
1839+ "group_cn" : "almalinux-individual" ,
1840+ "category_id" : "individual" ,
1841+ "sort_order" : 0 ,
1842+ "enabled" : True ,
1843+ },
1844+ )
1845+
1846+ alice = FreeIPAUser ("alice" , {"uid" : ["alice" ], "memberof_group" : [], "c" : ["US" ]})
1847+ self ._login_as_freeipa_user ("alice" )
1848+
1849+ with (
1850+ patch ("core.backends.FreeIPAUser.get" , return_value = alice ),
1851+ patch ("core.views_membership.block_action_without_coc" , return_value = None ),
1852+ patch ("core.views_membership.block_action_without_country_code" , return_value = None ) as country_mock ,
1853+ ):
1854+ resp = self .client .get (reverse ("membership-request" ))
1855+
1856+ self .assertEqual (resp .status_code , 200 )
1857+ self .assertEqual (country_mock .call_count , 1 )
1858+ self .assertIs (country_mock .call_args .kwargs ["user_data" ], alice ._user_data )
1859+
15771860 def test_org_detail_renewal_cta_uses_canonical_membership_request_link (self ) -> None :
15781861 from core .models import Membership , MembershipType , Organization
15791862
0 commit comments