QA Test Scenarios
Status
Section titled “Status”This document is FINAL and ENFORCEABLE for QA, UAT, and access-control validation.
It converts the architecture, access matrix, middleware, cache rules, admin flows, and migration assumptions into concrete test scenarios.
It complements:
2.4.-Access-Control-Integration.md2.5.-Access-Matrix.md2.6.-Middleware-Implementation.md2.7.-Access-Caching-Strategy.md2.8.-Admin-Platform-Spec.md2.9.-Migration-Plan-Mongo-to-Auth-Core.md3.-Frontend-Implementation.md3.1.-Frontend-Tasks-Breakdown.md
1. Purpose
Section titled “1. Purpose”This document defines:
- test cases
- expected responses
- expected UI outcomes
- edge cases
- failure behavior
- regression scenarios
It is designed so QA does not need to infer the access model from architecture docs.
2. Core access rule under test
Section titled “2. Core access rule under test”A tenant-scoped request is allowed only if all are true:
- JWT is valid
x-orgis present and valid- user has valid membership in the company
- company owns the module
- membership is granted the module
- membership has the required permission
- Auth access resolution is available
3. Expected status code contract
Section titled “3. Expected status code contract”| Status | Meaning | Typical QA interpretation |
|---|---|---|
| 200 / 201 | Success | Request allowed |
| 400 | Bad request | Missing or invalid request context, usually x-org |
| 401 | Unauthorized | Missing/invalid/expired token |
| 403 | Forbidden | Authenticated, but access denied |
| 404 | Not found | Resource not found or intentionally hidden depending on endpoint policy |
| 409 | Conflict | Business conflict, not access issue |
| 503 | Service unavailable | Auth/Core/dependency not available for access resolution |
4. Test data setup requirements
Section titled “4. Test data setup requirements”QA should have the following reusable test fixtures.
4.1 Companies
Section titled “4.1 Companies”Create at minimum:
- Company A → Basic + Finance + Market
- Company B → Finance only
- Company C → Basic only
- Company D → no active entitlements
- Company E → Venue + AI only
4.2 Users / memberships
Section titled “4.2 Users / memberships”Create at minimum:
- Platform Admin User
- Tenant Superadmin User
- Admin User
- Finance User
- Manager User
- Submitter/User
- User with no membership
- Vendor User (if vendor routes tested)
4.3 Membership access patterns
Section titled “4.3 Membership access patterns”At minimum, prepare:
- User 1 → Company A → Basic + Finance + Market
- User 2 → Company A → Finance only
- User 3 → Company A → Basic only
- User 4 → Company A → Finance permission revoked
- User 5 → Company B → Finance only
- User 6 → Company C → Basic only
- User 7 → Company D → no modules
- User 8 → Company E → Venue only
- User 9 → Company E → AI only
4.4 Permission examples
Section titled “4.4 Permission examples”Use permission keys such as:
basic.dashboard.viewbasic.event.viewbasic.event.createfinance.expense.viewfinance.expense.createfinance.expense.editmarket.contract.viewmarket.contract.approvevenue.calendar.viewai.chat.use
5. Happy path test scenarios
Section titled “5. Happy path test scenarios”5.1 Login → bootstrap → access load
Section titled “5.1 Login → bootstrap → access load”Scenario
Section titled “Scenario”User logs in successfully and frontend loads access.
- login with valid credentials
- receive JWT + refresh token
- call
/auth/me - select company
- call
/auth/me/accesswithx-org
Expected
Section titled “Expected”- login returns 200
/auth/mereturns 200/auth/me/accessreturns 200- frontend receives effective modules and permissions
- module tiles render according to effective access
5.2 Basic module allowed
Section titled “5.2 Basic module allowed”Scenario
Section titled “Scenario”User has Basic through company entitlement and membership grant.
Preconditions
Section titled “Preconditions”- company owns Basic
- membership granted Basic
- user has
basic.dashboard.view
Request
Section titled “Request”GET /api/basic/dashboardAuthorization: Bearer <token>x-org: <companyId>Expected
Section titled “Expected”200- backend executes
- frontend shows Basic module
5.3 Finance module allowed
Section titled “5.3 Finance module allowed”Scenario
Section titled “Scenario”User has Finance entitlement and membership grant.
Preconditions
Section titled “Preconditions”- company owns Finance
- membership granted Finance
- user has
finance.expense.view
Request
Section titled “Request”GET /api/finance/expensesAuthorization: Bearer <token>x-org: <companyId>Expected
Section titled “Expected”200- finance page loads
- create button shown only if
finance.expense.createalso exists
5.4 Standalone module without Basic
Section titled “5.4 Standalone module without Basic”Scenario
Section titled “Scenario”Company has Finance only, no Basic.
Preconditions
Section titled “Preconditions”- company owns Finance only
- membership granted Finance
- no Basic entitlement
Expected
Section titled “Expected”- Core App hidden
- Finance module visible
- finance routes still succeed
- Basic routes return
403
6. JWT / authentication tests
Section titled “6. JWT / authentication tests”6.1 Missing token
Section titled “6.1 Missing token”Request
Section titled “Request”No Authorization header
Expected
Section titled “Expected”- backend returns
401 - frontend redirects to login or shows unauthenticated state
6.2 Invalid token
Section titled “6.2 Invalid token”Request
Section titled “Request”Malformed bearer token
Expected
Section titled “Expected”401- error code indicates unauthorized
- request not executed
6.3 Expired token
Section titled “6.3 Expired token”Expected
Section titled “Expected”- initial request returns
401 - frontend attempts refresh if flow supports it
- if refresh succeeds, request may retry
- if refresh fails, user is logged out
6.4 Wrong issuer / audience
Section titled “6.4 Wrong issuer / audience”Expected
Section titled “Expected”401- request denied before access resolution
7. x-org / company context tests
Section titled “7. x-org / company context tests”7.1 Missing x-org
Section titled “7.1 Missing x-org”Request
Section titled “Request”Tenant request without x-org
Expected
Section titled “Expected”400- error clearly indicates missing
x-org - backend must not infer default company
7.2 Invalid x-org format
Section titled “7.2 Invalid x-org format”Expected
Section titled “Expected”400- request rejected before business logic
7.3 Company switch
Section titled “7.3 Company switch”Scenario
Section titled “Scenario”User switches from Company A to Company B
- current access loaded for Company A
- switch active company
- frontend calls
/auth/me/accessagain with newx-org
Expected
Section titled “Expected”- old access state discarded
- new effective modules loaded
- visible modules update immediately
- subsequent requests carry new
x-org
8. Membership and role tests
Section titled “8. Membership and role tests”8.1 User not in company
Section titled “8.1 User not in company”Preconditions
Section titled “Preconditions”- valid user
- valid token
- user has no membership in selected company
Expected
Section titled “Expected”/auth/me/accessor backend access resolution returns403- backend route returns
403 - frontend shows access denied / company not accessible
8.2 Tenant Superadmin with granted module
Section titled “8.2 Tenant Superadmin with granted module”Expected
Section titled “Expected”- access allowed only if membership has module
- role alone must not auto-grant every module if architecture forbids auto-grant
8.3 Finance role without Finance module grant
Section titled “8.3 Finance role without Finance module grant”Preconditions
Section titled “Preconditions”- user role indicates finance-oriented scope
- company owns Finance
- membership NOT granted Finance
Expected
Section titled “Expected”- Finance module hidden
- Finance route returns
403
8.4 Submitter with explicit Finance grant
Section titled “8.4 Submitter with explicit Finance grant”Preconditions
Section titled “Preconditions”- user is low-role / submitter
- company owns Finance
- membership explicitly granted Finance
- permission exists
Expected
Section titled “Expected”- Finance module allowed
- only permitted actions available
- role does not block basic usage if grant exists
9. Entitlement tests
Section titled “9. Entitlement tests”9.1 Company does not own module
Section titled “9.1 Company does not own module”Preconditions
Section titled “Preconditions”- membership granted module
- company does NOT own module
Expected
Section titled “Expected”- effective access excludes module
- frontend hides module
- backend route returns
403
9.2 Entitlement removed after previous access existed
Section titled “9.2 Entitlement removed after previous access existed”Scenario
Section titled “Scenario”Company previously had Finance, then Finance removed.
- user has cached Finance access
- admin removes Finance add-on
- entitlementVersion changes
- next request happens
Expected
Section titled “Expected”- stale cache not reused
- access rebuilt
- Finance removed from effective modules
- route returns
403 - frontend refresh removes Finance UI
9.3 Basic removed but standalone module remains
Section titled “9.3 Basic removed but standalone module remains”Preconditions
Section titled “Preconditions”- company loses Basic
- company still owns Finance
Expected
Section titled “Expected”- Core App hidden
- Finance remains accessible if membership granted
- Basic endpoints return
403
10. Permission tests
Section titled “10. Permission tests”10.1 Permission allowed
Section titled “10.1 Permission allowed”Preconditions
Section titled “Preconditions”- module allowed
- permission present
Expected
Section titled “Expected”- route returns
200 - UI action visible
10.2 Permission missing
Section titled “10.2 Permission missing”Preconditions
Section titled “Preconditions”- module allowed
- permission absent
Expected
Section titled “Expected”- route returns
403 - UI action hidden if frontend refreshed correctly
10.3 Revoked permission
Section titled “10.3 Revoked permission”Scenario
Section titled “Scenario”Permission existed, then removed.
- user loads page with permission
- admin revokes permission
- accessVersion changes
- user retries action
Expected
Section titled “Expected”- stale access invalidated
- next protected action returns
403 - frontend refresh hides action
This is a critical regression scenario.
10.4 Permission belongs to disabled module
Section titled “10.4 Permission belongs to disabled module”Preconditions
Section titled “Preconditions”- permission exists in raw assignment
- module not effective
Expected
Section titled “Expected”- permission must not grant access by itself
- backend denies route with
403
11. Delegation / admin-management tests
Section titled “11. Delegation / admin-management tests”11.1 Superadmin grants allowed module
Section titled “11.1 Superadmin grants allowed module”Expected
Section titled “Expected”- success
- accessVersion changes
- affected user cache invalidated
11.2 Admin tries to grant module outside delegation scope
Section titled “11.2 Admin tries to grant module outside delegation scope”Expected
Section titled “Expected”403- no DB change
- no accessVersion bump for target user
11.3 Manager tries to grant unauthorized permission
Section titled “11.3 Manager tries to grant unauthorized permission”Expected
Section titled “Expected”403- audit entry created if policy requires
- no change persisted
11.4 User without delegation attempts user-management endpoint
Section titled “11.4 User without delegation attempts user-management endpoint”Expected
Section titled “Expected”403
12. Cache tests
Section titled “12. Cache tests”12.1 Cache hit
Section titled “12.1 Cache hit”Preconditions
Section titled “Preconditions”- identical user/company/version inputs
- access already cached
Expected
Section titled “Expected”- Auth returns cached access successfully
- response still correct
- no stale mismatch
12.2 tokenVersion change invalidates cache
Section titled “12.2 tokenVersion change invalidates cache”- user has cached access
- tokenVersion increases (logout-all / session reset)
- same route requested
Expected
Section titled “Expected”- old key not reused
- access rebuilt
- request succeeds only if new session valid
12.3 entitlementVersion change invalidates cache
Section titled “12.3 entitlementVersion change invalidates cache”- company entitlement changes
- entitlementVersion increments
- request executed
Expected
Section titled “Expected”- cache miss / rebuild
- new access reflects entitlement change
12.4 accessVersion change invalidates cache
Section titled “12.4 accessVersion change invalidates cache”- membership permissions change
- accessVersion increments
- request executed
Expected
Section titled “Expected”- old cached access not reused
- rebuilt access reflects new grants
12.5 Redis unavailable but recompute succeeds
Section titled “12.5 Redis unavailable but recompute succeeds”Expected
Section titled “Expected”- request still succeeds
- no incorrect 403/503 if recomputation is possible
12.6 Redis unavailable and recompute fails
Section titled “12.6 Redis unavailable and recompute fails”Expected
Section titled “Expected”503- request denied
- fail-closed respected
13. Dependency failure tests
Section titled “13. Dependency failure tests”13.1 Auth unavailable
Section titled “13.1 Auth unavailable”Expected
Section titled “Expected”- protected backend routes return
503 - no optimistic allow
- frontend shows temporary unavailable state
This is a required edge case.
13.2 Core unavailable during access resolution
Section titled “13.2 Core unavailable during access resolution”Expected
Section titled “Expected”- Auth cannot resolve entitlements
- Auth returns failure
- backend returns
503 - frontend shows retry state
This is a required edge case.
13.3 Partial malformed response from Auth
Section titled “13.3 Partial malformed response from Auth”Expected
Section titled “Expected”- backend treats access resolution as failed
- returns
503 - no fallback to guessed access
14. Frontend UX tests
Section titled “14. Frontend UX tests”14.1 Module hidden when not effective
Section titled “14.1 Module hidden when not effective”Expected
Section titled “Expected”- module tile absent
- direct route attempt denied or redirected
14.2 Stale UI after revoke
Section titled “14.2 Stale UI after revoke”Scenario
Section titled “Scenario”UI still shows button briefly after revoke.
Expected
Section titled “Expected”- backend action returns
403 - frontend refreshes access
- button disappears
14.3 Company switch rerenders app shell
Section titled “14.3 Company switch rerenders app shell”Expected
Section titled “Expected”- module menu updates
- route guards update
- stale screens are not preserved incorrectly
14.4 Access denied messaging
Section titled “14.4 Access denied messaging”Expected
Section titled “Expected”- clear UX message
- no generic crash
- no misleading “not found” unless intentionally designed
15. Admin platform tests
Section titled “15. Admin platform tests”15.1 Create package
Section titled “15.1 Create package”Expected
Section titled “Expected”- package created in Core
- visible in admin catalog
- no direct user access change until company assigned
15.2 Assign add-on to company
Section titled “15.2 Assign add-on to company”Expected
Section titled “Expected”- entitlementVersion increments
- company cache invalidation triggered
- users see module after next access rebuild only if granted
15.3 Delegation limit change
Section titled “15.3 Delegation limit change”Expected
Section titled “Expected”- accessVersion changes for affected users if model requires it
- UI controls update after refresh
- unauthorized grants now blocked
15.4 Company approval
Section titled “15.4 Company approval”Expected
Section titled “Expected”- approved company becomes active
- users can load company after proper membership/access exists
16. Vendor / non-tenant tests
Section titled “16. Vendor / non-tenant tests”16.1 Vendor route without vendor token
Section titled “16.1 Vendor route without vendor token”Expected
Section titled “Expected”- unauthorized or forbidden according to vendor auth model
16.2 Tenant route with vendor token
Section titled “16.2 Tenant route with vendor token”Expected
Section titled “Expected”- denied
- vendor auth must not satisfy tenant access model
16.3 Public route
Section titled “16.3 Public route”Expected
Section titled “Expected”- no JWT needed
- no
x-orgneeded - protected data not exposed
17. Migration regression tests
Section titled “17. Migration regression tests”17.1 Legacy user mapped to Auth UUID
Section titled “17.1 Legacy user mapped to Auth UUID”Expected
Section titled “Expected”- membership joins work
- access resolution works
- old Mongo identity is no longer required at runtime
17.2 Company mapping after migration
Section titled “17.2 Company mapping after migration”Expected
Section titled “Expected”- canonical company id used in
x-org - no mixed legacy company identifiers in active flows
17.3 Old permission model removed
Section titled “17.3 Old permission model removed”Expected
Section titled “Expected”- access decisions no longer rely on legacy backend-local permission truth
- Auth/Core model is authoritative
18. Suggested test case format
Section titled “18. Suggested test case format”Use this format for execution tracking:
Test Case ID
Section titled “Test Case ID”Example: QA-ACCESS-001
Example: Missing x-org on tenant route returns 400
Preconditions
Section titled “Preconditions”- user exists
- route is tenant-scoped
- send request without
x-org
Expected
Section titled “Expected”- HTTP 400
- no business logic execution
- mapped to section 7.1
19. Example detailed cases
Section titled “19. Example detailed cases”QA-ACCESS-001 — Missing x-org
Section titled “QA-ACCESS-001 — Missing x-org”Preconditions
Section titled “Preconditions”- valid user
- valid token
- tenant route
/api/finance/expenses
- send GET request without
x-org
Expected
Section titled “Expected”- status
400 - body indicates validation error
- no downstream DB logic executed
QA-ACCESS-002 — Revoked permission
Section titled “QA-ACCESS-002 — Revoked permission”Preconditions
Section titled “Preconditions”- user previously had
finance.expense.create - company owns Finance
- membership has Finance
- permission revoked before request
- user clicks Create Expense
- frontend sends request
- backend resolves fresh access
Expected
Section titled “Expected”- status
403 - no create occurs
- frontend refreshes access and hides create action
QA-ACCESS-003 — Entitlement removed
Section titled “QA-ACCESS-003 — Entitlement removed”Preconditions
Section titled “Preconditions”- company previously had Market
- Market entitlement removed
- user still has stale UI state
- user opens Market page or sends Market API request
Expected
Section titled “Expected”- fresh access resolution excludes Market
- status
403 - frontend removes Market module after refresh
QA-ACCESS-004 — Auth down
Section titled “QA-ACCESS-004 — Auth down”Preconditions
Section titled “Preconditions”- backend depends on Auth for access
- Auth unavailable
- send protected request
Expected
Section titled “Expected”- status
503 - request denied
- no optimistic allow
20. Final rule
Section titled “20. Final rule”QA must always validate the full chain:JWT → x-org → membership → entitlement → module → permission → dependency health21. Final one-line summary
Section titled “21. Final one-line summary”A request is only truly valid when identity, company context, entitlements, grants, permissions, and dependency health all succeed together.