# OCDP Test Scenarios > **Platform**: OCDP (Open Cloud Deployment Platform) > **Deployed at**: http://10.6.80.114:18080 > **Scope**: Full-stack test scenarios covering authentication, configuration, artifact browser, instance lifecycle, monitoring, user management, multi-tenancy, UI/UX, data persistence, security, and edge cases. --- ## Category 1: Authentication & Authorization (25+ cases) ### AUTH-001 — Login with valid credentials | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Admin account exists in the system | | **Steps** | 1. Navigate to `/`
2. Enter valid username and password
3. Click "Login" | | **Expected Result** | User is authenticated, redirected to `/home`, token stored in localStorage/session, toast "Welcome, [username]!" displayed | ### AUTH-002 — Login with incorrect password | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Valid username exists | | **Steps** | 1. Enter valid username with wrong password
2. Click "Login" | | **Expected Result** | Login fails with 401 error, red error message displayed, user stays on login page | ### AUTH-003 — Login with non-existent username | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | None | | **Steps** | 1. Enter username that does not exist
2. Enter any password
3. Click "Login" | | **Expected Result** | 401 returned, error message shown, no user enumerated | ### AUTH-004 — Login with empty credentials | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | None | | **Steps** | 1. Leave username and password empty
2. Click "Login" | | **Expected Result** | HTML5 form validation prevents submission, or backend returns validation error | ### AUTH-005 — Login with special characters in username | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | None | | **Steps** | 1. Enter username with SQL injection patterns: `admin' OR '1'='1`
2. Enter password
3. Click "Login" | | **Expected Result** | Login fails, no SQL injection succeeds, no data leak | ### AUTH-006 — Successful login response contains expected fields | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Valid credentials | | **Steps** | 1. Call `POST /api/v1/auth/login`
2. Inspect response body | | **Expected Result** | Response contains `accessToken`, `refreshToken`, `username`, `role`, `permissions`, `userId`, `workspaceId` | ### AUTH-007 — JWT token sent in Authorization header | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Valid token obtained | | **Steps** | 1. Capture XHR request to any protected API
2. Inspect Authorization header | | **Expected Result** | Header contains `Bearer ` | ### AUTH-008 — Access protected route without token | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Clear all auth tokens | | **Steps** | 1. Navigate directly to `/home`
2. Navigate to `/artifact/instances`
3. API call to `/api/v1/clusters` without token | | **Expected Result** | Frontend redirects to `/`, backend returns 401 | ### AUTH-009 — Access protected API without token | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | None | | **Steps** | 1. Call `GET /api/v1/clusters` without Authorization header | | **Expected Result** | 401 Unauthorized returned | ### AUTH-010 — Token expiry handling | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Use a token near expiry or manipulate expiry | | **Steps** | 1. Make API call with expired token | | **Expected Result** | Backend returns 401, frontend should redirect to login page or attempt token refresh | ### AUTH-011 — Token refresh flow | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Valid refresh token exists | | **Steps** | 1. Call `POST /api/v1/auth/refresh` with valid refresh token
2. Call with expired/invalid refresh token | | **Expected Result** | Valid refresh returns new access token; invalid returns 401 | ### AUTH-012 — Logout behavior | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | User is logged in | | **Steps** | 1. Click logout/sign out button
2. Try to navigate to previously visited protected page | | **Expected Result** | Token cleared from storage, redirected to login page, protected routes inaccessible | ### AUTH-013 — Logout clears token from localStorage | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | User is logged in | | **Steps** | 1. Inspect localStorage for auth tokens after login
2. Logout
3. Inspect localStorage again | | **Expected Result** | Tokens removed after logout | ### AUTH-014 — Role-based page access: admin | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Admin user logged in | | **Steps** | 1. Navigate to `/configuration/users`
2. Navigate to `/configuration/clusters`
3. Navigate to `/artifact/instances` | | **Expected Result** | All pages accessible | ### AUTH-015 — Role-based page access: regular user | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Regular user logged in (non-admin) | | **Steps** | 1. Navigate to `/configuration/users`
2. Navigate to `/admin` | | **Expected Result** | Redirected to `/forbidden` or access denied page | ### AUTH-016 — Regular user can access own resources pages | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Regular user logged in | | **Steps** | 1. Navigate to `/home`
2. Navigate to `/configuration/clusters`
3. Navigate to `/configuration/registries`
4. Navigate to `/artifact/registries`
5. Navigate to `/artifact/instances` | | **Expected Result** | All pages accessible (user sees own resources) | ### AUTH-017 — Login page redirect when already authenticated | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | User is logged in | | **Steps** | 1. Navigate to `/`
2. Observe behavior | | **Expected Result** | Redirected to `/home` instead of showing login form | ### AUTH-018 — Login page UI elements | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Not authenticated | | **Steps** | 1. Observe login page
2. Check for OCDP Console branding, username input, password input, Login button | | **Expected Result** | Page displays brand icon, "OCDP Console" title, username/password fields with correct autocomplete attributes, Login button | ### AUTH-019 — Login button loading state | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | None | | **Steps** | 1. Enter credentials and click Login
2. Observe button state during API call | | **Expected Result** | Button shows spinner/loading state, text changes to "Logging in...", button disabled during request | ### AUTH-020 — Login error display | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | None | | **Steps** | 1. Enter wrong credentials and submit
2. Observe error message | | **Expected Result** | Red error text appears below the login button, message is user-friendly (not a raw stack trace) | ### AUTH-021 — Password change flow (mustChangePassword) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | User created with `mustChangePassword: true` | | **Steps** | 1. Login as that user
2. Observe redirect/behavior
3. Change password
4. Login again with new password | | **Expected Result** | First login forces password change, old password rejected after change | ### AUTH-022 — Refresh token expiry logout | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Both access and refresh tokens expired | | **Steps** | 1. Wait for full token expiry
2. Make any API call that triggers refresh | | **Expected Result** | User is logged out, redirected to login page | ### AUTH-023 — Concurrent login sessions | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | User account exists | | **Steps** | 1. Login in browser tab 1
2. Login in browser tab 2 with same credentials
3. Perform operations in both tabs | | **Expected Result** | Both sessions work independently, no cross-tab interference | ### AUTH-024 — Admin login shows "Admin only" badge on User Management | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Admin logged in | | **Steps** | 1. Navigate to `/configuration/users`
2. Check for admin badge | | **Expected Result** | "Admin only" badge visible in the User Management page header | ### AUTH-025 — Token manipulation (tampered JWT) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Get valid token, modify its payload | | **Steps** | 1. Decode JWT, change `role` to "admin" for a regular user token
2. Re-encode with modified payload and send API request | | **Expected Result** | Backend rejects tampered token (signature verification fails), returns 401 | --- ## Category 2: Cluster CRUD (15+ cases) ### CLU-001 — Create cluster with all required fields | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Logged in as admin/user with cluster permissions | | **Steps** | 1. Navigate to `/configuration/clusters`
2. Click "Add Cluster"
3. Fill in name, API Server URL, CA cert, client cert, client key
4. Click "Save" | | **Expected Result** | Cluster created successfully, success toast shown, cluster appears in the list | ### CLU-002 — Create cluster with token auth | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Logged in | | **Steps** | 1. Click "Add Cluster"
2. Fill name, API Server URL, Bearer Token (leave cert fields empty)
3. Click "Save" | | **Expected Result** | Cluster created using token authentication | ### CLU-003 — Create cluster with empty name | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Create modal open | | **Steps** | 1. Leave name empty
2. Fill all other required fields
3. Click "Save" | | **Expected Result** | Validation error "Cluster name is required" displayed near the name field | ### CLU-004 — Create cluster with invalid API Server URL | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Create modal open | | **Steps** | 1. Enter name
2. Enter invalid URL (e.g., `not-a-url`, `ftp://...`)
3. Click "Save" | | **Expected Result** | Validation error "Invalid URL format" displayed | ### CLU-005 — Create cluster without auth credentials | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Create modal open | | **Steps** | 1. Enter name and URL
2. Leave all cert/key/token fields empty
3. Click "Save" | | **Expected Result** | Validation errors on CA/Client Cert/Client Key fields | ### CLU-006 — Edit cluster name and URL | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Existing cluster present | | **Steps** | 1. Click edit on existing cluster
2. Change name and host
3. Click "Save" | | **Expected Result** | Cluster updated, changes reflected in list | ### CLU-007 — Edit cluster with new certificate (overwrite) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Existing cluster with cert auth | | **Steps** | 1. Edit cluster
2. Enter new CA cert, client cert, client key in the "new" fields
3. Click "Save" | | **Expected Result** | Certificate updated, "hasCaData" still appears as configured | ### CLU-008 — Edit cluster leaving cert fields empty (no change) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Existing cluster with cert auth | | **Steps** | 1. Edit cluster
2. Leave the "new" cert fields empty
3. Click "Save" | | **Expected Result** | Cluster updated, existing certs retained | ### CLU-009 — Delete cluster | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Existing cluster with no running instances (or expected behavior defined) | | **Steps** | 1. Click delete icon on a cluster
2. Confirm deletion in browser confirm dialog | | **Expected Result** | Cluster removed from list, success toast shown | ### CLU-010 — Delete cluster cancellation | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Existing cluster | | **Steps** | 1. Click delete on a cluster
2. Click "Cancel" in the confirmation dialog | | **Expected Result** | Cluster not deleted, still visible in the list | ### CLU-011 — Health check on reachable cluster | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | A reachable Kubernetes cluster configured | | **Steps** | 1. Click health check / test button on the cluster row | | **Expected Result** | Success toast with connection healthy message | ### CLU-012 — Health check on unreachable cluster | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Cluster with invalid host/cert configured | | **Steps** | 1. Click health check / test button on the cluster | | **Expected Result** | Error toast with connection failure message | ### CLU-013 — Empty clusters state | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | No clusters configured | | **Steps** | 1. Navigate to `/configuration/clusters` | | **Expected Result** | Empty state message displayed, add cluster action available | ### CLU-014 — Cluster list with multiple clusters | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | 3+ clusters configured | | **Steps** | 1. Navigate to `/configuration/clusters`
2. Scroll list | | **Expected Result** | All clusters listed with name, URL, status indicators | ### CLU-015 — Cluster description display in list | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Cluster with description exists | | **Steps** | 1. View cluster list
2. Check if description is visible | | **Expected Result** | Description shown as subtitle or tooltip in the cluster row | ### CLU-016 — Cluster CRUD as regular user | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Regular user logged in | | **Steps** | 1. Create a new cluster
2. Edit the cluster
3. Delete the cluster | | **Expected Result** | User can manage their own clusters, or see appropriate empty/permission state | ### CLU-017 — Cluster form modal close/reset | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Create modal open with partially filled form | | **Steps** | 1. Fill partial data
2. Click Cancel | | **Expected Result** | Modal closes, form data cleared when reopened | --- ## Category 3: Registry CRUD (15+ cases) ### REG-001 — Create registry with all required fields | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Logged in | | **Steps** | 1. Navigate to `/configuration/registries`
2. Click "Add Registry"
3. Fill name, URL, username, password
4. Click "Save" | | **Expected Result** | Registry created, success toast shown, appears in list | ### REG-002 — Create registry with insecure flag | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Logged in | | **Steps** | 1. Open add registry modal
2. Fill required fields
3. Check "Allow insecure connection"
4. Click "Save" | | **Expected Result** | Registry created with `insecure: true`, works for HTTP/self-signed registries | ### REG-003 — Create registry without name | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Create modal open | | **Steps** | 1. Leave name empty
2. Fill other fields
3. Click "Save" | | **Expected Result** | HTML5 form validation prevents submission (required attribute) | ### REG-004 — Create registry with invalid URL | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Create modal open | | **Steps** | 1. Enter non-URL string for URL field (type=url)
2. Fill other fields
3. Click "Save" | | **Expected Result** | HTML5 form validation prevents submission (type=url validation) | ### REG-005 — Test registry connection | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Saved registry exists, it's reachable | | **Steps** | 1. Edit an existing registry
2. Click "Test Connection" button | | **Expected Result** | Connection test runs, success/error toast based on connectivity | ### REG-006 — Test registry connection without saving first | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Creating new registry (unsaved) | | **Steps** | 1. Fill registry form but do not save
2. Check if "Test Connection" is available | | **Expected Result** | "Test Connection" button is not shown (only visible for saved registries) | ### REG-007 — Edit registry name and URL | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Existing registry | | **Steps** | 1. Edit a registry
2. Change its name and URL
3. Save | | **Expected Result** | Registry updated, changes reflected | ### REG-008 — Edit registry with new password | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Existing registry with password set | | **Steps** | 1. Edit registry
2. Enter new password in the "New Password" field
3. Save | | **Expected Result** | Password updated, "hasPassword" indicator shows as configured | ### REG-009 — Edit registry leaving password empty | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Existing registry | | **Steps** | 1. Edit registry
2. Leave new password field empty
3. Save | | **Expected Result** | Registry updated, existing password retained | ### REG-010 — Delete registry | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Existing registry with no active dependencies | | **Steps** | 1. Click delete on a registry
2. Confirm deletion | | **Expected Result** | Registry removed from list, success toast | ### REG-011 — Delete registry with existing instances | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Registry has active instances deployed from it | | **Steps** | 1. Try to delete registry that has active instances deriving from it | | **Expected Result** | Backend should return error preventing deletion, or handle cascading gracefully | ### REG-012 — Empty registries state | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | No registries configured | | **Steps** | 1. Navigate to `/configuration/registries` | | **Expected Result** | Empty state message displayed | ### REG-013 — Registry toggle insecure flag | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Existing registry | | **Steps** | 1. Edit registry
2. Toggle insecure checkbox
3. Save | | **Expected Result** | Insecure flag updated | ### REG-014 — Registry list display | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Multiple registries exist | | **Steps** | 1. View the registries page
2. Check each row | | **Expected Result** | Each registry shows name, URL, username, insecure badge (if enabled) | ### REG-015 — Registry CRUD as regular user | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Regular user logged in | | **Steps** | 1. Create a new registry
2. Edit the registry
3. Delete the registry | | **Expected Result** | User can manage their own registries | --- ## Category 4: Chart Browser / Launch Instance (20+ cases) ### CHT-001 — Browse registries in chart browser | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Registries configured with Helm charts | | **Steps** | 1. Navigate to `/artifact/registries`
2. Observe left panel | | **Expected Result** | Registries listed with expand/collapse toggle, count badge | ### CHT-002 — Expand registry tree and list repositories | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Registry has chart repositories | | **Steps** | 1. Click on a registry to expand it
2. Observe repositories listed underneath | | **Expected Result** | Repositories displayed as clickable items, each showing name | ### CHT-003 — Empty repository list message | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Registry exists but has no chart repositories | | **Steps** | 1. Expand registry
2. Observe sub-items | | **Expected Result** | "No chart repositories found." message shown | ### CHT-004 — Select repository and view artifacts | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Repository with chart artifacts exists | | **Steps** | 1. Click on a repository in the left panel
2. Observe right panel | | **Expected Result** | Repository name displayed in header, artifact tags shown as cards | ### CHT-005 — Filter artifacts by Charts / All tags | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Repository has both chart and non-chart artifacts | | **Steps** | 1. Select a repository
2. Click "Charts" filter button
3. Click "All tags" filter button | | **Expected Result** | "Charts" filter shows only chart artifacts, "All tags" shows all | ### CHT-006 — Filter toggle active state | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Repository selected | | **Steps** | 1. Toggle between Charts and All tags | | **Expected Result** | Active filter button has blue highlight, inactive has default styling | ### CHT-007 — Tag card displays correct info | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Artifact loaded | | **Steps** | 1. Observe a tag card | | **Expected Result** | Card shows tag name, artifact type badge (chart/image), repository path, size | ### CHT-008 — Launch button visible only for chart tags | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Chart and non-chart artifacts exist | | **Steps** | 1. Observe a chart tag card
2. Observe a non-chart tag card | | **Expected Result** | Chart tag card has blue "Launch" button; non-chart card does not | ### CHT-009 — Copy pull command from tag card | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Tag card displayed | | **Steps** | 1. Click "Copy" on a tag card | | **Expected Result** | Helm pull command copied to clipboard, success toast shown | ### CHT-010 — Search registries/repositories | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Multiple registries with repositories exist | | **Steps** | 1. Type in the search box in the left panel
2. Observe filtering | | **Expected Result** | List filters to matching registries and repositories; non-matching entries hidden | ### CHT-011 — Open Launch modal | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Chart tag selected | | **Steps** | 1. Click "Launch" on a chart tag | | **Expected Result** | Launch modal opens with repository:tag header, cluster selector, instance name, namespace, values options | ### CHT-012 — Launch modal loads clusters | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Clusters exist in the system | | **Steps** | 1. Open Launch modal
2. Observe cluster dropdown | | **Expected Result** | Cluster dropdown populated with available clusters | ### CHT-013 — Launch modal: no clusters available | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | No clusters configured | | **Steps** | 1. Open Launch modal
2. Observe cluster section | | **Expected Result** | Warning message "No clusters available. Please add a cluster first." displayed, Launch button disabled | ### CHT-014 — Launch modal: instance name validation | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Launch modal open with cluster selected | | **Steps** | 1. Leave instance name empty
2. Click Launch | | **Expected Result** | Toast error "Instance name is required" | ### CHT-015 — Launch modal: namespace validation | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Cluster with namespace policy configured | | **Steps** | 1. Select a disallowed namespace (not in allowedNamespaces)
2. Click Launch | | **Expected Result** | Toast error "Selected namespace is not allowed for this cluster." | ### CHT-016 — Launch modal: Quick / Form / YAML input modes | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Launch modal open | | **Steps** | 1. Click each mode button (Quick, Guided, YAML)
2. Observe content changes | | **Expected Result** | Quick: info panel about chart defaults. Guided: schema form (if schema exists). YAML: textarea for YAML input. Active mode highlighted. | ### CHT-017 — Launch modal: YAML validation | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | YAML input mode selected | | **Steps** | 1. Enter invalid YAML (e.g., `key: [invalid`)
2. Observe error state | | **Expected Result** | Red error text below textarea, Launch button disabled | ### CHT-018 — Launch modal: Load Defaults from values.yaml | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Chart has values.yaml with defaults | | **Steps** | 1. Switch to YAML mode
2. Click "Load Defaults from values.yaml" | | **Expected Result** | values.yaml content loaded into the textarea | ### CHT-019 — Submit launch and navigate to instances | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | All required fields filled with valid data | | **Steps** | 1. Fill cluster, instance name, namespace
2. Click Launch
3. Wait for redirect | | **Expected Result** | Instance creation API called, success toast, redirected to `/artifact/instances`, instance shown with "Pending Install" status | ### CHT-020 — Launch modal: namespace controlled by workspace policy | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Cluster has namespace readOnly policy | | **Steps** | 1. Open Launch modal
2. Select cluster with readonly namespace policy
3. Check namespace field | | **Expected Result** | Namespace field is disabled with blue info message: "Namespace is controlled by your workspace policy." | ### CHT-021 — Launch modal: namespace dropdown (allowed namespaces) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Cluster has allowedNamespaces configured | | **Steps** | 1. Select such cluster
2. Observe namespace field | | **Expected Result** | Namespace becomes a dropdown with only allowed values | ### CHT-022 — Launch modal: user's default cluster pre-selected | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | User has defaultClusterId set | | **Steps** | 1. Open Launch modal | | **Expected Result** | Default cluster auto-selected in the dropdown | --- ## Category 5: Instance Management (20+ cases) ### INS-001 — View instances (all clusters) | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Instances exist across clusters | | **Steps** | 1. Navigate to `/artifact/instances` | | **Expected Result** | All instances listed grouped by cluster, stats cards show totals | ### INS-002 — Filter instances by cluster | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Multiple clusters with instances | | **Steps** | 1. Navigate to instances page
2. Select a specific cluster from dropdown | | **Expected Result** | Only instances from that cluster displayed | ### INS-003 — Instance status: Deployed | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Instance in deployed state | | **Steps** | 1. Look for a deployed instance card | | **Expected Result** | Green "DEPLOYED" badge with checkmark icon, status reason shown | ### INS-004 — Instance status: Failed | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Instance in failed state | | **Steps** | 1. Look for a failed instance card | | **Expected Result** | Red "FAILED" badge, error details visible (lastError section appears) | ### INS-005 — Instance status: Pending (Install/Upgrade/Rollback/Delete) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Instance in transition state | | **Steps** | 1. Look for pending instance card | | **Expected Result** | Amber/yellow "PENDING INSTALL/UPGRADE/ROLLBACK/DELETE" badge | ### INS-006 — Instance status: Unknown | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Instance with unknown status | | **Steps** | 1. Look for unknown instance card | | **Expected Result** | Gray "UNKNOWN" badge | ### INS-007 — Refresh instance status | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Instance exists | | **Steps** | 1. Click "Refresh" button on the instance card | | **Expected Result** | Instance status re-fetched, card updates with latest status | ### INS-008 — Instance card displays metadata correctly | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Instance exists | | **Steps** | 1. Examine instance card content | | **Expected Result** | Card shows: instance name, repository, version tag, namespace, revision, launch date, status reason | ### INS-009 — Instance action buttons visibility | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Instance exists | | **Steps** | 1. Check the action bar at bottom of instance card | | **Expected Result** | Five buttons visible: Refresh, Entries, Diagnostics, Modify, Delete | ### INS-010 — View entries (Services) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Deployed instance with services | | **Steps** | 1. Click "Entries" on the instance card
2. Observe modal | | **Expected Result** | Modal shows Services with name, type, cluster IP, ports; source badge visible | ### INS-011 — View entries (Ingresses) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Deployed instance with ingresses | | **Steps** | 1. Open entries modal
2. Check for Ingresses section | | **Expected Result** | Ingresses listed with host, paths, TLS status | ### INS-012 — View diagnostics (Describe) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Deployed instance | | **Steps** | 1. Click "Diagnostics" on instance card
2. Observe Describe tab | | **Expected Result** | Modal shows Pods (with status, node, restarts, containers) and Services summary | ### INS-013 — View diagnostics (Events) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Deployed instance | | **Steps** | 1. Open diagnostics
2. Click "Events" tab | | **Expected Result** | Kubernetes events listed with type badge, reason, message, timestamp, count | ### INS-014 — View diagnostics (Pod Logs) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Deployed instance with running pods | | **Steps** | 1. Open diagnostics
2. Click "Pod Logs" tab | | **Expected Result** | Pod logs displayed in dark terminal-style blocks, copy button available | ### INS-015 — Copy pod logs | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Diagnostics with logs loaded | | **Steps** | 1. Open pod logs
2. Click "Copy Logs" | | **Expected Result** | Combined logs copied to clipboard, success toast shown | ### INS-016 — Modify instance version tag | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Deployed instance, new chart version available | | **Steps** | 1. Click "Modify" on instance
2. Change version tag
3. Confirm | | **Expected Result** | Instance upgrade initiated, instance moves to "Pending Upgrade" status | ### INS-017 — Modify instance with values changes | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Deployed instance | | **Steps** | 1. Open modify modal
2. Switch to YAML input
3. Update values
4. Confirm | | **Expected Result** | Instance upgraded with modified values | ### INS-018 — Terminate/delete instance with confirmation | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Deployed instance exists | | **Steps** | 1. Click "Delete" on instance card
2. Confirm in browser dialog | | **Expected Result** | Deletion initiated, instance enters "Pending Delete" status, eventually disappears | ### INS-019 — Terminate instance cancellation | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Deployed instance | | **Steps** | 1. Click "Delete"
2. Click "Cancel" in the confirmation dialog | | **Expected Result** | Instance not deleted, dialog dismissed | ### INS-020 — Empty instances state | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | No instances deployed | | **Steps** | 1. Navigate to `/artifact/instances` | | **Expected Result** | Empty state displayed: "No instances found. Launch your first service instance from Artifact Registries" | ### INS-021 — Instances auto-refresh | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Instances page open | | **Steps** | 1. Stay on instances page
2. Observe network requests for 30+ seconds | | **Expected Result** | Background auto-refresh fires every 30 seconds without user interaction | --- ## Category 6: Cluster Monitoring (10+ cases) ### MON-001 — View cluster health monitoring | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Clusters configured | | **Steps** | 1. Navigate to `/monitoring/clusters` | | **Expected Result** | Cluster monitoring cards displayed with health status badges, metrics grid | ### MON-002 — Stats cards display summary | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | 3+ clusters with varying health | | **Steps** | 1. Navigate to monitoring page | | **Expected Result** | Stats cards show: Total Clusters, Healthy count, Warning count, Error count | ### MON-003 — Monitoring card shows cluster metrics | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Healthy cluster | | **Steps** | 1. Observe a cluster monitoring card | | **Expected Result** | Card shows: cluster name, uptime, node count, pod count, GPU usage, CPU usage bar, memory usage bar, last checked time | ### MON-004 — Expand node details | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Cluster has nodes | | **Steps** | 1. Click "Show Nodes" button on a cluster card
2. Observe node list | | **Expected Result** | Node list expands showing individual node metrics (CPU, memory, GPU per node) | ### MON-005 — Healthy cluster status display | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Cluster is healthy | | **Steps** | 1. Check card header | | **Expected Result** | Green "Healthy" badge, green checkmark icon | ### MON-006 — Error cluster status display | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Cluster is unhealthy/error | | **Steps** | 1. Check card header | | **Expected Result** | Red "Error" badge, red X icon | ### MON-007 — Auto-refresh monitoring | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Monitoring page open | | **Steps** | 1. Stay on page
2. Observe metrics updates over time | | **Expected Result** | Page auto-refreshes every 30 seconds, "Auto-refresh every 30 seconds" text visible | ### MON-008 — Manual refresh | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Monitoring page open | | **Steps** | 1. Click "Refresh" button | | **Expected Result** | Data reloaded, loading state shown during refresh | ### MON-009 — Empty monitoring state | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | No clusters configured | | **Steps** | 1. Navigate to monitoring page | | **Expected Result** | "No Clusters Available" empty state displayed | ### MON-010 — Error state when cluster unreachable | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Cluster monitoring API returns error | | **Steps** | 1. Simulate API failure
2. Observe page | | **Expected Result** | Error state with retry button shown, error message displayed | --- ## Category 7: User Management (Admin) (15+ cases) ### USR-001 — Create user with role "user" | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Admin logged in | | **Steps** | 1. Navigate to `/configuration/users`
2. Fill username, password, role=User
3. Set namespace, default cluster, resource limits
4. Click "Create User" | | **Expected Result** | User created, appears in accounts table | ### USR-002 — Create user with role "admin" | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Admin logged in | | **Steps** | 1. Open create user form
2. Select Role=Admin
3. Fill username and password only (namespace/limits hidden for admin)
4. Click "Create User" | | **Expected Result** | Admin user created, namespace/limits not required, role badge shows "admin" | ### USR-003 — Create user with mustChangePassword | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Create user modal | | **Steps** | 1. Ensure "Require password change after first login" checkbox is checked
2. Create user | | **Expected Result** | User created and must change password on first login | ### USR-004 — Create user without required fields | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Create form open | | **Steps** | 1. Leave username or password empty
2. Click "Create User" | | **Expected Result** | Validation error toast "Username and initial password are required." | ### USR-005 — Edit user resource limits | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Non-admin user exists | | **Steps** | 1. Click "Limits" on a user row
2. Change CPU, Memory, GPU, GPU Mem values
3. Click "Save Limits" | | **Expected Result** | Limits modal closes, success toast, updated values shown in table | ### USR-006 — Toggle user role (user ↔ admin) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | User exists | | **Steps** | 1. Click "Make Admin" on a user row
2. Observe role change
3. Click "Make User" to revert | | **Expected Result** | Role toggled, badge updates, admin users can access all pages after re-login | ### USR-007 — Enable/disable user | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Active user exists | | **Steps** | 1. Click "Disable" on an active user
2. Observe badge change
3. Try to login as that user | | **Expected Result** | Badge changes to "Disabled", disabled user cannot login (returns 401) | ### USR-008 — Delete user | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Non-self user exists | | **Steps** | 1. Click "Delete" on a user
2. Confirm deletion | | **Expected Result** | User removed from table | ### USR-009 — Cannot delete own admin account | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Current admin logged in | | **Steps** | 1. Look at own user row | | **Expected Result** | Delete button is disabled (or not rendered) for the current user | ### USR-010 — Cannot disable own admin account | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Current admin logged in | | **Steps** | 1. Look at own user row
2. Check Disable button state | | **Expected Result** | Disable button is disabled for current user | ### USR-011 — User Management page admin-only badge | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Admin logged in | | **Steps** | 1. Observe page header | | **Expected Result** | "Admin only" badge visible near the title | ### USR-012 — User table displays all columns | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Users exist | | **Steps** | 1. Observe the accounts table | | **Expected Result** | Columns: User (username+email), Role (badge), Status (Active/Disabled), Namespace, Quota (CPU/Mem/GPU), Actions | ### USR-013 — Namespace auto-generation for user | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Creating user with role=user | | **Steps** | 1. Enter username
2. Check namespace field (before user edits it) | | **Expected Result** | Namespace auto-populated as `ocdp-u-` | ### USR-014 — Create user with resource limits | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Create form open, role=User | | **Steps** | 1. Set specific CPU, Memory, GPU, GPU memory limits
2. Create user
3. View user in table | | **Expected Result** | Limits stored and displayed in the quota column | ### USR-015 — User list refresh | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Users page open | | **Steps** | 1. Click "Refresh" button | | **Expected Result** | User list reloaded, loading state shown | --- ## Category 8: Multi-tenancy & Permissions (15+ cases) ### MTN-001 — User A cannot see User B's clusters | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Two regular users (A, B), each with their own cluster | | **Steps** | 1. Login as User A
2. List clusters via API
3. Login as User B
4. List clusters via API | | **Expected Result** | User A sees only their clusters, User B sees only their clusters. No cross-tenant leakage | ### MTN-002 — User A cannot see User B's registries | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Two regular users with separate registries | | **Steps** | 1. List registries as User A
2. List registries as User B | | **Expected Result** | Each user sees only their own registries | ### MTN-003 — User A cannot delete User B's instances | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | User A and B each have instances | | **Steps** | 1. As User A, try to call DELETE on User B's instance | | **Expected Result** | Backend returns 403 Forbidden or 404 Not Found | ### MTN-004 — User A cannot modify User B's instances | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | User B has an instance | | **Steps** | 1. As User A, try to update User B's instance | | **Expected Result** | Backend returns 403 Forbidden | ### MTN-005 — Admin can see all clusters | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Admin user, clusters belonging to multiple users exist | | **Steps** | 1. Login as admin
2. List clusters | | **Expected Result** | Admin sees all clusters across all users | ### MTN-006 — Admin can see all registries | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Admin user, registries belonging to multiple users exist | | **Steps** | 1. Login as admin
2. List registries | | **Expected Result** | Admin sees all registries across all users | ### MTN-007 — Admin can see all instances | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Instances exist across different users | | **Steps** | 1. Login as admin
2. List instances per cluster | | **Expected Result** | Admin sees instances from all users' releases | ### MTN-008 — ResourceQuota enforcement (CPU) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | User with CPU quota set, deploying | | **Steps** | 1. As user with CPU quota=4, try to deploy chart requesting >4 CPU
2. Check deployment outcome | | **Expected Result** | Deployment should fail or ResourceQuota enforced in the namespace | ### MTN-009 — ResourceQuota enforcement (GPU) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | User with GPU quota=0 | | **Steps** | 1. Try to deploy a chart requiring GPU | | **Expected Result** | Deployment fails due to quota enforcement | ### MTN-010 — Namespace isolation across users | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Users A and B configured with different namespaces | | **Steps** | 1. User A deploys instance to their namespace
2. User B deploys instance to their namespace
3. Verify User A cannot see User B's pods/instances | | **Expected Result** | Instances isolated by namespace, no cross-tenant visibility | ### MTN-011 — Regular user cannot access admin pages | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Regular user logged in | | **Steps** | 1. Navigate to `/configuration/users`
2. Navigate to `/admin` | | **Expected Result** | Redirected to `/forbidden`, access denied page shown | ### MTN-012 — Regular user does not see "Users" in home setup card | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Regular user logged in | | **Steps** | 1. Navigate to `/home`
2. Check "Setup" section | | **Expected Result** | "Users" card is not rendered for non-admin users | ### MTN-013 — Default user permissions match expected set | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Regular user created without custom permissions | | **Steps** | 1. Get user info from `/api/v1/auth/me` or similar
2. Inspect permissions array | | **Expected Result** | Default permissions include: home:view, configuration:clusters:manage_own, configuration:registries:manage_own, artifact:registries:view, artifact:instances:manage_own | ### MTN-014 — User workspace metadata stored and returned | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | User exists | | **Steps** | 1. Login and inspect user response
2. Check workspaceId, workspaceName, namespace, defaultClusterId | | **Expected Result** | Workspace metadata present and consistent | ### MTN-015 — Admin can create resources under any user scope | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Admin logged in | | **Steps** | 1. Check if admin can create clusters/registries without ownership restriction | | **Expected Result** | Admin-created resources are accessible to admin (global scope) | --- ## Category 9: UI/UX Bugs (20+ cases) ### UI-001 — Page layout does not overflow horizontally | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | None | | **Steps** | 1. Navigate to each page at 1440px viewport width
2. Check for horizontal scrollbar | | **Expected Result** | No horizontal overflow, all content fits within viewport | ### UI-002 — Responsive layout at mobile breakpoint (768px) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | None | | **Steps** | 1. Resize browser to 768px width
2. Navigate through all pages | | **Expected Result** | Navigation collapses, content stacks vertically, no broken layout | ### UI-003 — Responsive layout at tablet breakpoint (1024px) | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | None | | **Steps** | 1. Resize to 1024px width
2. Check all pages | | **Expected Result** | Content reflows gracefully, no overlap | ### UI-004 — Loading state displays correctly | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Slow network (simulated) | | **Steps** | 1. Enable network throttling
2. Navigate to each page | | **Expected Result** | Loading spinner/message appears while data is being fetched, content appears after load | ### UI-005 — No flickering during page transitions | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | None | | **Steps** | 1. Navigate between pages rapidly
2. Observe visual transitions | | **Expected Result** | Smooth transitions, no white flash or layout shift | ### UI-006 — Empty states show informative messages | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Fresh/empty system | | **Steps** | 1. Check clusters page (empty)
2. Check registries page (empty)
3. Check instances page (empty) | | **Expected Result** | Each page has a distinct, informative empty state message with relevant icon | ### UI-007 — Error states show retry action | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | API returns error | | **Steps** | 1. Simulate backend error
2. Observe error state | | **Expected Result** | Error message displayed with a "Retry" button | ### UI-008 — Form validation feedback is visible | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | None | | **Steps** | 1. Submit forms with invalid data | | **Expected Result** | Red error text appears near the invalid field, or toast notification with specific message | ### UI-009 — Toast notifications appear and disappear | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | None | | **Steps** | 1. Perform actions that trigger toasts (save, delete, error)
2. Observe toast behavior | | **Expected Result** | Toast appears at expected position, auto-dismisses after timeout, can be dismissed manually | ### UI-010 — Button states: disabled | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | None | | **Steps** | 1. Find disabled buttons (Launch when no clusters, Submit with invalid YAML) | | **Expected Result** | Disabled buttons have reduced opacity, no pointer cursor, cannot be clicked | ### UI-011 — Button states: loading | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Action in progress | | **Steps** | 1. Click a button that triggers an API call
2. Observe button during request | | **Expected Result** | Button shows spinner/loading indicator, disabled during request | ### UI-012 — Truncation of long text labels | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Long names exist | | **Steps** | 1. Create resources with very long names
2. Observe display in cards and lists | | **Expected Result** | Long text is truncated with ellipsis, no layout breakage | ### UI-013 — Sidebar navigation highlight matches current page | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | None | | **Steps** | 1. Navigate to each page
2. Check sidebar nav item highlight | | **Expected Result** | Current page's nav item is highlighted/active | ### UI-014 — Page header shows correct title and icon | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | None | | **Steps** | 1. Navigate to each page | | **Expected Result** | Page header displays correct title, icon, and description | ### UI-015 — Color contrast meets readability | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | None | | **Steps** | 1. Inspect text colors against backgrounds using DevTools | | **Expected Result** | All text meets WCAG AA contrast ratio (4.5:1 for normal text, 3:1 for large text) | ### UI-016 — Access denied page renders correctly | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | User with insufficient permissions | | **Steps** | 1. Access a restricted page | | **Expected Result** | Access denied page shown with "Back Home" button | ### UI-017 — Cluster list shows health status indicator | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Clusters exist | | **Steps** | 1. Navigate to cluster config page
2. Check each cluster row | | **Expected Result** | Each cluster shows a health status indicator (green/yellow/red dot or similar) | ### UI-018 — Search/filter in chart browser works correctly | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Multiple registries/repositories exist | | **Steps** | 1. Type partial name in search box
2. Type a query that matches no results | | **Expected Result** | Matching entries remain visible, non-matching hidden. "No registries" state when nothing matches. | ### UI-019 — Modal backdrop click behavior | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Any modal open | | **Steps** | 1. Open a modal (e.g., Launch modal, Cluster form, Modify modal)
2. Click on the dark backdrop | | **Expected Result** | Modal closes (or stays open depending on design). Should not cause errors. | ### UI-020 — Home page displays all sections | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Logged in as admin | | **Steps** | 1. Navigate to `/home` | | **Expected Result** | Three sections visible: primary actions (Launch Instance, Instances, Cluster Monitoring), runtime focus sidebar, setup section | --- ## Category 10: Data Persistence (10+ cases) ### PER-001 — Data survives page refresh (clusters) | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Clusters exist | | **Steps** | 1. Navigate to clusters page
2. Refresh the page (F5) | | **Expected Result** | Clusters still displayed after refresh | ### PER-002 — Data survives page refresh (registries) | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Registries exist | | **Steps** | 1. Navigate to registries page
2. Refresh | | **Expected Result** | Registries still displayed | ### PER-003 — Data survives page refresh (instances) | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Instances exist | | **Steps** | 1. Navigate to instances page
2. Refresh | | **Expected Result** | Instances still displayed | ### PER-004 — Data survives browser tab close/reopen | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Resources exist | | **Steps** | 1. Close browser tab
2. Open new tab and navigate to app
3. Login
4. Check all pages | | **Expected Result** | All data intact after session restoration | ### PER-005 — Created cluster persists after logout/login | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Cluster was created | | **Steps** | 1. Logout
2. Login again
3. Check cluster list | | **Expected Result** | Cluster still present | ### PER-006 — Created registry persists after logout/login | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Registry was created | | **Steps** | 1. Logout
2. Login
3. Check registry list | | **Expected Result** | Registry still present | ### PER-007 — Instance deployment persists across page navigation | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Instance was launched | | **Steps** | 1. Navigate away from instances page
2. Navigate back to instances page | | **Expected Result** | Instance still listed with its status | ### PER-008 — Cache consistency: new cluster appears in Launch dropdown | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Clusters page and artifact browser cached | | **Steps** | 1. Add a new cluster
2. Navigate to chart browser
3. Open Launch modal
4. Check cluster dropdown | | **Expected Result** | New cluster visible in dropdown (cache refreshed properly) | ### PER-009 — Cache consistency: new registry appears in chart browser | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Registry page open, then chart browser | | **Steps** | 1. Add a new registry
2. Navigate to chart browser
3. Check left panel | | **Expected Result** | New registry visible (after refresh or auto-reload) | ### PER-010 — Delete data persists (no phantom data) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Item deleted earlier | | **Steps** | 1. Delete a cluster/registry
2. Refresh page
3. Check list | | **Expected Result** | Deleted item does not reappear | --- ## Category 11: Security (15+ cases) ### SEC-001 — XSS via form inputs (cluster name) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Logged in | | **Steps** | 1. Create cluster with name ``
2. Observe if script executes on the list page | | **Expected Result** | Script tag is escaped/rendered as text, no XSS execution | ### SEC-002 — XSS via form inputs (registry description) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Logged in | | **Steps** | 1. Create registry with description containing HTML/script tags
2. Observe rendering | | **Expected Result** | HTML is escaped, no script execution | ### SEC-003 — XSS via instance name | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Logged in | | **Steps** | 1. Launch instance with name ``
2. Navigate to instances page | | **Expected Result** | Name is rendered safely, no XSS | ### SEC-004 — IDOR: access another user's instance detail | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | User A has an instance, User B knows its ID | | **Steps** | 1. Login as User B
2. Try to access User A's instance detail by ID | | **Expected Result** | Backend returns 403 Forbidden or 404 | ### SEC-005 — IDOR: modify another user's instance | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | User B has instance ID of User A | | **Steps** | 1. Login as User A
2. Attempt PUT on User B's instance | | **Expected Result** | 403 Forbidden | ### SEC-006 — IDOR: delete another user's cluster | Field | Value | |-------|-------| | **Priority** | P0 | | **Preconditions** | Two regular users exist | | **Steps** | 1. User A creates a cluster
2. User B attempts to delete it using cluster ID | | **Expected Result** | 403 Forbidden | ### SEC-007 — Sensitive data in API responses | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | None | | **Steps** | 1. Call `GET /api/v1/clusters`
2. Inspect response for raw certs/keys/tokens | | **Expected Result** | Sensitive fields are masked or encrypted (e.g., `hasCaData: true` instead of raw cert) | ### SEC-008 — Sensitive data in registry responses | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | None | | **Steps** | 1. Call `GET /api/v1/registries`
2. Check response for password exposure | | **Expected Result** | Password not returned in plain text; `hasPassword` boolean used instead | ### SEC-009 — JWT token manipulation: signature removed | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Valid JWT obtained | | **Steps** | 1. Strip JWT signature, keep base64 payload
2. Send API request with tampered token | | **Expected Result** | Backend rejects token, returns 401 | ### SEC-010 — JWT token manipulation: alg changed to "none" | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Valid JWT obtained | | **Steps** | 1. Change JWT header `alg` to `none`
2. Send modified token | | **Expected Result** | Backend rejects, returns 401 | ### SEC-011 — Directory traversal in repository name | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | None | | **Steps** | 1. Try to access artifacts with `../../etc/passwd` as repository name | | **Expected Result** | Returns 400 Bad Request or 404, no directory traversal occurs | ### SEC-012 — Rate limiting on login endpoint | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | None | | **Steps** | 1. Send rapid login requests (20+ in 1 second) with wrong passwords | | **Expected Result** | After threshold, rate limiting kicks in (429 Too Many Requests) | ### SEC-013 — Brute force protection | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | None | | **Steps** | 1. Attempt login with wrong password 10+ times in succession | | **Expected Result** | Account should be temporarily locked or delayed responses introduced | ### SEC-014 — Session fixation test | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | None | | **Steps** | 1. Capture pre-auth token
2. Login
3. Check if pre-auth token is still valid | | **Expected Result** | Pre-auth token invalidated, new token issued on login | ### SEC-015 — No sensitive data in error messages | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | None | | **Steps** | 1. Trigger various API errors (invalid auth, bad request, server error)
2. Inspect error responses | | **Expected Result** | Error messages do not reveal stack traces, SQL queries, or system internals | --- ## Category 12: Edge Cases (10+ cases) ### EDG-001 — Rapid double-click on submit buttons | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Create modal open | | **Steps** | 1. Click "Save" or "Create" button rapidly multiple times | | **Expected Result** | Button is disabled after first click (loading state), duplicate submissions prevented | ### EDG-002 — Very long instance name | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Launch modal open | | **Steps** | 1. Enter instance name of 253+ characters
2. Submit | | **Expected Result** | Backend validates Kubernetes naming constraints, returns error if too long | ### EDG-003 — Special characters in namespace | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Launch modal open | | **Steps** | 1. Enter namespace with uppercase letters or special characters
2. Submit | | **Expected Result** | Backend validates DNS-1123 label constraints, returns error | ### EDG-004 — Browser back/forward navigation | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Authenticated | | **Steps** | 1. Navigate to page A, then page B
2. Click browser back button
3. Click browser forward button | | **Expected Result** | Navigation works correctly, no infinite redirects, no blank pages | ### EDG-005 — Concurrent operations: launch instance in two tabs | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Same user, same cluster, same namespace | | **Steps** | 1. Tab 1: Launch instance "test-a"
2. Tab 2 (simultaneously): Launch instance "test-b" | | **Expected Result** | Both instances created, no data race or corruption | ### EDG-006 — Delete cluster with running instances | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Cluster has active Helm releases | | **Steps** | 1. Attempt to delete a cluster that has running instances | | **Expected Result** | Backend should reject deletion or return a warning about active instances | ### EDG-007 — Instance name collision (same namespace) | Field | Value | |-------|-------| | **Priority** | P1 | | **Preconditions** | Instance "test" already exists in namespace "default" on cluster X | | **Steps** | 1. Try to create another instance named "test" in the same namespace and cluster | | **Expected Result** | Backend returns conflict error, instance not created | ### EDG-008 — Rapid create/delete/create same resource name | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | None | | **Steps** | 1. Create cluster named "test-cluster"
2. Delete it
3. Immediately create another cluster named "test-cluster" | | **Expected Result** | Second creation succeeds after deletion completes | ### EDG-009 — Helm release name collision across namespaces | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Helm release exists in namespace-a | | **Steps** | 1. Launch instance with same name in namespace-b on the same cluster | | **Expected Result** | Helm releases are namespaced, so creation should succeed in different namespace | ### EDG-010 — YAML values with non-object top-level structure | Field | Value | |-------|-------| | **Priority** | P2 | | **Preconditions** | Launch modal open, YAML mode | | **Steps** | 1. Enter just a string `"hello"` or array `[1,2,3]` as YAML values
2. Click Launch | | **Expected Result** | YAML validation error: "Values YAML must be an object" | --- ## Priority Distribution Summary | Priority | Count | |----------|-------| | P0 (Critical) | 26 | | P1 (High) | 87 | | P2 (Medium) | 34 | | **Total** | **147** | ## Existing Test Coverage Reference The following test scripts already exist in `test/` and cover portions of these scenarios: | Test Script | Coverage | |-------------|----------| | `current-platform-smoke.sh` | Login, registry health, chart browsing, optional deploy/cleanup | | `frontend-playwright-smoke.py` | Login UI, chart browser rendering, instance page, mobile layout | | `frontend-interactions-audit.py` | Auth, navigation, config modals, health buttons, launch modes | | `multitenant_rbac_api_contract.py` | Auth denial, RBAC differences, resource isolation, admin cleanup | | `multitenant_rbac_ui_playwright.py` | Multi-tenant UI isolation tests | | `vllm_k3s_deploy_smoke.py` | Real k3s deployment, GPU quota, ResourceQuota, diagnostics | | `chart_values_yaml_api_contract.py` | Values YAML API contract validation | | `user_namespace_quota_api_contract.py` | User namespace and quota API contract | | `instance_card_action_layout_playwright.py` | Instance card action button layout |