# Pruebas · NimiApps 0.2.0 (ENT-002)

- **Parte A**: pruebas que he ejecutado de verdad, con sus resultados.
- **Parte B**: pruebas pendientes de hacer en el servidor cPanel de staging.

---

# Parte A · Pruebas realizadas

Fecha: 21/10/2026.

## A.1 Entorno

| Elemento     | Entorno de pruebas                                    | Servidor de staging          |
|--------------|-------------------------------------------------------|------------------------------|
| Sistema      | Ubuntu 24.04 (contenedor Linux)                       | CentOS 7 con cPanel/WHM      |
| PHP          | 8.3.6 CLI                                             | 8.3.35 (ea-php83)            |
| Laravel      | 13.38.0 · Livewire 4.4.7 (mismos que ENT-001)         | igual                        |
| Base de datos| SQLite en memoria y **MariaDB 10.6.28** (Docker)      | MariaDB 10.6.28              |
| Servidor web | `php artisan serve`                                   | Apache + PHP-FPM             |
| Navegador    | Chromium (Playwright 1.56.1), escritorio 1366 px y móvil 390 px | —                  |
| Correo       | Transporte `log` (canal `correo`)                     | SMTP de cPanel               |

Diferencias que importan: no he probado con Apache, `.htaccess`, PHP-FPM,
HTTPS real ni SMTP real, y la versión menor de PHP es distinta. Eso queda en
la Parte B.

Las pruebas con MariaDB han usado bases de datos de prueba propias
(`nimiapps_test` y `nimiapps_staging_sim`) en un contenedor local. **Nunca se
ha usado la base de datos de staging.**

## A.2 Pruebas automáticas (PHPUnit)

Comandos:

```bash
php vendor/bin/phpunit                                   # SQLite en memoria
DB_CONNECTION=mariadb DB_HOST=127.0.0.1 DB_PORT=3306 \
DB_DATABASE=nimiapps_test DB_USERNAME=... DB_PASSWORD=... \
php vendor/bin/phpunit                                   # MariaDB 10.6.28
```

Resultado final (con el código del paquete):

| Base de datos    | Pruebas | Aserciones | Fallos | Tiempo |
|------------------|--------:|-----------:|-------:|-------:|
| SQLite           | 142     | 731        | 0      | ~15 s  |
| MariaDB 10.6.28  | 142     | 731        | 0      | ~30 s  |

Reparto:

| Archivo                                   | Pruebas | Qué cubre |
|-------------------------------------------|--------:|-----------|
| `Auth/RegistrationTest`                   | 9  | Formulario con casilla sin marcar, alta con espacio personal y aceptación legal (versión y fecha), validación y mensajes en español, email existente con la misma respuesta y sin cuenta nueva, no se crean administradores, límite de intentos. |
| `Auth/LoginTest`                          | 10 | Acceso y redirección al panel, error genérico (email inexistente = contraseña errónea), bloqueo por intentos, límite por IP, «recordarme», cierre de sesión con invalidación y nuevo token CSRF, invitados fuera del panel, token CSRF en formularios. |
| `Auth/PasswordResetTest`                  | 7  | Envío, misma respuesta para email desconocido, token de un solo uso, caducado, inválido, límites, correo en español. |
| `Auth/EmailVerificationTest`              | 9  | Acceso bloqueado sin verificar, enlace válido (también sin sesión), caducado, manipulado, de otro usuario, reenvío con límites, cambio de email con contraseña y nueva verificación. |
| `Workspaces/OrganizationTest`             | 9  | Crear organizaciones, identificador único, datos fiscales opcionales, varias organizaciones, el creador es propietario. |
| `Workspaces/SwitchWorkspaceTest`          | 4  | Cambio de espacio solo a espacios propios. |
| `Workspaces/RolesTest`                    | 15 | Permisos de propietario, administrador y miembro; nadie se hace propietario cambiando el rol; el propietario no puede ser expulsado ni degradado ni salir; la base de datos rechaza un segundo propietario; transferencia con contraseña y confirmación; no se transfiere a alguien de otra organización. |
| `Invitations/InvitationTest`              | 21 | Solo se guarda el hash; correo en español; admin invita miembros pero no administradores; el propietario ve quién invitó; duplicados (aplicación y base de datos); aceptar con cuenta y sin cuenta (registro, verificación, aceptación); email distinto; caducidad (y configurable); reutilización; aceptación simultánea; revocación; reenvío con nuevo token, espera y máximo; enlaces inválidos; fallo del correo sin perder la invitación. |
| `Tenancy/IsolationTest`                   | 11 | Un usuario de A no ve, no cambia a, no modifica (manipulando sesión o ids) miembros, invitaciones ni transferencias de B; expulsado pierde el acceso en la siguiente petición con la sesión abierta; bajada de rol inmediata. |
| `Tenancy/WorkspaceScopingTest`            | 11 | Rasgo `BelongsToWorkspace`: lecturas y escrituras limitadas, falla sin contexto, no escribe ni mueve datos de otro espacio, `WorkspaceContext`, trabajos en cola (se ejecutan en su espacio y se descartan si el usuario fue expulsado), permisos en rutas de módulos. |
| `Admin/PlatformAdminTest`                 | 9  | Clientes y propietarios sin acceso a `/admin`, admin con acceso, sin permisos sobre organizaciones, admin sin verificar bloqueado, búsqueda Livewire, comando `microapps:admin-crear` (interactivo, rechaza contraseñas débiles y modo no interactivo, asciende cuentas con confirmación). |
| `SecurityTest`                            | 7  | Auditoría sin contraseñas ni emails completos de terceros, escapado de datos, cambio de contraseña y cierre de otras sesiones, errores sin detalles técnicos, cookies de sesión. |
| `CheckInstallationCommandTest`            | 5  | `microapps:check` sin secretos, tablas y columnas de ENT-002, integridad de espacios, correo sin enviar. |
| `SendTestMailCommandTest`                 | 2  | `microapps:correo-prueba`. |
| `UpgradeFromEnt001Test`                   | 1  | 1.203 usuarios «de ENT-001» reciben su espacio personal; repetir la migración no duplica. |
| `HealthCheckTest`, `HomePageTest`, `ModulesTest` | 12 | Pruebas de ENT-001 adaptadas a 0.2.0. |

En MariaDB comprobé además con `SHOW CREATE TABLE` que las columnas generadas
`owner_marker` y `pending_email` se crean como `STORED` con sus índices únicos.

## A.3 Recorridos reales en el navegador

Con la aplicación arrancada (`php artisan serve`) y Chromium:

- Capturas de todas las pantallas en escritorio y móvil (acceso, registro,
  panel personal y de organización, selector de espacio, menú móvil, espacio,
  miembros, invitaciones, configuración, perfil, crear organización, diálogo
  de confirmación, `/admin` con sus listados y búsqueda). Sin errores de
  JavaScript ni respuestas 4xx/5xx inesperadas.
- Recorrido completo con correos reales del transporte `log`: enlace de
  invitación sin sesión → registro con email precargado → enlace de
  verificación del correo → acceso → vuelta a la invitación → aceptar →
  reutilizar el enlace (rechazado) → pedir recuperación → enlace del correo →
  nueva contraseña → acceso con la nueva contraseña. **Todo correcto.**
- Plantilla de correo (cabecera NimiApps, botón, texto alternativo)
  renderizada y revisada en captura.

## A.4 Ensayo de la actualización desde ENT-001

Simulé el servidor con un usuario sin privilegios (`nimisim`) y la estructura
`~/public_html/staging.nimiapps.com`:

1. Descomprimí el ZIP **original** de ENT-001, comprobé `CHECKSUMS.sha256`,
   creé `.env` (MariaDB 10.6.28, `APP_ENV=production`), `key:generate`,
   `migrate`, `optimize`.
2. Añadí datos «previos»: 2 usuarios (uno verificado y otro no), un log en
   `storage/logs`, y una autorización extra al final de `public/.htaccess`
   (como en el servidor real). Guardé las huellas de `.env`, `.htaccess`,
   `public/.htaccess` y el log.
3. Copia de la base de datos (`mariadb-dump --single-transaction`) y de los
   archivos (`tar`).
4. Seguí el manual: comprobar el ZIP con `sha256sum -c`, descomprimir en la
   carpeta personal, `--simular`, `artisan down`, `actualizar.sh`, borrar
   cachés, `migrate --force`, `optimize`, `microapps:check`, `artisan up`.

Resultados:

| Comprobación | Resultado |
|--------------|-----------|
| Ejecutar como root | Se niega |
| Sin modo mantenimiento | Se detiene antes de tocar nada |
| Archivo del servidor modificado / archivo «nuevo» ya existente con otro contenido | Lista ambos y no copia nada |
| Paquete alterado (un byte cambiado) | Detectado por `paquete.sha256`, se detiene |
| Actualización real | 189 archivos copiados y verificados; copia de seguridad creada en `~/nimiapps-respaldos/` |
| Repetir el script tras actualizar | «Ya están instalados y coinciden», no toca nada |
| `migrate --force` | 8 migraciones aplicadas sin errores |
| `microapps:check` | `NimiApps 0.2.0 (ENT-002)`, instalación correcta; 2 espacios personales creados para los usuarios previos |
| `.env`, `.htaccess`, `public/.htaccess`, log previo | Huellas idénticas a las de antes |
| `CHECKSUMS.sha256` de la 0.2.0 | Todo correcto |
| `/`, `/estado`, `/up`, `/acceso`, `/registro` | 200; `/estado` con `"version":"0.2.0"` |
| Acceso con la usuaria previa (contraseña de antes) | Entra al panel con su espacio personal |
| Usuario previo sin verificar | Va a «Confirma tu correo electrónico» |
| Crear organización | Correcto, queda como espacio activo |
| `/admin` sin ser administrador | 403 «Acceso denegado» |
| `microapps:admin-crear` (interactivo) | Crea la cuenta; la contraseña no aparece en la salida |
| `/admin` con esa cuenta | Listados de usuarios y organizaciones correctos |
| Sesiones abiertas en la base de datos | Se conservan |

**Vuelta atrás ensayada:**

- `migrate:rollback --step=8` + `restaurar-archivos.sh`: la aplicación vuelve
  a `Plataforma de Microapps 0.1.0 (ENT-001)`, `microapps:check` correcto,
  quedan las tablas de la 0.1.0 y las cuentas de usuario.
- Restauración completa: carpeta antigua apartada + `tar` + borrado de las
  tablas de ENT-002 + carga del volcado. Vuelve a 0.1.0 con exactamente los
  datos de antes y las huellas de `.env` y `.htaccess` intactas.

## A.5 Concurrencia en MariaDB 10.6.28

Diez procesos PHP independientes lanzados en el mismo milisegundo contra la
instalación ensayada:

| Prueba | Resultado |
|--------|-----------|
| 10 aceptaciones simultáneas de la misma invitación | 1 aceptada, 9 rechazadas («ya se ha utilizado»); una sola membresía |
| 10 invitaciones simultáneas al mismo email | 1 creada, 9 rechazadas («ya hay una invitación pendiente») |
| 10 transferencias de propiedad simultáneas | 1 realizada, 9 rechazadas; un solo propietario |

## A.6 Incidencias encontradas y corregidas

| # | Incidencia | Corrección |
|---|-----------|------------|
| 1 | La migración que crea los espacios personales de usuarios existentes recorría la tabla por páginas y, como cada página «desaparece» de la consulta al procesarla, **se saltaba usuarios a partir del número 1.000** (lo detectó la prueba con 1.203 usuarios: solo 1.000 espacios). | Recorrido por id (`eachById`). La prueba queda incluida. |
| 2 | En MariaDB, 10 invitaciones simultáneas al mismo email daban **bloqueo mutuo** (error 1213) y 7 errores 500. Los datos quedaban bien, pero el usuario vería un error. | Se bloquea primero el espacio y después la invitación, en el mismo orden en invitar y aceptar, con reintento automático. Repetido: 1 creada y 9 mensajes normales. |
| 3 | `optimize:clear` vacía también la caché de aplicación (tabla `cache`) y con ella los contadores de intentos. | El manual usa `config:clear`, `route:clear`, `view:clear` y `event:clear`. |
| 4 | Cambiar un rol a la vez que otra persona daba un error 409 sin página propia. | Se muestra un mensaje de validación («El rol ha cambiado mientras tanto»). |
| 5 | El límite de invitaciones se calculaba por espacio antes de conocer el espacio activo. | Límite por usuario. |
| 6 | Atributo `x-data` duplicado en los formularios (el indicador «enviando» no funcionaba) y doble escapado en el perfil. | Corregidos en los componentes. |
| 7 | Con `LOG_LEVEL=warning` los correos del transporte de pruebas no se veían. | Canal `correo` propio (`storage/logs/correo-*.log`). |

## A.7 Lo que no he podido probar

- Apache, `.htaccess`, PHP-FPM y HTTPS reales de cPanel (cookies `Secure`).
- Envío por SMTP real y cómo se ven los correos en Gmail/Outlook.
- PHP 8.3.35 exacto y CentOS 7 (el script usa solo bash 4.2 y coreutils
  básicos, compatibles con CentOS 7, pero no lo he ejecutado allí).
- `mysqldump` del servidor con sus credenciales.

## A.8 Observaciones conocidas

- La prueba de «fallo del correo» deja una línea de aviso en el log de las
  pruebas (provocada a propósito por el simulador); no afecta a la aplicación.
- Queda en el servidor el CSS antiguo `public/build/assets/app-c66wexFq.css`
  sin uso.

---

# Parte B · Pruebas pendientes en cPanel (staging)

Marca cada casilla al comprobarla. Usa un correo real tuyo y, para probar
invitaciones, una segunda dirección (o un alias). Si usas el transporte de
pruebas (`MAIL_MAILER=log`), los enlaces están en `storage/logs/correo-*.log`.

## B.1 Actualización y diagnóstico

- [ ] `actualizar.sh --simular` termina con «Todo listo para actualizar».
- [ ] `actualizar.sh` termina con «Archivos actualizados correctamente».
- [ ] `migrate --force` aplica 8 migraciones.
- [ ] `microapps:check` muestra `NimiApps 0.2.0 (ENT-002)` y «instalación correcta» (sin `[ERROR]`).
- [ ] `microapps:correo-prueba tu-email` llega a la bandeja de entrada.
- [ ] https://staging.nimiapps.com muestra NimiApps 0.2.0 y https://staging.nimiapps.com/estado devuelve `"estado":"ok"`.

## B.2 Registro

- [ ] `/registro`: la casilla de condiciones y privacidad aparece **sin marcar** y los textos legales indican que son provisionales.
- [ ] Sin marcar la casilla no deja registrarse (mensaje en español).
- [ ] Contraseña corta o sin números: mensaje en español.
- [ ] Registro correcto: pantalla «Revisa tu correo».
- [ ] Registrarse otra vez con el mismo email: misma pantalla, no se crea otra cuenta y llega un aviso al titular.

## B.3 Verificación de email

- [ ] Llega el correo de verificación, en español y con la imagen de NimiApps.
- [ ] El enlace lleva a la web por **https** y verifica la cuenta.
- [ ] Sin verificar, `/panel` lleva a «Confirma tu correo electrónico».
- [ ] «Reenviar» funciona y, tras varios intentos seguidos, avisa del límite.

## B.4 Acceso

- [ ] Acceso correcto lleva a `/panel`.
- [ ] Contraseña errónea: mensaje genérico; tras 5 intentos, bloqueo temporal.
- [ ] «Recordarme»: cerrar y abrir el navegador mantiene la sesión.
- [ ] «Cerrar sesión» vuelve al acceso y `/panel` ya no es accesible.
- [ ] En las herramientas del navegador, la cookie de sesión es `Secure`, `HttpOnly` y `SameSite=Lax`.

## B.5 Recuperación de contraseña

- [ ] `/recuperar-contrasena` con tu email: llega el correo.
- [ ] Con un email inexistente, el mensaje es el mismo.
- [ ] El enlace permite poner una contraseña nueva y entrar con ella.
- [ ] El mismo enlace no sirve una segunda vez.

## B.6 Perfil

- [ ] Cambiar nombre y apellidos.
- [ ] Cambiar contraseña (pide la actual).
- [ ] «Cerrar las demás sesiones» cierra la sesión abierta en otro navegador.
- [ ] Cambiar el email pide la contraseña, envía verificación a la nueva dirección y un aviso a la anterior.

## B.7 Organizaciones y cambio de espacio

- [ ] Crear una organización (nombre, identificador, opcionalmente nombre comercial y datos fiscales).
- [ ] Un identificador ya usado da error.
- [ ] El selector de la barra superior cambia entre el espacio personal y la organización.
- [ ] El panel muestra el espacio activo, su tipo, el resumen de miembros y «Todavía no tienes aplicaciones activas».
- [ ] Editar los datos de la organización en «Configuración».

## B.8 Invitaciones

- [ ] Invitar a la segunda dirección como miembro: llega el correo.
- [ ] En «Invitaciones» aparece pendiente, con quién la envió.
- [ ] Abrir el enlace sin cuenta → registro con el email ya puesto → verificación → aceptar.
- [ ] El mismo enlace ya no sirve.
- [ ] Invitar otra vez al mismo email mientras está pendiente da aviso.
- [ ] Reenviar (después de 1 minuto) y comprobar que el enlace anterior deja de funcionar.
- [ ] Revocar una invitación y comprobar que su enlace deja de funcionar.
- [ ] Abrir una invitación con una cuenta de otro email: la rechaza.

## B.9 Roles

- [ ] El propietario asciende a un miembro a administrador y lo vuelve a bajar.
- [ ] Con la cuenta administradora: puede invitar miembros, pero no ve la opción de invitar administradores ni de transferir.
- [ ] Con la cuenta administradora: no puede expulsar al propietario.
- [ ] Con la cuenta miembro: no ve «Miembros», «Invitaciones» ni «Configuración» en el menú, y escribir esas URLs da 403.
- [ ] Transferir la propiedad (pide contraseña y confirmación escrita); llegan los correos; el antiguo propietario queda como administrador.
- [ ] Expulsar a un miembro con su sesión abierta en otro navegador: en su siguiente clic pierde el acceso y vuelve a su espacio personal con un aviso.

## B.10 Administración interna

- [ ] Antes de crear administradores, `/admin` da 403 con cualquier cuenta.
- [ ] `microapps:admin-crear tu-email` y `/admin` muestra el resumen.
- [ ] Usuarios y organizaciones con búsqueda; detalle de miembros de una organización.
- [ ] `microapps:admin-listar` muestra la cuenta; `microapps:admin-revocar` le quita el acceso.

## B.11 Seguridad

- [ ] https://staging.nimiapps.com/.env, `/../.env`, `/storage/logs/` y `/vendor/` dan 403 o 404.
- [ ] Una URL inexistente muestra la página 404 de NimiApps, sin detalles técnicos.
- [ ] Enviar un formulario tras dejar la página abierta más de 2 horas: página «419» y no se procesa.
- [ ] En «Miembros», cambiar a mano el identificador en la URL de una acción por el de otra organización da 404/403.
- [ ] En `storage/logs/laravel-*.log` no aparecen contraseñas ni enlaces de invitación o recuperación.

## B.12 Diagnóstico final

- [ ] `microapps:check` al terminar las pruebas: sin `[ERROR]`.
- [ ] Si usaste el transporte de pruebas, borrar `storage/logs/correo-*.log`.
