Kulcare Docs
Feature QA checklists

Users Management Facility Scoping — QA Guide

Scope: Smoke test for the facility selector on Shell's /account/users page and the new CMS native users page at /settings/users (shipped in #1429).

Prerequisites: Staging access (or local dev with the backend on :3000 and Shell on :3000, CMS lazy-loaded). A multi-facility tenant with at least one active practitioner role at each facility — seed via /account/users invites if missing.

Related: Users management developer guide · Access control overview


1. Test environment setup

1.1 Tenants and facilities

You need:

  • One tenant with two facilities (e.g. "Acme Health" with "Acme Downtown" + "Acme Westside").
  • At least one practitioner with an active PractitionerRole at each facility.
  • At least one staff with an active StaffRole at one of the facilities (for the Staff tab path).
  • Optional: a second tenant with its own facility, to verify cross-tenant facility_id returns 404.

If staging doesn't have this, seed via Shell /account/users → Doctors tab → Invite Practitioner, repeated for each facility.

1.2 Users

Log in as:

  • owner or admin of the multi-facility tenant (to see all three tabs in Shell, and Staff in CMS).
  • For role-gating spot-checks: a practitioner at the tenant (Staff/Stakeholders tabs in Shell should be hidden; CMS only shows Practitioners).

2. Shell — /account/users

2.1 Facility selector renders for multi-facility tenants

  1. Navigate to /account/users.
  2. Verify: a "Facility" dropdown appears above the four stats cards.
  3. Verify: dropdown contains "All Facilities" (default) plus one entry per facility in the tenant.

2.2 "All Facilities" default — tenant-wide view

  1. With "All Facilities" selected, verify:
    • Stats cards show counts across the entire tenant.
    • Doctors tab lists practitioners from every facility.
    • Pending invites (visible at the bottom of Doctors / Staff tabs) include invites from any facility.

2.3 Specific facility — scoped view

  1. Switch the selector to Facility A. Verify:
    • Stats drop to Facility A counts.
    • Doctors tab lists only practitioners with an active role at Facility A.
    • If you're on a later page of results, pagination resets to page 1.
  2. Switch to Facility B. Verify the same pattern with a different roster.

2.4 Stakeholders tab when facility is selected

  1. With Facility A selected, click the Stakeholders tab. Verify: empty list (stakeholders are tenant-scoped, not facility-scoped; backend returns [] for role=stakeholder&facility_id=...).

2.5 Role-gating (spot check)

  1. Log out, log in as a practitioner. Navigate to /account/users. Verify: Staff and Stakeholders tabs are hidden — only Doctors is visible.

3. CMS — /settings/users

3.1 Page loads natively, not a redirect

  1. Log back in as owner/admin. Enter the CMS app, ensure the active facility is Facility A.
  2. Navigate to /settings/users (Settings → Users in the sidebar). Verify:
    • The URL stays at /cms/settings/users (does NOT redirect to /account/users).
    • The page renders with two tabs: Practitioners and Staff. There is no Stakeholders tab — stakeholders are tenant-scoped and managed in Shell only.
    • Stats cards reflect Facility A counts (not tenant-wide).

3.2 Tab clicks stay inside CMS

  1. Click the Staff tab. Verify:
    • URL becomes /cms/settings/users/staff (NOT /account/users/staff — a regression bug we shipped + fixed during QA).
    • You stay on the CMS page; the Shell layout does not take over.
  2. Click back to Practitioners. URL becomes /cms/settings/users/practitioners. Same containment.

3.3 Facility switch updates the view

  1. Via the CMS clinic switcher (or Shell facility switcher, then re-enter CMS), switch the active facility to Facility B.
  2. Re-open /settings/users. Verify: Practitioners + Staff lists now reflect Facility B's roster, not Facility A.

3.4 No-facility guard

  1. If the test account has any state where the JWT lacks currentFacilityId (rare; usually only mid-switch or for a tenant with no facilities), /settings/users should render the "Select a facility from the clinic switcher to see users here" message instead of loading data.
  2. Verify in DevTools network tab: when the guard is showing, no /identity/users or /identity/practitioner_invites or /identity/staff_invites request fires.

3.5 Role-gating

  1. As a practitioner at the tenant, navigate to /settings/users. Verify:
    • Only the Practitioners tab is visible.
    • No Staff tab.

4. Backend API spot-checks

Optional but useful for confirming the query shape is right. Use the clinician JWT.

4.1 Users endpoint

# Practitioners at a specific facility
curl -H "Authorization: Bearer $CLINICIAN_TOKEN" \
  "$API_BASE/identity/users?role=practitioner&facility_id=$FACILITY_A_ID"

# Owner/admin with facility_id → empty list (tenant-scoped role)
curl -H "Authorization: Bearer $CLINICIAN_TOKEN" \
  "$API_BASE/identity/users?role=admin&facility_id=$FACILITY_A_ID"
# Expect: { "data": [], ... }

# Cross-tenant facility_id → empty list (defense in depth)
curl -H "Authorization: Bearer $CLINICIAN_TOKEN" \
  "$API_BASE/identity/users?role=practitioner&facility_id=$OTHER_TENANT_FACILITY_ID"
# Expect: { "data": [], ... }

4.2 Invites endpoints

# Default — current_facility from JWT
curl -H "Authorization: Bearer $CLINICIAN_TOKEN" \
  "$API_BASE/identity/practitioner_invites?status=pending"

# Specific facility
curl -H "Authorization: Bearer $CLINICIAN_TOKEN" \
  "$API_BASE/identity/practitioner_invites?facility_id=$FACILITY_A_ID&status=pending"

# Tenant-wide aggregation
curl -H "Authorization: Bearer $CLINICIAN_TOKEN" \
  "$API_BASE/identity/practitioner_invites?all_facilities=true&status=pending"

# Cross-tenant facility_id → 404
curl -i -H "Authorization: Bearer $CLINICIAN_TOKEN" \
  "$API_BASE/identity/practitioner_invites?facility_id=$OTHER_TENANT_FACILITY_ID"
# Expect: HTTP/1.1 404 Not Found

Same shape for staff_invites — substitute the path.


5. Regression checks (single-facility tenant)

A single-facility tenant must keep working without showing a no-op selector.

  1. Log in as owner of a tenant with exactly one facility.
  2. Navigate to /account/users. Verify:
    • No facility dropdown — the selector is hidden, not disabled.
    • Stats and lists show normally.
  3. Navigate to /cms/settings/users. Verify:
    • Page loads, tabs visible, scoped to the one facility.

6. Network-tab sanity (CMS)

In DevTools → Network, with /cms/settings/users loaded and Facility A active:

  • Every /identity/users request: query string includes facility_id=<facility-a-uuid>.
  • Every /identity/practitioner_invites and /identity/staff_invites request: query string includes facility_id=<facility-a-uuid>.
  • No request fires with all_facilities=true or without facility_id — CMS is facility-locked, never tenant-wide.

7. Sign-off checklist

  • §2.1–2.5: Shell facility selector works (default, switch, tab visibility, role-gating).
  • §3.1–3.5: CMS native page works (loads, tab clicks stay in /cms, facility switch updates, guard renders, role-gating).
  • §4: Backend endpoints behave per spec (specific facility, tenant-wide aggregation, cross-tenant 404, owner/admin returns empty).
  • §5: Single-facility tenant has no selector and works normally.
  • §6: CMS network tab shows facility-scoped requests only.

If anything fails, file a follow-up with the failing step number and a screenshot / cURL output.

On this page