generated from jric11/baseProject
49 lines
4.7 KiB
Python
49 lines
4.7 KiB
Python
|
|
import re
|
||
|
|
from application_harness import Application
|
||
|
|
app=Application();checks=0
|
||
|
|
def check(ok,message):
|
||
|
|
global checks
|
||
|
|
assert ok,message
|
||
|
|
checks+=1;print('PASS:',message)
|
||
|
|
def summary(club,membership,extra=''):
|
||
|
|
return app.request('/main.php?Xaction=Roster.memberSummary&club_serial='+str(club)+'&membership_serial='+str(membership)+extra)
|
||
|
|
try:
|
||
|
|
ids=app.fixture('tenantSeed');app.fixture('memberSummarySeed',str(ids['clubA']),str(ids['clubB']),str(ids['clubA_membera']),str(ids['clubA_shared']),str(ids['clubB_shared']))
|
||
|
|
_,login,_=app.request()
|
||
|
|
app.request(data={'action':'SignIn.login','csrf_token':app.csrf(login),'user_email':'admin@example.test','user_password':'Admin test password 123!'})
|
||
|
|
status,body,_=summary(ids['clubA'],ids['clubA_membera'])
|
||
|
|
check(status==200 and b'<h1>Account security</h1>' in body and b'<h1>Tenant Membera</h1>' not in body,'Member summary cannot bypass mandatory administrator MFA')
|
||
|
|
check(app.request('/main.php?Xaction=Roster.memberSummary&club_serial='+str(ids['clubA'])+'&membership_serial='+str(ids['clubA_membera']),ajax=True)[0]==403,'AJAX member summary also requires completed administrator MFA')
|
||
|
|
app.login();before=app.fixture('fingerprints')
|
||
|
|
status,body,_=summary(ids['clubA'],ids['clubA_membera'])
|
||
|
|
check(status==200 and b'<h1>Tenant Membera</h1>' in body and b'membera@example.test' in body,'Super Administrator opens a club member summary')
|
||
|
|
check(b'$3,150.00' in body and b'$31.00' in body and b'$3,119.00' in body,'Totals include all dues corrections and exclude reversed payments across every page')
|
||
|
|
check(b'Own reversed receipt' in body and b'OWN-RECEIPT-32' in body,'Payment history preserves reversed receipts and their reasons')
|
||
|
|
check(b'<script>unsafe</script>' in body and b'<script>unsafe</script>' not in body,'Correction reasons are escaped safely')
|
||
|
|
check(b'NEIGHBOR-PRIVATE' not in body and b'FOREIGN-PRIVATE' not in body,'Selected member summary excludes neighboring members and other clubs')
|
||
|
|
check(body.count(b'OWN-RECEIPT-')==25 and b'31 records.' in body and b'32 records.' in body,'Member dues and payment histories are bounded and counted independently')
|
||
|
|
links=re.findall(rb'href="([^"]+)"',body)
|
||
|
|
check(any(b'payments_page=2' in link and b'membership_serial='+str(ids['clubA_membera']).encode() in link and b'Roster.memberSummary' in link for link in links),'History pager links retain both club and membership scope')
|
||
|
|
status,last,_=summary(ids['clubA'],ids['clubA_membera'],'&payments_page=9999&dues_page=9999')
|
||
|
|
check(status==200 and last.count(b'OWN-RECEIPT-')==7 and b'Page 2 of 2' in last,'Final history pages are clamped and show only remaining records')
|
||
|
|
check(b'$3,150.00' in last and b'$31.00' in last and b'$3,119.00' in last,'Summary totals remain identical on later history pages')
|
||
|
|
check(summary(ids['clubB'],ids['clubA_membera'])[0]==404,'Membership IDs cannot be paired with another club')
|
||
|
|
check(summary(ids['clubA'],'invalid')[0]==422,'Invalid membership identifiers are rejected')
|
||
|
|
check(summary(ids['clubA'],ids['clubA_membera'],'&payments_page=0')[0]==422,'Invalid history pages are rejected')
|
||
|
|
check(app.fixture('fingerprints')==before,'Opening summaries and changing pages perform no database writes beyond authentication bookkeeping')
|
||
|
|
app.logout();app.login('admina@example.test','Tenant test password 123!')
|
||
|
|
check(summary(ids['clubA'],ids['clubA_membera'])[0]==200,'Club administrator can view a member in the administered club')
|
||
|
|
check(summary(ids['clubB'],ids['clubB_shared'])[0]==404,'Administrator cannot view another member where they are only an ordinary member')
|
||
|
|
app.logout();app.login('adminb@example.test','Tenant test password 123!')
|
||
|
|
check(summary(ids['clubA'],ids['clubA_membera'])[0]==403,'Other club administrator is denied access to the club summary')
|
||
|
|
app.logout();app.login('membera@example.test','Tenant test password 123!')
|
||
|
|
status,body,_=summary(ids['clubA'],ids['clubA_membera'])
|
||
|
|
check(status==200 and b'$3,119.00' in body and b'club-action-modal' not in body,'Member can read their own finances without administrator actions')
|
||
|
|
check(summary(ids['clubA'],ids['clubA_shared'])[0]==404,'Member cannot read another member summary in the same club')
|
||
|
|
check(summary(ids['clubB'],ids['clubB_shared'])[0]==403,'Member cannot read another club summary')
|
||
|
|
app.logout();app.login('shared@example.test','Tenant test password 123!')
|
||
|
|
_,a,_=summary(ids['clubA'],ids['clubA_shared']);_,b,_=summary(ids['clubB'],ids['clubB_shared'])
|
||
|
|
check(b'NEIGHBOR-PRIVATE' in a and b'FOREIGN-PRIVATE' not in a and b'FOREIGN-PRIVATE' in b and b'NEIGHBOR-PRIVATE' not in b,'A member of two clubs sees separate membership histories')
|
||
|
|
print(checks,'member summary HTTP checks passed.')
|
||
|
|
finally:app.close()
|