Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,12 @@ jobs:
- name: Run linting
run: pnpm lint


- name: Run tests
run: pnpm test

- name: Run coverage
run: pnpm test:coverage
build:
runs-on: ubuntu-latest
steps:
Expand Down
62 changes: 38 additions & 24 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,29 +1,39 @@
<div align="center">
<img src="public/logo.png" alt="Workout.cool Logo" width="120" height="120">
<h1>Workout.cool</h1>
<h1>Workout.cool / Tester for Tabanos Team</h1>
<p align="center">
<img src="public/logo.png" alt="Tester Logo" width="100" height="100" /><br>

<span style="font-size:28px; margin:0 15px;">
for
</span>

<img src="https://github.com/user-attachments/assets/5e1a7603-a014-496d-b343-a3ea6acd5617"
alt="Tabanos Logo"
width="100"
height="100" />
</p>

</div>
<h3><em>Modern fitness coaching platform with comprehensive exercise database</em></h3>
<p>
<a href="https://github.com/Snouzy/workout-cool/graphs/contributors"><img src="https://img.shields.io/github/contributors/Snouzy/workout-cool?style=plastic" alt="Contributors">
<a href="https://github.com/Snouzy/workout-cool/network/members">
<img src="https://img.shields.io/github/forks/Snouzy/workout-cool" alt="Forks">
<a href="https://github.com/Snouzy/workout-cool/stargazers">
<img src="https://img.shields.io/github/stars/Snouzy/workout-cool" alt="Stars">
<a href="https://github.com/Snouzy/workout-cool/issues">
<img src="https://img.shields.io/github/issues/Snouzy/workout-cool" alt="Issues">
<img src="https://img.shields.io/github/repo-size/Snouzy/workout-cool" alt="Repository Size">
<a href="LICENSE">
<img src="https://img.shields.io/badge/License-MIT-green.svg" alt="MIT License">
</a>
<div>
<a href="https://github.com/Snouzy/workout-cool/graphs/contributors"><img src="https://img.shields.io/github/contributors/Snouzy/workout-cool?style=plastic" alt="Contributors"></a>
<a href="https://github.com/Snouzy/workout-cool/network/members"><img src="https://img.shields.io/github/forks/Snouzy/workout-cool" alt="Forks"></a>
<a href="https://github.com/Snouzy/workout-cool/stargazers"><img src="https://img.shields.io/github/stars/Snouzy/workout-cool" alt="Stars"></a>
<a href="https://github.com/Snouzy/workout-cool/issues"><img src="https://img.shields.io/github/issues/Snouzy/workout-cool" alt="Issues"></a>
<img src="https://img.shields.io/github/repo-size/Snouzy/workout-cool" alt="Repository Size">
<a href="LICENSE"><img src="https://img.shields.io/badge/License-MIT-green.svg" alt="MIT License"></a>
</div>

<p>
<a href="https://discord.gg/NtrsUBuHUB">
<img src="https://img.shields.io/badge/Discord-Join%20Community-5865F2?style=for-the-badge&logo=discord&logoColor=white" alt="Discord">
</a>
<a href="https://ko-fi.com/workoutcool">
<img src="https://img.shields.io/badge/Ko--fi-Support%20Project-FF5E5B?style=for-the-badge&logo=ko-fi&logoColor=white" alt="Ko-fi">
</a>
</p>
<!-- Keep these links. Translations will automatically update with the README. -->
<div>
<a href="https://discord.gg/NtrsUBuHUB">
<img src="https://img.shields.io/badge/Discord-Join%20Community-5865F2?style=for-the-badge&logo=discord&logoColor=white" alt="Discord">
</a>
<a href="https://ko-fi.com/workoutcool">
<img src="https://img.shields.io/badge/Ko--fi-Support%20Project-FF5E5B?style=for-the-badge&logo=ko-fi&logoColor=white" alt="Ko-fi">
</a>
</div>
<!-- Keep these links. Translations will automatically update with the README. -->
<div>
<a href="https://readme-i18n.com/Snouzy/workout-cool?lang=de">Deutsch</a> |
<a href="https://readme-i18n.com/Snouzy/workout-cool?lang=es">Español</a> |
<a href="https://readme-i18n.com/Snouzy/workout-cool?lang=fr">français</a> |
Expand All @@ -32,9 +42,13 @@
<a href="https://readme-i18n.com/Snouzy/workout-cool?lang=pt">Português</a> |
<a href="https://readme-i18n.com/Snouzy/workout-cool?lang=ru">Русский</a> |
<a href="https://readme-i18n.com/Snouzy/workout-cool?lang=zh">中文</a>
</p>
</div>
</div>

> **Fork academico** -- Curso de Pruebas de Software, Sprint 1.
> Repositorio original: [Snouzy/workout-cool](https://github.com/Snouzy/workout-cool)
>
> Documentacion del proyecto: [GitHub Pages](https://czrich.github.io/workout-cool/) | Plan de pruebas: [Wiki](https://github.com/CZrich/workout-cool/wiki) | Tablero Kanban: [Projects](https://github.com/users/CZrich/projects/3)
## Table of Contents

- [About](#about)
Expand Down
108 changes: 108 additions & 0 deletions docs/functional-test-training.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# PF-entrenamiento: Casos de Prueba Funcionales

## 1. Objetivo

Validar funcionalmente los flujos de entrenamiento de Workout Cool, cubriendo la creacion de rutinas, ejecucion de sesiones,
registro de series y uso de programas predefinidos.

Este documento corresponde al issue [#33 PF-entrenamiento](https://github.com/Tabanos-Team/workout-cool/issues/33), asignado a
`JeremyCruzGallegos`.

## 2. Alcance

### Incluido

- Constructor de entrenamiento desde la pagina principal.
- Seleccion de equipamiento y musculos.
- Generacion, cambio, seleccion y eliminacion de ejercicios.
- Inicio, ejecucion y finalizacion de una sesion.
- Registro de series con repeticiones, peso, tiempo y peso corporal.
- Historial de sesiones: repetir y eliminar sesiones.
- Programas predefinidos: consulta, seleccion de semanas, inicio y finalizacion de sesiones.

### Fuera de alcance

- Pruebas visuales por captura.
- Pruebas de rendimiento.
- Validacion real de pagos o suscripciones externas.
- Pruebas directas contra servicios externos como Stripe, RevenueCat, SMTP, OpenPanel o YouTube.

## 3. Referencias

- Wiki: Plan de Pruebas Unitarias.
- Wiki: Informacion General del Proyecto.
- Requerimientos relacionados:
- RF16: El usuario puede crear programas de entrenamiento personalizados.
- RF17: El usuario puede acceder a programas de entrenamiento predefinidos.
- RF18: El usuario puede registrar series, repeticiones y peso por ejercicio.

## 4. Ambiente de Prueba

| Elemento | Valor |
|----------|-------|
| Aplicacion | Workout Cool |
| Tipo | Aplicacion Web Progresiva |
| Framework | Next.js |
| Base de datos | PostgreSQL |
| Navegadores | Chrome, Firefox, Edge |
| Resoluciones | Desktop 1366x768, Mobile 390x844 |
| Datos base | Ejercicios importados desde `data/sample-exercises.csv` o seed equivalente |

## 5. Datos de Prueba

| Dato | Valor sugerido |
|------|----------------|
| Usuario anonimo | Sesion sin iniciar |
| Usuario autenticado | Cuenta de prueba con email valido |
| Usuario premium | Cuenta de prueba con estado premium habilitado |
| Equipamiento | Dumbbell, Barbell, Bodyweight |
| Musculos | Chest, Back, Quadriceps, Biceps |
| Serie por repeticiones | 12 reps |
| Serie por peso | 40 kg |
| Serie por tiempo | 1 min 30 sec |

## 6. Casos de Prueba Funcionales

| ID | Escenario | Precondicion | Pasos | Resultado esperado | RF |
|----|-----------|--------------|-------|--------------------|----|
| PF-ENT-01 | Bloquear avance sin equipamiento | Usuario en pagina principal | 1. Abrir constructor de entrenamiento. 2. No seleccionar equipamiento. 3. Intentar continuar. | El sistema mantiene al usuario en el paso de equipamiento y no permite avanzar. | RF18 |
| PF-ENT-02 | Seleccionar equipamiento y avanzar | Usuario en pagina principal | 1. Seleccionar al menos un equipamiento. 2. Presionar continuar. | El sistema avanza al paso de seleccion de musculos. | RF18 |
| PF-ENT-03 | Limpiar equipamiento seleccionado | Equipamiento seleccionado | 1. Presionar la opcion para limpiar equipamiento. | El equipamiento queda desmarcado y el avance vuelve a quedar bloqueado. | RF18 |
| PF-ENT-04 | Bloquear generacion sin musculos | Usuario en paso de musculos | 1. No seleccionar musculos. 2. Intentar continuar. | El sistema no avanza al paso de ejercicios. | RF18 |
| PF-ENT-05 | Generar ejercicios por musculo | Equipamiento y musculos seleccionados | 1. Seleccionar uno o mas musculos. 2. Continuar al paso de ejercicios. | Se muestran ejercicios agrupados segun los musculos seleccionados. | RF18 |
| PF-ENT-06 | Cambiar ejercicio sugerido | Lista de ejercicios generada | 1. Presionar la accion de cambiar ejercicio en una tarjeta. | El ejercicio seleccionado se reemplaza por otro compatible. | RF18 |
| PF-ENT-07 | Elegir ejercicio manualmente | Lista de ejercicios generada | 1. Abrir selector de ejercicio. 2. Elegir un ejercicio. | El ejercicio elegido reemplaza al ejercicio actual. | RF18 |
| PF-ENT-08 | Eliminar ejercicio del entrenamiento | Lista de ejercicios generada | 1. Presionar eliminar en un ejercicio. | El ejercicio desaparece de la rutina y el orden restante se conserva. | RF18 |
| PF-ENT-09 | Iniciar entrenamiento | Rutina con al menos un ejercicio | 1. Presionar iniciar entrenamiento. | Se abre la interfaz de sesion activa con ejercicios y series. | RF18 |
| PF-ENT-10 | Registrar serie por repeticiones | Sesion activa | 1. Seleccionar tipo Reps. 2. Ingresar 12. 3. Finalizar serie. | La serie queda marcada como completada y sus campos quedan deshabilitados. | RF18 |
| PF-ENT-11 | Registrar serie por peso | Sesion activa | 1. Agregar columna Weight. 2. Ingresar 40. 3. Seleccionar kg. 4. Finalizar serie. | La serie registra peso y unidad correctamente. | RF18 |
| PF-ENT-12 | Registrar serie por tiempo | Sesion activa | 1. Seleccionar tipo Time. 2. Ingresar 1 minuto y 30 segundos. 3. Finalizar serie. | La serie queda completada con duracion correcta. | RF18 |
| PF-ENT-13 | Editar serie completada | Serie completada | 1. Presionar editar. 2. Modificar valores. 3. Finalizar nuevamente. | La serie vuelve a modo edicion y permite guardar los cambios. | RF18 |
| PF-ENT-14 | Agregar y eliminar serie | Sesion activa | 1. Presionar agregar serie. 2. Eliminar la nueva serie. | La sesion refleja la adicion y eliminacion sin afectar otras series. | RF18 |
| PF-ENT-15 | Avanzar al siguiente ejercicio | Sesion con varios ejercicios | 1. Completar o dejar una serie. 2. Presionar siguiente ejercicio. | El foco cambia al siguiente ejercicio de la sesion. | RF18 |
| PF-ENT-16 | Finalizar sesion | Sesion activa | 1. Presionar finalizar sesion. | La sesion se completa, se muestra confirmacion y se sincroniza el historial si aplica. | RF18 |
| PF-ENT-17 | Repetir sesion anterior | Historial con sesion finalizada y sin sesion activa | 1. Abrir historial. 2. Presionar repetir. | El constructor carga equipamiento, musculos y ejercicios de la sesion anterior. | RF18 |
| PF-ENT-18 | Impedir repetir si hay sesion activa | Historial con sesion finalizada y una sesion activa | 1. Abrir historial. 2. Revisar accion repetir. | La accion repetir aparece deshabilitada mientras exista una sesion activa. | RF18 |
| PF-ENT-19 | Eliminar sesion del historial | Historial con sesion finalizada | 1. Presionar eliminar. 2. Confirmar eliminacion. | La sesion se elimina del historial. | RF18 |
| PF-ENT-20 | Cancelar eliminacion de sesion | Historial con sesion finalizada | 1. Presionar eliminar. 2. Cancelar confirmacion. | La sesion permanece en el historial. | RF18 |
| PF-ENT-21 | Consultar programas predefinidos | Programas cargados | 1. Abrir `/programs`. 2. Seleccionar un programa. | Se muestra detalle del programa con informacion, semanas y sesiones. | RF17 |
| PF-ENT-22 | Cambiar semana de programa | Programa con varias semanas | 1. Abrir pestaña de sesiones. 2. Seleccionar otra semana. | La lista muestra las sesiones de la semana seleccionada. | RF17 |
| PF-ENT-23 | Iniciar sesion de programa gratuita | Usuario con acceso permitido | 1. Abrir una sesion gratuita. 2. Presionar iniciar sesion. | El sistema inscribe al usuario si corresponde e inicia la sesion con ejercicios del programa. | RF17, RF18 |
| PF-ENT-24 | Bloquear sesion premium sin acceso | Usuario no premium en sesion premium | 1. Abrir una sesion premium. | El sistema muestra restriccion de acceso y no permite iniciar la sesion. | RF17 |
| PF-ENT-25 | Completar sesion de programa | Sesion de programa activa | 1. Finalizar entrenamiento. | La sesion se marca como completada y el usuario vuelve al programa con progreso actualizado. | RF17, RF18 |

## 7. Criterios de Aceptacion

- Todos los casos criticos `PF-ENT-01` a `PF-ENT-16` deben ejecutarse sin fallos bloqueantes.
- Los casos de programas `PF-ENT-21` a `PF-ENT-25` deben validar acceso gratuito, premium y actualizacion de progreso.
- Cualquier defecto detectado debe registrarse como issue y vincularse con el caso funcional afectado.
- La evidencia minima por caso debe incluir fecha, navegador, usuario utilizado, resultado y observaciones.

## 8. Estado del Entregable

| Campo | Valor |
|-------|-------|
| Issue | #33 PF-entrenamiento |
| Responsable | JeremyCruzGallegos |
| Estado | Diseñado |
| Fecha | 2026-06-04 |
151 changes: 151 additions & 0 deletions docs/integration-flow-auth-db-premium.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
# INT-01: Flujos de Integracion Auth -> DB -> Premium

## Objetivo

Definir el flujo de integracion entre autenticacion, persistencia en base de datos y estado premium para Workout Cool.

Este documento cubre el issue `#21` titulado `INT-01: Definir flujos de integración (auth → DB → premium)`.

## Alcance

### Incluido

- Registro e inicio de sesion.
- Validacion de sesion en acciones servidor.
- Persistencia de usuarios, suscripciones y enrolamientos en Prisma.
- Creacion de checkout premium.
- Sincronizacion de estado premium desde Stripe y RevenueCat.
- Consulta unificada del estado premium.

### Fuera de alcance

- Refactor de logica de negocio.
- Pruebas reales contra proveedores externos.
- Cambios visuales de interfaz.

## Componentes involucrados

| Capa | Archivos clave |
|------|----------------|
| Auth | `src/features/auth/signup/model/signup.action.ts`, `src/features/auth/lib/better-auth.ts`, `src/features/auth/lib/auth-client.ts` |
| DB | `src/shared/lib/prisma`, tablas `user`, `subscription`, `userProgramEnrollment`, `revenueCatWebhookEvent` |
| Premium | `src/shared/lib/premium/premium.service.ts`, `src/shared/lib/premium/premium.manager.ts` |
| Checkout | `app/api/premium/checkout/route.ts`, `src/features/premium/ui/premium-upgrade-card.tsx` |
| Status | `app/api/premium/status/route.ts` |
| Webhooks | `app/api/webhooks/stripe/route.ts`, `app/api/revenuecat/webhook/route.ts`, `app/api/revenuecat/link-user/route.ts`, `app/api/revenuecat/sync-status/route.ts` |
| Programas | `src/features/programs/actions/enroll-program.action.ts`, `src/features/programs/actions/start-program-session.action.ts`, `src/features/programs/actions/complete-program-session.action.ts` |

## Flujo 1: Registro y sesion

1. El usuario completa el formulario de registro.
2. `signUpAction` valida el input y llama a `auth.api.signUpEmail`.
3. Better Auth crea el usuario.
4. Si existe OpenPanel, se registra el evento analitico.
5. El usuario obtiene sesion y puede continuar hacia modulos protegidos.

Punto de integracion clave:

- La identidad nace en Auth.
- La fuente de verdad del usuario pasa a Prisma para permisos, premium y progreso.

## Flujo 2: Acceso a acciones servidor

1. Una accion protegida pide la sesion con `auth.api.getSession` o `serverRequiredUser`.
2. Si no hay sesion, la accion responde `Unauthorized`.
3. Si hay sesion, se obtiene `user.id`.
4. Prisma usa ese `user.id` para leer o escribir datos.

Ejemplos reales:

- `enrollInProgram(programId)` crea `userProgramEnrollment`.
- `startProgramSession(enrollmentId, sessionId)` inicia progreso de sesion.
- `completeProgramSession(...)` marca la sesion como completada.

## Flujo 3: Checkout premium

1. El usuario abre la tarjeta premium.
2. Si no esta autenticado, la UI guarda el plan pendiente y redirige a signin.
3. Si esta autenticado, la UI llama `POST /api/premium/checkout`.
4. El endpoint exige usuario autenticado con `serverRequiredUser`.
5. `PremiumManager.createCheckout` crea la sesion de pago para el proveedor elegido.
6. El frontend redirige al checkout devuelto por el backend.

Regla de integracion:

- La UI no decide el estado premium.
- El backend crea el checkout y la base de datos confirma el resultado posterior.

## Flujo 4: Sincronizacion premium

### Stripe

1. Stripe llama `POST /api/webhooks/stripe`.
2. El webhook valida la firma.
3. `PremiumManager.processWebhook("stripe", ...)` procesa el evento.
4. La suscripcion y el flag premium se actualizan en Prisma.

### RevenueCat

1. RevenueCat llama `POST /api/revenuecat/webhook`.
2. El webhook valida la firma HMAC.
3. Segun el tipo de evento:
- `INITIAL_PURCHASE`, `RENEWAL`, `UNCANCELLATION` activan premium.
- `CANCELLATION`, `EXPIRATION` desactivan premium.
- `BILLING_ISSUE` marca el ciclo como expirado.
- `PRODUCT_CHANGE` actualiza la expiracion.
4. El servicio premium sincroniza estado, actualiza `user.isPremium` y la tabla `subscription`.

Regla de integracion:

- Los webhooks son la fuente de verdad para cambios de suscripcion.
- Prisma conserva el estado operable para la app.

## Flujo 5: Estado premium unificado

1. El frontend consulta `GET /api/premium/status`.
2. El backend obtiene la sesion compatible con mobile.
3. Prisma lee `user.isPremium` y suscripciones activas.
4. La respuesta expone:
- `isPremium`
- `source`
- resumen de suscripciones
- suscripcion actual, si existe

Uso:

- La UI decide bloquear o desbloquear features segun ese estado.

## Flujo 6: Relacion auth -> DB -> programas

1. El usuario entra autenticado.
2. `enrollInProgram` crea o reutiliza el enrolamiento.
3. Prisma incrementa `participantCount`.
4. `program-detail-page` y `ProgramSessionClient` usan ese estado para mostrar progreso.
5. La finalizacion de sesiones actualiza progreso en DB y refresca la vista del programa.

## Reglas del flujo

- Auth valida identidad.
- Prisma guarda el estado persistente.
- Premium no se calcula en la UI.
- Webhooks actualizan la verdad del sistema.
- Las acciones protegidas deben fallar sin sesion.

## Casos de integracion a verificar

| ID | Escenario | Resultado esperado |
|----|-----------|--------------------|
| INT-01-01 | Registro exitoso | Usuario creado en Auth y disponible para operar con Prisma |
| INT-01-02 | Accion sin sesion | Respuesta `Unauthorized` |
| INT-01-03 | Checkout con usuario autenticado | Se crea sesion de pago |
| INT-01-04 | Webhook Stripe valido | Estado premium actualizado en DB |
| INT-01-05 | Webhook RevenueCat valido | `user.isPremium` y `subscription` sincronizados |
| INT-01-06 | Consulta premium status | Respuesta refleja DB y suscripciones activas |
| INT-01-07 | Enrolamiento de programa | Se crea registro y se incrementa el contador |

## Criterio de aceptacion

- El documento debe servir como base para pruebas de integracion y seguimiento del sprint.
- Los flujos deben ser consistentes con el comportamiento real de los archivos citados.
- No debe quedar ambiguedad entre autenticar, persistir y sincronizar premium.

Loading