Files
huntingclubroster/tests/tenant_repairs.php
T

33 lines
4.3 KiB
PHP

<?php
if(PHP_SAPI!=='cli')exit;
if(!preg_match('/\Ahcr_http_test_[a-f0-9]{12}\z/',getenv('HCR_DB_NAME')?:''))throw new RuntimeException('Disposable database required.');
require __DIR__.'/../classes/Base.php';
$db=(new Base())->connect();$ids=json_decode(getenv('HCR_TENANT_IDS'),true,512,JSON_THROW_ON_ERROR);$checks=0;
function repairCheck(bool $ok,string $message):void{global $checks;if(!$ok)throw new RuntimeException($message);$checks++;echo 'PASS: '.$message."\n";}
function repairActor(int $id):void{global $db;$s=$db->prepare('SELECT * FROM users WHERE user_serial=?');$s->execute([$id]);$u=$s->fetch();$_SESSION=[Base::USER_SERIAL=>$id,Base::USER_NAME=>$u['user_name'],Base::USER_ROLE=>$u['user_role']];}
class RepairInvitations extends ClubInvitations {protected function deliver(string $email,string $club):bool{return true;}}
$roster=new RosterService();$invites=new RepairInvitations();$access=new TenantAccess();
function invitationId(int $club,string $email):int{global $db;$s=$db->prepare('SELECT invitation_serial FROM club_invitations WHERE club_serial=? AND invite_email=?');$s->execute([$club,$email]);return (int)$s->fetchColumn();}
function ageInvitations():void{global $db;$db->exec('UPDATE club_invitations SET sent_at=DATE_SUB(UTC_TIMESTAMP(),INTERVAL 2 MINUTE)');}
repairActor(1);$roster->saveMember($ids['clubA'],$ids['clubA_admina'],'','inactive','2026-01-01','administrator');
repairActor($ids['admina2']);$invites->invite($ids['clubA'],'admina@example.test','member');
repairActor($ids['admina']);$invites->accept(invitationId($ids['clubA'],'admina@example.test'));
repairCheck(!$access->canManage($ids['clubA']),'Member invitation cannot restore an inactive administrator role');
$row=$db->query('SELECT membership_role,membership_status FROM club_memberships WHERE membership_serial='.$ids['clubA_admina'])->fetch();
repairCheck($row['membership_role']==='member'&&$row['membership_status']==='active','Former administrator rejoins as the newly approved member role');
repairCheck(!$access->canManage($ids['clubB']),'Rejoining cannot change an ordinary membership in another club');
repairActor(1);ageInvitations();$invites->invite($ids['clubA'],'admina@example.test','administrator');
repairActor($ids['admina']);$invites->accept(invitationId($ids['clubA'],'admina@example.test'));
repairCheck($access->canManage($ids['clubA']),'Fresh Super System Administrator invitation can explicitly restore administrator access');
repairActor($ids['admina2']);$invites->invite($ids['clubA'],'admina2@example.test','member');$invites->accept(invitationId($ids['clubA'],'admina2@example.test'));
repairCheck($access->canManage($ids['clubA']),'Member invitation does not accidentally demote an already active administrator');
repairActor(1);$invites->invite($ids['clubA'],'membera@example.test','administrator');
$pending=invitationId($ids['clubA'],'membera@example.test');$roster->saveMember($ids['clubA'],$ids['clubA_membera'],'','active','2026-01-01','administrator');
repairCheck($db->query('SELECT invitation_status FROM club_invitations WHERE invitation_serial='.$pending)->fetchColumn()==='revoked','Explicit role changes revoke outstanding invitations for that membership');
ageInvitations();$invites->invite($ids['clubA'],'membera@example.test','administrator');$pending=invitationId($ids['clubA'],'membera@example.test');$roster->saveMember($ids['clubA'],$ids['clubA_membera'],'','active','2026-01-01','member');
repairActor($ids['membera']);try{$invites->accept($pending);throw new RuntimeException('Stale invitation was accepted');}catch(AppException $e){repairCheck($e->status===404,'A pending administrator invitation cannot undo a later explicit demotion');}
ageInvitations();repairActor($ids['admina2']);$invites->invite($ids['clubA'],'shared@example.test','member');repairActor(1);$roster->saveMember($ids['clubA'],$ids['clubA_shared'],'','inactive','2026-01-01');
repairActor($ids['shared']);try{$invites->accept(invitationId($ids['clubA'],'shared@example.test'));throw new RuntimeException('Deactivation invitation accepted');}catch(AppException $e){repairCheck($e->status===404,'Membership deactivation still invalidates outstanding invitations');}
repairCheck(count($roster->clubs())===1&&(int)$roster->clubs()[0]['club_serial']===$ids['clubB'],'Deactivation leaves access to the other club intact');
echo "$checks tenant repair service checks passed.\n";