Proxy Voting System - Testing Guide
Pre-Testing Checklist
- Backend compiled successfully:
cargo checkpasses - Frontend compiled successfully:
npm run checkpasses - Database exists and migrations have been run
-
VITE_API_BASEenvironment variable correctly points to backend - Auth service running if required
- Fresh database state recommended
Test Scenarios
Scenario 1: Non-Senator (No Proxy)
Expected Behavior: User gets 0 vote instances, cannot vote
Steps:
- Log in to voter interface
- Enter session code and join
- On
ProxySetupscreen:- Select “No” for senator
- Leave proxy name empty
- Click “Continue”
- Should see notice: “You currently have 0 vote instances for this session.”
- Verify on
WaitingPagethat notice is displayed - When motion becomes active, verify user sees “No voting options available”
Verification:
-
/session/{code}/proxyreturnsvote_instance_count: 0 - Database check: 0
user_sessionrows for this user+session withjoin_left = Joined - Attendance endpoint shows user as
is_proxy_holder: false,proxy_for: []
Scenario 2: Non-Senator Proxy
Expected Behavior: User gets 1 vote instance (proxy only), can vote once
Steps:
- Log in to voter interface
- Enter session code and join
- On
ProxySetupscreen:- Select “No” for senator
- Enter proxy name: “Jane Doe”
- Click “Continue”
- Should see notice: “You now have 1 proxy vote instance.”
- When motion becomes active, verify user sees 1 voting option labeled “Jane Doe”
- Cast vote on that option
- Verify vote is recorded with
is_proxy: true,proxy_for_name: "Jane Doe"
Verification:
-
/session/{code}/proxyreturnsvote_instance_count: 1, is_senator: false, has_proxy: true - Database check: 1
user_sessionrow withproxy = 'Jane Doe' -
/events/{id}/vote-instancesreturns 1 instance withis_proxy: true, proxy_for_name: "Jane Doe" - Vote in database has
proxy: truein data field - Attendance endpoint shows user as
is_proxy_holder: true,proxy_for: ["Jane Doe"]
Scenario 3: Senator (No Proxy)
Expected Behavior: User gets 1 vote instance (base only), can vote once as self
Steps:
- Log in to voter interface
- Enter session code and join
- On
ProxySetupscreen:- Select “Yes” for senator
- Leave proxy name empty
- Click “Continue”
- Should see notice: “You now have 1 vote instance.”
- When motion becomes active, verify user sees 1 voting option (unnamed, or labeled “Yourself”)
- Cast vote on that option
- Verify vote is recorded with
is_proxy: false
Verification:
-
/session/{code}/proxyreturnsvote_instance_count: 1, is_senator: true, has_proxy: false - Database check: 1
user_sessionrow withproxy = NULL -
/events/{id}/vote-instancesreturns 1 instance withis_proxy: false, proxy_for_name: null - Vote in database has
proxy: falsein data field - Attendance endpoint shows user as
is_proxy_holder: false,proxy_for: []
Scenario 4: Senator Proxy
Expected Behavior: User gets 2 vote instances (base + proxy), can vote twice
Steps:
- Log in to voter interface
- Enter session code and join
- On
ProxySetupscreen:- Select “Yes” for senator
- Enter proxy name: “John Smith”
- Click “Continue”
- Should see notice: “You now have 2 vote instances (your own vote + one proxy vote).”
- When motion becomes active, verify user sees 2 voting options: one unnamed (self) + one labeled “John Smith”
- Cast votes on both options (can be same or different)
- Verify both votes are recorded correctly
Verification:
-
/session/{code}/proxyreturnsvote_instance_count: 2, is_senator: true, has_proxy: true - Database check: 2
user_sessionrows (one withproxy = NULL, one withproxy = 'John Smith') -
/events/{id}/vote-instancesreturns 2 instances: one withis_proxy: false, one withis_proxy: true, proxy_for_name: "John Smith" - Both votes in database with appropriate
proxyfields - Attendance endpoint shows user as
is_proxy_holder: true,proxy_for: ["John Smith"]
Scenario 5: Idempotency - Same Submission Twice
Expected Behavior: Endpoint is idempotent; calling twice with same payload returns same result
Steps:
- On
ProxySetupscreen:- Select “Yes” for senator
- Enter “Jane Doe”
- Click “Continue” → notice shows 2 instances
- (Hypothetically) Call same endpoint again with identical payload
- Should still get notice saying 2 instances
Manual Test (via curl or API client):
curl -X POST http://localhost:8000/session/ABC123/proxy \ -H "Content-Type: application/json" \ -H "Cookie: <auth_cookie>" \ -d '{"is_senator": true, "proxy_for": "Jane Doe"}'# Response: 2 instances
# Call again with same payloadcurl -X POST http://localhost:8000/session/ABC123/proxy \ -H "Content-Type: application/json" \ -H "Cookie: <auth_cookie>" \ -d '{"is_senator": true, "proxy_for": "Jane Doe"}'# Response: should still be 2 instances, no errorVerification:
- Both calls return identical response
- No duplicate
user_sessionrows created - Database remains consistent
Scenario 6: Re-Submission with Changes
Expected Behavior: Changing configuration updates instance set correctly
Steps:
- User goes through flow as senator with proxy “Jane” → gets 2 instances
- On wait page, user realizes they entered wrong name → goes “Back”
- Re-enters proxy setup, changes to senator with proxy “John”
- Should see notice: “You now have 2 vote instances…” (same count, updated proxy)
- Verify database only has “John”, not “Jane”
Manual Test:
# First callcurl -X POST http://localhost:8000/session/ABC123/proxy \ -d '{"is_senator": true, "proxy_for": "Jane"}'# Response: 2 instances
# Second call with different proxycurl -X POST http://localhost:8000/session/ABC123/proxy \ -d '{"is_senator": true, "proxy_for": "John"}'# Response: 2 instances (but for John, not Jane)
# Verify databaseSELECT proxy FROM user_session WHERE user_id = ? AND session_id = ? AND join_left = 'Joined'# Should show: NULL and 'John' (not 'Jane')Verification:
- Old proxy instance replaced with new proxy name
- Instance count remains 2
- No orphaned database rows
Scenario 7: Changing from Senator to Non-Senator
Expected Behavior: Base instance deleted; only proxy remains
Steps:
- User initially selects “Yes” for senator with proxy “Jane” → 2 instances
- User changes mind on proxy setup, selects “No” for senator + proxy “Jane”
- Should see notice: “You now have 1 proxy vote instance.”
- Verify database: only 1 row with
proxy = 'Jane', no base row
Manual Test:
# First call (senator)curl -X POST http://localhost:8000/session/ABC123/proxy \ -d '{"is_senator": true, "proxy_for": "Jane"}'# Response: 2 instances
# Second call (non-senator, same proxy)curl -X POST http://localhost:8000/session/ABC123/proxy \ -d '{"is_senator": false, "proxy_for": "Jane"}'# Response: 1 instance
# Verify databaseSELECT proxy FROM user_session WHERE user_id = ? AND session_id = ? AND join_left = 'Joined'# Should show: 'Jane' only (no NULL row)Verification:
- Base instance deleted
- Proxy instance preserved
- Instance count becomes 1
Scenario 8: Host Attendance View
Expected Behavior: Host sees proxy assignments clearly in meeting overview
Steps:
- Host creates session and starts waiting for attendees
- Multiple users join with different configurations:
- User A: Senator, no proxy
- User B: Non-senator, proxying for “User A”
- User C: Senator, proxying for “User D”
- Host views attendance (in
SessionCreationhover cards) - Verify each user shows correct proxy status
Verification:
- GET
/session/{code}/attendancereturns:- User A:
is_proxy_holder: false, proxy_for: [] - User B:
is_proxy_holder: true, proxy_for: ["User A"] - User C:
is_proxy_holder: true, proxy_for: ["User D"]
- User A:
- Host UI displays these correctly in participant hover cards
Scenario 9: Proxy Name Whitespace Handling
Expected Behavior: Leading/trailing spaces trimmed server-side
Steps:
- User enters proxy name: ” Jane Doe ” (with extra spaces)
- Backend should trim and store as “Jane Doe”
- Verify notice and voting options show clean name
Manual Test:
curl -X POST http://localhost:8000/session/ABC123/proxy \ -d '{"is_senator": true, "proxy_for": " Jane Doe "}'# Response: should work, instance created with "Jane Doe"Verification:
- Database stores “Jane Doe” (no extra spaces)
- Voting interface displays “Jane Doe” (no padding)
Scenario 10: Empty Proxy Name
Expected Behavior: Empty string treated as null; no proxy instance created
Steps:
- User enters proxy name: "" (empty string)
- Backend should treat as null
- If senator, should get 1 base instance
- If non-senator, should get 0 instances
Manual Test:
curl -X POST http://localhost:8000/session/ABC123/proxy \ -d '{"is_senator": true, "proxy_for": ""}'# Response: 1 instance (base only)
curl -X POST http://localhost:8000/session/ABC123/proxy \ -d '{"is_senator": false, "proxy_for": ""}'# Response: 0 instancesVerification:
- Empty string not stored in database
- Instance count calculated correctly
Integration Test: Full Voting Flow
Objective: Test complete proxy voting flow from session creation to vote recording
Setup:
- Create a session as admin
- 3 attendees join: Alice (senator), Bob (non-senator proxying for Alice), Charlie (senator proxying for David)
Steps:
- Alice: “Yes” senator, no proxy → expects 1 instance
- Bob: “No” non-senator, proxy “Alice” → expects 1 instance
- Charlie: “Yes” senator, proxy “David” → expects 2 instances
- Admin starts a motion
- Each user casts votes on all available options
- Verify vote counts in results:
- Should see 4 total votes (1 + 1 + 2 = 4, not 3)
- Votes labeled with their “from” user and proxy status
Verification:
- Vote instances total 4 (not 3)
- Each vote correctly tagged with user + proxy info
- Results display includes proxy vote attribution
Performance & Edge Cases
High Concurrency Test
- 100+ users join session simultaneously
- All submit participation config in parallel
- Verify no duplicate instances created
SQL Injection / Input Validation
- Try proxy name:
"'; DROP TABLE user_session; --" - Verify: Safely escaped, stored literally (or rejected)
Large Proxy Names
- Try proxy name: 1000+ character string
- Verify: Either accepted or reasonable error message
Special Characters
- Try proxy names with: emoji, unicode, quotes, ampersands
- Verify: Accepted and displayed correctly
Regression Tests
Ensure No Breaking Changes
-
Session creation still works:
POST /session/createreturns expected response- No proxy fields in response
-
Regular voting still works (non-proxy case):
- Users without proxy can still vote normally
- Vote structure unchanged
-
Results endpoints unchanged:
/events/{id}/resultsreturns same structure- (Proxy data is supplementary in vote metadata)
-
Admin endpoints unchanged:
- Session status checks work
- Event start/end unchanged
Debugging Commands
Check instance count for user
SELECT COUNT(*) FROM user_sessionWHERE user_id = ? AND session_id = ? AND join_left = 'Joined'Check proxy assignments
SELECT user_id, proxy FROM user_sessionWHERE session_id = ? AND join_left = 'Joined'ORDER BY user_idCheck votes cast
SELECT user_session_id, data FROM voteWHERE event_id = ?ORDER BY user_session_idVerify attendance endpoint
curl http://localhost:8000/session/ABC123/attendance \ -H "Cookie: <auth_cookie>"Test proxy endpoint directly
curl -X POST http://localhost:8000/session/ABC123/proxy \ -H "Content-Type: application/json" \ -H "Cookie: <auth_cookie>" \ -d '{"is_senator": true, "proxy_for": "Test Name"}'Expected Behavior Matrix
| User Type | Input | Expected Instances | Base | Proxy | Notice |
|---|---|---|---|---|---|
| Senator | No proxy | 1 | ✓ | - | “You now have 1 vote instance.” |
| Senator | Proxy “Jane” | 2 | ✓ | ✓ | “You now have 2 vote instances…” |
| Non-senator | No proxy | 0 | - | - | “You currently have 0 vote instances.” |
| Non-senator | Proxy “Jane” | 1 | - | ✓ | “You now have 1 proxy vote instance.” |
Cleanup After Testing
# Clear all sessions (if needed)DELETE FROM vote;DELETE FROM user_session;DELETE FROM session;
# Or reset database# (Depends on your DB setup/teardown strategy)Sign-Off Checklist
After all tests pass:
- Non-senator no-proxy test OK
- Non-senator proxy test OK
- Senator no-proxy test OK
- Senator proxy test OK
- Idempotency test OK
- Configuration change test OK
- Senator→Non-senator change test OK
- Host attendance view test OK
- Whitespace handling test OK
- Empty proxy name test OK
- Full voting flow test OK
- Regression tests OK
- Code compiles without errors
- Documentation is accurate