miinventario-v2/apps/backend/test/validations.e2e-spec.ts
rckrdmrd 1a53b5c4d3 [MIINVENTARIO] feat: Initial commit - Sistema de inventario con análisis de video IA
- Backend NestJS con módulos de autenticación, inventario, créditos
- Frontend React con dashboard y componentes UI
- Base de datos PostgreSQL con migraciones
- Tests E2E configurados
- Configuración de Docker y deployment

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-13 02:25:48 -06:00

272 lines
9.4 KiB
TypeScript

import { Test, TestingModule } from '@nestjs/testing';
import { INestApplication, ValidationPipe } from '@nestjs/common';
import * as request from 'supertest';
import { AppModule } from '../src/app.module';
import { getRepositoryToken } from '@nestjs/typeorm';
import { User } from '../src/modules/users/entities/user.entity';
import { Store } from '../src/modules/stores/entities/store.entity';
import { StoreUser, StoreUserRole } from '../src/modules/stores/entities/store-user.entity';
import { Video, VideoStatus } from '../src/modules/videos/entities/video.entity';
import { InventoryItem } from '../src/modules/inventory/entities/inventory-item.entity';
import { ValidationRequest, ValidationRequestStatus } from '../src/modules/validations/entities/validation-request.entity';
import { ValidationResponse } from '../src/modules/validations/entities/validation-response.entity';
import { JwtService } from '@nestjs/jwt';
import { Repository } from 'typeorm';
describe('ValidationsController (e2e)', () => {
let app: INestApplication;
let jwtService: JwtService;
let userRepository: Repository<User>;
let storeRepository: Repository<Store>;
let storeUserRepository: Repository<StoreUser>;
let videoRepository: Repository<Video>;
let inventoryRepository: Repository<InventoryItem>;
let validationRequestRepository: Repository<ValidationRequest>;
let validationResponseRepository: Repository<ValidationResponse>;
let testUser: User;
let testStore: Store;
let testVideo: Video;
let testItems: InventoryItem[];
let testValidationRequest: ValidationRequest;
let authToken: string;
beforeAll(async () => {
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
}).compile();
app = moduleFixture.createNestApplication();
app.useGlobalPipes(new ValidationPipe({ whitelist: true }));
await app.init();
jwtService = moduleFixture.get<JwtService>(JwtService);
userRepository = moduleFixture.get(getRepositoryToken(User));
storeRepository = moduleFixture.get(getRepositoryToken(Store));
storeUserRepository = moduleFixture.get(getRepositoryToken(StoreUser));
videoRepository = moduleFixture.get(getRepositoryToken(Video));
inventoryRepository = moduleFixture.get(getRepositoryToken(InventoryItem));
validationRequestRepository = moduleFixture.get(getRepositoryToken(ValidationRequest));
validationResponseRepository = moduleFixture.get(getRepositoryToken(ValidationResponse));
// Create test user
testUser = await userRepository.save({
phone: '+529876543210',
name: 'Test Validation User',
});
// Create test store
testStore = await storeRepository.save({
name: 'Test Validation Store',
ownerId: testUser.id,
});
// Create store-user relationship
await storeUserRepository.save({
storeId: testStore.id,
userId: testUser.id,
role: StoreUserRole.OWNER,
});
// Create test video
testVideo = await videoRepository.save({
storeId: testStore.id,
uploadedById: testUser.id,
fileName: 'test-validation.mp4',
status: VideoStatus.COMPLETED,
});
// Create test inventory items
testItems = await inventoryRepository.save([
{
storeId: testStore.id,
detectedByVideoId: testVideo.id,
name: 'Validation Item 1',
quantity: 5,
detectionConfidence: 0.65,
},
{
storeId: testStore.id,
detectedByVideoId: testVideo.id,
name: 'Validation Item 2',
quantity: 10,
detectionConfidence: 0.85,
},
{
storeId: testStore.id,
detectedByVideoId: testVideo.id,
name: 'Validation Item 3',
quantity: 3,
detectionConfidence: 0.55,
},
]);
// Create test validation request
const expiresAt = new Date();
expiresAt.setHours(expiresAt.getHours() + 24);
testValidationRequest = await validationRequestRepository.save({
videoId: testVideo.id,
userId: testUser.id,
storeId: testStore.id,
totalItems: 3,
triggerReason: 'LOW_CONFIDENCE_DETECTIONS',
probabilityScore: 0.35,
expiresAt,
});
// Generate auth token
authToken = jwtService.sign({ sub: testUser.id, phone: testUser.phone });
});
afterAll(async () => {
// Cleanup
await validationResponseRepository.delete({ requestId: testValidationRequest.id });
await validationRequestRepository.delete({ id: testValidationRequest.id });
for (const item of testItems) {
await inventoryRepository.delete({ id: item.id });
}
await videoRepository.delete({ id: testVideo.id });
await storeUserRepository.delete({ storeId: testStore.id });
await storeRepository.delete({ id: testStore.id });
await userRepository.delete({ id: testUser.id });
await app.close();
});
describe('/validations/:requestId/items (GET)', () => {
it('should return validation items', async () => {
const response = await request(app.getHttpServer())
.get(`/validations/${testValidationRequest.id}/items`)
.set('Authorization', `Bearer ${authToken}`)
.expect(200);
expect(response.body.request).toBeDefined();
expect(response.body.request.id).toBe(testValidationRequest.id);
expect(response.body.items).toBeDefined();
expect(Array.isArray(response.body.items)).toBe(true);
});
it('should return 404 for non-existent request', async () => {
await request(app.getHttpServer())
.get('/validations/00000000-0000-0000-0000-000000000000/items')
.set('Authorization', `Bearer ${authToken}`)
.expect(404);
});
it('should reject without auth', async () => {
await request(app.getHttpServer())
.get(`/validations/${testValidationRequest.id}/items`)
.expect(401);
});
});
describe('/validations/:requestId/submit (POST)', () => {
it('should submit validation responses', async () => {
// First, create a fresh validation request for this test
const expiresAt = new Date();
expiresAt.setHours(expiresAt.getHours() + 24);
const freshRequest = await validationRequestRepository.save({
videoId: testVideo.id,
userId: testUser.id,
storeId: testStore.id,
totalItems: 3,
triggerReason: 'TEST',
probabilityScore: 0.5,
expiresAt,
});
const response = await request(app.getHttpServer())
.post(`/validations/${freshRequest.id}/submit`)
.set('Authorization', `Bearer ${authToken}`)
.send({
responses: [
{ inventoryItemId: testItems[0].id, isCorrect: true },
{ inventoryItemId: testItems[1].id, isCorrect: false, correctedQuantity: 12 },
{ inventoryItemId: testItems[2].id, isCorrect: true },
],
})
.expect(201);
expect(response.body.message).toBe('Validacion completada exitosamente');
expect(response.body.itemsValidated).toBe(3);
expect(response.body.creditsRewarded).toBeGreaterThanOrEqual(0);
// Cleanup
await validationResponseRepository.delete({ requestId: freshRequest.id });
await validationRequestRepository.delete({ id: freshRequest.id });
});
it('should reject invalid item IDs', async () => {
await request(app.getHttpServer())
.post(`/validations/${testValidationRequest.id}/submit`)
.set('Authorization', `Bearer ${authToken}`)
.send({
responses: [
{ inventoryItemId: 'invalid-uuid', isCorrect: true },
],
})
.expect(400);
});
});
describe('/validations/:requestId/skip (POST)', () => {
it('should skip validation', async () => {
// Create fresh request for skip test
const expiresAt = new Date();
expiresAt.setHours(expiresAt.getHours() + 24);
const skipRequest = await validationRequestRepository.save({
videoId: testVideo.id,
userId: testUser.id,
storeId: testStore.id,
totalItems: 3,
triggerReason: 'TEST_SKIP',
probabilityScore: 0.5,
expiresAt,
});
const response = await request(app.getHttpServer())
.post(`/validations/${skipRequest.id}/skip`)
.set('Authorization', `Bearer ${authToken}`)
.expect(201);
expect(response.body.message).toBe('Validacion omitida');
// Verify status changed
const updatedRequest = await validationRequestRepository.findOne({
where: { id: skipRequest.id },
});
expect(updatedRequest?.status).toBe(ValidationRequestStatus.SKIPPED);
// Cleanup
await validationRequestRepository.delete({ id: skipRequest.id });
});
it('should reject skip for already processed request', async () => {
// Create and complete a request
const expiresAt = new Date();
expiresAt.setHours(expiresAt.getHours() + 24);
const completedRequest = await validationRequestRepository.save({
videoId: testVideo.id,
userId: testUser.id,
storeId: testStore.id,
totalItems: 3,
triggerReason: 'TEST_COMPLETED',
probabilityScore: 0.5,
status: ValidationRequestStatus.COMPLETED,
expiresAt,
});
await request(app.getHttpServer())
.post(`/validations/${completedRequest.id}/skip`)
.set('Authorization', `Bearer ${authToken}`)
.expect(400);
// Cleanup
await validationRequestRepository.delete({ id: completedRequest.id });
});
});
});