FASE 7 COMPLETADA: Testing y Lanzamiento - PROYECTO FINALIZADO
Some checks failed
CI/CD Pipeline / 🧪 Tests (push) Has been cancelled
CI/CD Pipeline / 🏗️ Build (push) Has been cancelled
CI/CD Pipeline / 🚀 Deploy to Staging (push) Has been cancelled
CI/CD Pipeline / 🚀 Deploy to Production (push) Has been cancelled
CI/CD Pipeline / 🏷️ Create Release (push) Has been cancelled
CI/CD Pipeline / 🧹 Cleanup (push) Has been cancelled

Implementados 4 módulos con agent swarm:

1. TESTING FUNCIONAL (Jest)
   - Configuración Jest + ts-jest
   - Tests unitarios: auth, booking, court (55 tests)
   - Tests integración: routes (56 tests)
   - Factories y utilidades de testing
   - Coverage configurado (70% servicios)
   - Scripts: test, test:watch, test:coverage

2. TESTING DE USUARIO (Beta)
   - Sistema de beta testers
   - Feedback con categorías y severidad
   - Beta issues tracking
   - 8 testers de prueba creados
   - API completa para gestión de feedback

3. DOCUMENTACIÓN COMPLETA
   - API.md - 150+ endpoints documentados
   - SETUP.md - Guía de instalación
   - DEPLOY.md - Deploy en VPS
   - ARCHITECTURE.md - Arquitectura del sistema
   - APP_STORE.md - Material para stores
   - Postman Collection completa
   - PM2 ecosystem config
   - Nginx config con SSL

4. GO LIVE Y PRODUCCIÓN
   - Sistema de monitoreo (logs, health checks)
   - Servicio de alertas multi-canal
   - Pre-deploy check script
   - Docker + docker-compose producción
   - Backup automatizado
   - CI/CD GitHub Actions
   - Launch checklist completo

ESTADÍSTICAS FINALES:
- Fases completadas: 7/7
- Archivos creados: 250+
- Líneas de código: 60,000+
- Endpoints API: 150+
- Tests: 110+
- Documentación: 5,000+ líneas

PROYECTO COMPLETO Y LISTO PARA PRODUCCIÓN
This commit is contained in:
2026-01-31 22:30:44 +00:00
parent e135e7ad24
commit dd10891432
61 changed files with 19256 additions and 142 deletions

View File

@@ -0,0 +1,308 @@
import { PrismaClient, User, Court, Booking, Payment, CourtSchedule, Prisma } from '@prisma/client';
import { hashPassword } from '../../src/utils/password';
import { UserRole, CourtType, BookingStatus, PaymentStatus } from '../../src/utils/constants';
import { getPrismaClient } from './testDb';
// Type for overrides
export type Overrides<T> = Partial<T>;
// Prisma client
let prisma: PrismaClient;
function getClient(): PrismaClient {
if (!prisma) {
prisma = getPrismaClient();
}
return prisma;
}
/**
* User Factory
*/
export interface CreateUserInput {
email?: string;
password?: string;
firstName?: string;
lastName?: string;
phone?: string;
role?: string;
playerLevel?: string;
handPreference?: string;
positionPreference?: string;
isActive?: boolean;
avatarUrl?: string;
city?: string;
bio?: string;
}
export async function createUser(overrides: CreateUserInput = {}): Promise<User> {
const client = getClient();
const defaultPassword = 'Password123!';
const hashedPassword = await hashPassword(overrides.password || defaultPassword);
const userData: Prisma.UserCreateInput = {
email: overrides.email || `user_${Date.now()}_${Math.random().toString(36).substring(2, 9)}@test.com`,
password: hashedPassword,
firstName: overrides.firstName || 'Test',
lastName: overrides.lastName || 'User',
phone: overrides.phone || '+1234567890',
role: overrides.role || UserRole.PLAYER,
playerLevel: overrides.playerLevel || 'BEGINNER',
handPreference: overrides.handPreference || 'RIGHT',
positionPreference: overrides.positionPreference || 'BOTH',
isActive: overrides.isActive ?? true,
avatarUrl: overrides.avatarUrl,
city: overrides.city,
bio: overrides.bio,
};
return client.user.create({ data: userData });
}
/**
* Create an admin user
*/
export async function createAdminUser(overrides: CreateUserInput = {}): Promise<User> {
return createUser({
...overrides,
role: UserRole.ADMIN,
});
}
/**
* Create a superadmin user
*/
export async function createSuperAdminUser(overrides: CreateUserInput = {}): Promise<User> {
return createUser({
...overrides,
role: UserRole.SUPERADMIN,
});
}
/**
* Court Factory
*/
export interface CreateCourtInput {
name?: string;
description?: string;
type?: string;
isIndoor?: boolean;
hasLighting?: boolean;
hasParking?: boolean;
pricePerHour?: number;
imageUrl?: string;
isActive?: boolean;
}
export async function createCourt(overrides: CreateCourtInput = {}): Promise<Court> {
const client = getClient();
const courtData: Prisma.CourtCreateInput = {
name: overrides.name || `Court ${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
description: overrides.description || 'A test court',
type: overrides.type || CourtType.PANORAMIC,
isIndoor: overrides.isIndoor ?? false,
hasLighting: overrides.hasLighting ?? true,
hasParking: overrides.hasParking ?? false,
pricePerHour: overrides.pricePerHour ?? 2000,
imageUrl: overrides.imageUrl,
isActive: overrides.isActive ?? true,
};
return client.court.create({ data: courtData });
}
/**
* Create court with default schedules
*/
export async function createCourtWithSchedules(overrides: CreateCourtInput = {}): Promise<Court & { schedules: CourtSchedule[] }> {
const client = getClient();
const court = await createCourt(overrides);
// Create schedules for all days (0-6)
const schedules: Prisma.CourtScheduleCreateManyInput[] = [];
for (let day = 0; day <= 6; day++) {
schedules.push({
courtId: court.id,
dayOfWeek: day,
openTime: '08:00',
closeTime: '23:00',
});
}
await client.courtSchedule.createMany({ data: schedules });
return client.court.findUnique({
where: { id: court.id },
include: { schedules: true },
}) as Promise<Court & { schedules: CourtSchedule[] }>;
}
/**
* Booking Factory
*/
export interface CreateBookingInput {
userId?: string;
courtId?: string;
date?: Date;
startTime?: string;
endTime?: string;
status?: string;
totalPrice?: number;
notes?: string;
}
export async function createBooking(overrides: CreateBookingInput = {}): Promise<Booking> {
const client = getClient();
// Create user if not provided
let userId = overrides.userId;
if (!userId) {
const user = await createUser();
userId = user.id;
}
// Create court if not provided
let courtId = overrides.courtId;
if (!courtId) {
const court = await createCourt();
courtId = court.id;
}
// Default to tomorrow
const tomorrow = new Date();
tomorrow.setDate(tomorrow.getDate() + 1);
tomorrow.setHours(0, 0, 0, 0);
const bookingData: Prisma.BookingCreateInput = {
user: { connect: { id: userId } },
court: { connect: { id: courtId } },
date: overrides.date || tomorrow,
startTime: overrides.startTime || '10:00',
endTime: overrides.endTime || '11:00',
status: overrides.status || BookingStatus.PENDING,
totalPrice: overrides.totalPrice ?? 2000,
notes: overrides.notes,
};
return client.booking.create({ data: bookingData });
}
/**
* Create a confirmed booking
*/
export async function createConfirmedBooking(overrides: CreateBookingInput = {}): Promise<Booking> {
return createBooking({
...overrides,
status: BookingStatus.CONFIRMED,
});
}
/**
* Create a cancelled booking
*/
export async function createCancelledBooking(overrides: CreateBookingInput = {}): Promise<Booking> {
return createBooking({
...overrides,
status: BookingStatus.CANCELLED,
});
}
/**
* Payment Factory
*/
export interface CreatePaymentInput {
userId?: string;
type?: string;
referenceId?: string;
amount?: number;
currency?: string;
status?: string;
providerPreferenceId?: string;
providerPaymentId?: string;
paymentMethod?: string;
installments?: number;
metadata?: string;
paidAt?: Date;
}
export async function createPayment(overrides: CreatePaymentInput = {}): Promise<Payment> {
const client = getClient();
// Create user if not provided
let userId = overrides.userId;
if (!userId) {
const user = await createUser();
userId = user.id;
}
const paymentData: Prisma.PaymentCreateInput = {
user: { connect: { id: userId } },
type: overrides.type || 'BOOKING',
referenceId: overrides.referenceId || 'test-reference-id',
amount: overrides.amount ?? 2000,
currency: overrides.currency || 'ARS',
status: overrides.status || PaymentStatus.PENDING,
providerPreferenceId: overrides.providerPreferenceId || `pref_${Date.now()}`,
providerPaymentId: overrides.providerPaymentId,
paymentMethod: overrides.paymentMethod,
installments: overrides.installments,
metadata: overrides.metadata,
paidAt: overrides.paidAt,
};
return client.payment.create({ data: paymentData });
}
/**
* Create a completed payment
*/
export async function createCompletedPayment(overrides: CreatePaymentInput = {}): Promise<Payment> {
return createPayment({
...overrides,
status: PaymentStatus.COMPLETED,
paidAt: new Date(),
});
}
/**
* Bulk create multiple entities
*/
export async function createManyUsers(count: number, overrides: CreateUserInput = {}): Promise<User[]> {
const users: User[] = [];
for (let i = 0; i < count; i++) {
users.push(await createUser({
...overrides,
email: `user_${i}_${Date.now()}@test.com`,
}));
}
return users;
}
export async function createManyCourts(count: number, overrides: CreateCourtInput = {}): Promise<Court[]> {
const courts: Court[] = [];
for (let i = 0; i < count; i++) {
courts.push(await createCourt({
...overrides,
name: `Court ${i}_${Date.now()}`,
}));
}
return courts;
}
export async function createManyBookings(count: number, overrides: CreateBookingInput = {}): Promise<Booking[]> {
const bookings: Booking[] = [];
for (let i = 0; i < count; i++) {
const date = new Date();
date.setDate(date.getDate() + i + 1);
date.setHours(0, 0, 0, 0);
bookings.push(await createBooking({
...overrides,
date,
}));
}
return bookings;
}