expect(app.post('Dropdowns.addDropdowntype',step='add',dropdowntype_name='Unsafe',dropdowntype_title='Unsafe',dropdowntype_table='*function',dropdowntype_column='getSettings'),422,'Callback metadata is rejected on write')
expect(app.post('Dropdowns.addDropdowntype',step='add',dropdowntype_name='Colors',dropdowntype_title='Colors',dropdowntype_table='*none',dropdowntype_column=''),200,'Safe dropdown type can be created')
expect(app.post('Dropdowns.updateDropdownDefault',dropdown_serial=first,dropdown_dropdowntype=kind,dropdown_checked='0, dropdown_value=123'),422,'SQL payload is rejected as an invalid boolean')
check(app.fixture('inspect')['dropdowns'][0]['dropdown_value']=='Green','Rejected SQL input leaves data unchanged')
expect(app.post('Dropdowns.updateDropdownDefault',dropdown_serial=first,dropdown_dropdowntype=kind,dropdown_checked='true'),409,'Inactive dropdown cannot become default')
expect(app.post('Dropdowns.updateDropdownActive',dropdown_serial=first,dropdown_checked='true'),200,'Dropdown can be activated')
expect(app.post('Dropdowns.updateDropdownDefault',dropdown_serial=first,dropdown_dropdowntype=kind,dropdown_checked='true'),200,'Active dropdown can become default')
expect(app.post('Dropdowns.deleteDropdown',dropdown_serial=first),409,'Default dropdown cannot be deleted')
expect(app.post('Dropdowns.deleteDropdowntype',dropdowntype_serial=kind),409,'Type with child dropdowns cannot be deleted')
expect(app.post('Dropdowns.updateDropdownDefault',dropdown_serial=first,dropdown_dropdowntype=99999,dropdown_checked='true'),404,'Wrong dropdown type is rejected')
expect(app.post('Dropdowns.addDropdown',step='add',dropdowntype_serial=kind,dropdown_value='Blue'),200,'Second dropdown can be created')
expect(app.post('Dropdowns.updateDropdownDefault',dropdown_serial=second,dropdown_dropdowntype=kind,dropdown_checked='true'),200,'Default can be switched atomically')
check(sum(int(row['dropdown_default'])forrowinapp.fixture('inspect')['dropdowns'])==1,'Only one dropdown default remains')
expect(app.post('Dropdowns.deleteDropdown',dropdown_serial=first),200,'Unused nondefault dropdown can be deleted')
unsafe=app.fixture('unsafeRule')['dropdown']
body=expect(app.post('Dropdowns.dropdownActive',dropdown_serial=unsafe),422,'Legacy callback also fails closed when already stored')
check(b'DatabasePassword'notinbodyandb'SmtpPassword'notinbody,'Callback failure does not return settings')
fornamein['Ready','Waiting']:
expect(app.post('Statuses.addStatus',step='add',status_name=name,status_classes='text-primary',status_active='true',status_default='true'),200,'Status '+name+' can be saved')
state=app.fixture('inspect')
check(sum(int(row['status_default'])forrowinstate['statuses'])==1,'Only one default status remains')
expect(app.post('Statuses.deleteStatus',status_serial=default),409,'Default status cannot be deleted')
expect(app.post('Statuses.statusActive',status_serial=default),200,'Status usage check no longer queries workorders')
expect(app.post('Statuses.addStatus',step='add',status_name='Broken',status_active='false',status_default='true'),422,'Inactive default status is rejected')
admin=state['users'][0]
expect(app.post('Users.deleteUser',user_serial=admin['user_serial']),409,'Administrator cannot delete own account')
expect(app.post('Users.updateActiveStatus',user_serial=admin['user_serial'],user_active='false'),409,'Last active administrator cannot be disabled')
expect(app.post('Users.editUser',step='update',user_serial=admin['user_serial'],user_name=admin['user_name'],user_first_name='Test',user_last_name='Administrator',user_email=admin['user_email'],user_role='User',user_active='true'),409,'Last administrator cannot be demoted')
expect(app.post('Notes.addNote',step='add',note_type='Other',note_reference=member['user_serial'],note_text='bad'),422,'Note type is validated during save')
expect(app.post('Notes.addNote',step='add',note_type='User',note_reference=999999,note_text='orphan'),409,'Orphan user note is rejected')
expect(app.post('Notes.addNote',step='add',note_type='User',note_reference=member['user_serial'],note_text='<script>text only</script>'),200,'Valid user note is saved')
expect(app.post('Users.deleteUser',user_serial=member['user_serial']),409,'User with note history cannot be deleted')
body=expect(app.request(data={'action':'Users.userSummary','user_serial':member['user_serial'],'csrf_token':csrf}),200,'User summary no longer depends on subscriptions')
check(b'<script>text only</script>'inbody,'Notes are escaped in the summary')
expect(app.post('Users.userActive',user_serial=member['user_serial']),200,'User usage check no longer queries requests')
expect(app.post('Roster.workspace',step='club',club_serial=0,club_name='North Woods'),200,'First club can be created')
expect(app.post('Roster.workspace',step='season',club_serial=club,season_name='2026 Season',starts_on='2026-01-01',ends_on='2026-12-31'),200,'Club season can be created')
expect(app.post('Roster.workspace',step='season',club_serial=club_a,season_name='Invalid',starts_on='2026-12-31',ends_on='2026-01-01'),422,'Reversed season dates are rejected')
expect(app.post('Roster.workspace',step='member',club_serial=club_a,identifier='member_one',membership_status='active',joined_on='2026-01-01'),409,'Unverified accounts cannot receive active memberships')
app.fixture('verifyEmail','member_one')
expect(app.post('Roster.workspace',step='member',club_serial=club_a,identifier='member_one',membership_status='active',joined_on='2026-01-01'),200,'Account can be assigned to a club')
expect(app.post('Roster.workspace',step='member',club_serial=club_a,identifier='member_one',membership_status='active',joined_on='2026-01-01'),409,'Duplicate club membership is rejected')
expect(app.post('Roster.workspace',step='charge',club_serial=club_a,membership_serial=membership,season_serial=season_b,amount='100.00'),409,'Cross-club season cannot be used for dues')
expect(app.post('Roster.workspace',step='charge',club_serial=club_a,membership_serial=membership,season_serial=season_a,amount='100.00'),200,'Season dues can be recorded')
expect(app.post('Roster.workspace',step='charge',club_serial=club_a,membership_serial=membership,season_serial=season_a,amount='100.00'),409,'Duplicate season dues are rejected')
check(notroot.findall('.//{http://schemas.openxmlformats.org/spreadsheetml/2006/main}f'),'Spreadsheet cells do not contain formulas')
body=expect(app.post('Users.editUser',step='update',user_serial=member['user_serial'],user_name='member_one',user_first_name='=HYPERLINK("unsafe")',user_last_name='Updated',user_email='member@example.test',user_role='User',user_active='true'),200,'Administrator can edit first and last names')
expect(app.request(data={'action':'SignUp.createAccount','registration_kind':'invited','csrf_token':app.csrf(signup),'user_first_name':'Regular','user_last_name':'Member','user_email':'http@example.test','user_name':'httpuser','user_password':'HTTP long password!','user_password_confirm':'HTTP long password!'}),200,'Regular member account can sign up')
expect(app.post('Roster.workspace',step='member',club_serial=club_a,membership_serial=http_membership,membership_status='inactive',joined_on='2026-01-01'),200,'Membership can be made inactive while preserving history')
app.client=member_client
expect(app.request('/?Xaction=Roster.workspace&club_serial='+str(club_a)),403,'Inactive membership immediately loses club access in an existing session')
expect(app.post('Roster.workspace',step='club',club_serial=club_a,club_name='North Woods',club_status='archived'),200,'Club can be archived without deleting its history')
app.client=member_client
expect(app.request('/?Xaction=Roster.workspace&club_serial='+str(club_a)),403,'Archived club is inaccessible to ordinary members')
app.client=admin_client
expect(app.post('Roster.workspace',step='club',club_serial=club_a,club_name='North Woods',club_status='active'),200,'Archived club can be reactivated')
check(len(app.fixture('inspect')['club_payments'])==1,'Archiving and reactivation preserve the payment ledger')
body=expect(app.request(data={'action':'Journal.viewJournal','csrf_token':csrf}),200,'Journal page loads after authentication events')
check('Showing 1 to 40 of 41'intext,'Per-page selection is respected')
expect(app.post('Journal.clearJournal'),404,'Journal-clearing endpoint is no longer routable')
expect(app.post('Roster.workspace',step='adjust',club_serial=club_a,due_serial=due,corrected_amount='90.00',reason='HTTP correction',request_key=secrets.token_hex(32)),200,'Dues corrections work through the protected HTTP route')
expect(app.post('Roster.workspace',step='payment',club_serial=club_a,due_serial=due,amount='1.00',paid_on='2026-01-02',request_key=secrets.token_hex(32),payment_method='check',payment_reference='=SUM(1,1)'),200,'Payment references and methods are saved through HTTP')
expect(app.post('Roster.workspace',step='reverse',club_serial=club_a,payment_serial=new_payment,reason='HTTP reversal',request_key=secrets.token_hex(32)),200,'Payment reversals work through the protected HTTP route')
body=expect(app.request(data={'action':'RosterReports.ledger','club_serial':club_a,'csrf_token':csrf}),200,'Financial history exports through HTTP')
check(b"'=SUM(1,1)"inbody,'Financial CSV neutralizes formula text in payment references')
check(b'HTTP correction'inbodyandb'HTTP reversal'inbody,'Financial CSV includes correction and reversal reasons')