Files
OmniOilPersonal/openspec/changes/archive/2026-05-07-console-tech-debt-cleanup/specs/testing/spec.md
henryor af5535dfcc docs(openspec): archiva el change console-tech-debt-cleanup
Persiste proposal, specs delta (auth, api-client, access-requests,
platform-shell, testing), design con 8 ADRs, tasks (27 items en 10 fases),
verify-report (28 tests, 0 críticos, 0 warnings tras el cierre) y
archive-report final.

Cierra el ciclo SDD del cleanup de deuda técnica del console.
2026-05-07 21:14:56 -05:00

5.0 KiB

Testing Baseline Specification

New Spec for: Testing Infrastructure — console-tech-debt-cleanup

Purpose

Establishes Vitest + Testing Library as the test runner in services/frontend-console and defines the minimum baseline suite that must pass before any P0/P1 change is merged.

Requirements

Requirement: Vitest + Testing Library Configured

services/frontend-console MUST have Vitest and @testing-library/react installed and configured. The configuration MUST mirror services/frontend-admin (jsdom environment, setupTests.ts importing @testing-library/jest-dom). pnpm test MUST execute the suite and exit non-zero on failure. pnpm test:watch MUST run in watch mode.

Scenario: pnpm test runs green on a clean checkout

  • GIVEN a developer runs pnpm install && pnpm test inside services/frontend-console
  • WHEN all baseline tests pass
  • THEN the process exits with code 0
  • AND no test is skipped

Scenario: pnpm test fails on a broken test

  • GIVEN a test assertion fails
  • WHEN pnpm test is run
  • THEN the process exits with non-zero code
  • AND the failing test name and assertion are printed to stdout

Requirement: SessionCoordinator Refresh Deduplication Test

The test suite MUST include a test that verifies SessionCoordinator.refresh() calls fetch (the HTTP POST to /auth/refresh) exactly once when two concurrent invocations are made.

Scenario: Two concurrent refresh calls share one HTTP request

  • GIVEN fetch is mocked to resolve after a short delay with { access_token: "new-token", role: "platform_admin", email: "op@x.com" }
  • WHEN SessionCoordinator.refresh() is called twice concurrently (without awaiting the first)
  • THEN fetch mock is called exactly once
  • AND both calls resolve with the same token string

Requirement: apiClient 401 Retry Path Test

The test suite MUST include tests for apiClient's 401 handling covering: single refresh-and-retry success, and refresh failure leading to logout + redirect.

Scenario: apiClient retries after 401 and resolves on success

  • GIVEN fetch returns 401 on first call and 200 with body on retry
  • AND SessionCoordinator.refresh resolves with a new token
  • WHEN apiClient('/resource', { method: 'GET' }) is called
  • THEN the resolved value equals the parsed body from the 200 response
  • AND fetch was called twice total

Scenario: apiClient calls logout on refresh failure

  • GIVEN fetch returns 401 and SessionCoordinator.refresh returns null
  • WHEN apiClient('/resource', { method: 'GET' }) is called
  • THEN useAuthStore.getState().logout is called
  • AND the function throws

Requirement: Auth Store setSession and logout Tests

The test suite MUST cover setSession writing to accessToken (and only accessToken for the token), and logout resetting accessToken and user to null.

Scenario: setSession writes accessToken

  • GIVEN the store is freshly initialized
  • WHEN setSession({ accessToken: 'tok', refreshToken: null, role: 'platform_admin', email: 'op@x.com' }) is called
  • THEN getState().accessToken equals 'tok'
  • AND getState().user equals { email: 'op@x.com', role: 'platform_admin' }
  • AND there is no token field with a non-null value on the state (field absent or null)

Scenario: logout clears state

  • GIVEN the store has a non-null accessToken and user
  • WHEN logout() is called
  • THEN getState().accessToken is null
  • AND getState().user is null

Requirement: AccessRequestsPage Mutation Baseline Tests

The test suite MUST include component tests for AccessRequestsPage covering the happy path and error path for each of the three mutations (approve, reject, mark-as-review).

Scenario: Approve mutation — success toast and invalidation

  • GIVEN AccessRequestsPage renders with a request in pending status
  • WHEN the operator selects that request and clicks "Aprobar"
  • AND approveAccessRequest resolves successfully
  • THEN toast.success is called with a message containing "aprobada"
  • AND queryClient.invalidateQueries is called with key ['platform-access-requests']

Scenario: Reject mutation — success toast, modal closes, invalidation

  • GIVEN the rejection modal is open with a valid reason
  • WHEN rejectAccessRequest resolves successfully
  • THEN toast.success is called with a message containing "rechazada"
  • AND the rejection modal is no longer visible
  • AND the query cache is invalidated

Scenario: Review mutation — success toast and invalidation

  • GIVEN a request with status = 'pending' is selected
  • WHEN the operator clicks "Revisar"
  • AND markAccessRequestInReview resolves successfully
  • THEN toast.success is called with a message containing "revision"
  • AND the query cache is invalidated

Scenario: Mutation error — error toast, no invalidation

  • GIVEN any mutation is triggered
  • WHEN the API call rejects with new Error('Network error')
  • THEN toast.error is called with 'Network error'
  • AND queryClient.invalidateQueries is NOT called