Skip to main content

Casos de prueba: Auth y API Keys

Documenta los tests unitarios (Jest + @nestjs/testing) de los módulos auth y api-key. Todos usan mocks de repositorios y servicios; no tocan base de datos.

Specs fuente (bajo src/app/modules/):

  • auth/controllers/auth.controller.spec.ts
  • auth/services/auth.service.spec.ts
  • api-key/controllers/api-key.controller.spec.ts
  • api-key/services/api-key.service.spec.ts

Resumen: 4 specs, 22 suites describe (incluye las raíces), 48 tests (46 bloques it(); uno es un it.each con 3 filas).

auth.controller.spec.ts​

AuthController / login​

TestEscenarioResultado esperadoMocks
should return message and token on successful loginAuthService.login resuelve { token } y se llama controller.login(dto, 'es')Llama al servicio con (dto, lang) y devuelve { message: 'auth.loginSuccess', data: { token } }AuthService.login, TranslateService.get (devuelve la clave)
should propagate UnauthorizedException on invalid credentialsEl servicio rechaza con UnauthorizedExceptionEl controlador propaga UnauthorizedExceptionAuthService.login

auth.service.spec.ts​

AuthService / login​

TestEscenarioResultado esperadoMocks
should return a token on valid credentialsUsuario existe, bcrypt.compare es true, tenant activoBusca por email, compara la contraseña, firma JWT con { sub: identifier, email } y devuelve { token }AuthRepository.findByEmail, bcrypt.compare, TenantService.findByMongoId, JwtService.sign
should throw UnauthorizedException when user is not foundfindByEmail devuelve nullLanza UnauthorizedExceptionAuthRepository.findByEmail
should throw UnauthorizedException when user is inactiveUsuario con isActive: falseLanza UnauthorizedExceptionAuthRepository.findByEmail
should throw UnauthorizedException when password is invalidbcrypt.compare devuelve falseLanza UnauthorizedExceptionAuthRepository.findByEmail, bcrypt.compare
rejects valid credentials when the tenant is %s (it.each, 3 filas: deactivated, with a non active status, missing)Credenciales válidas pero tenant con isActive: false, con status: 'inactive' o inexistente (null)Lanza UnauthorizedException, traduce auth.tenantInactive y no firma el JWTAuthRepository.findByEmail, bcrypt.compare, TenantService.findByMongoId, TranslateService.get, JwtService.sign
does not reveal the tenant state when the password is wrongContraseña incorrecta y tenant inactivoLanza UnauthorizedException con auth.invalidCredentials; ni siquiera consulta el tenant (no se filtra su estado)AuthRepository.findByEmail, bcrypt.compare, TenantService.findByMongoId, TranslateService.get

AuthService / tenant state on frame endpoints (B4)​

TestEscenarioResultado esperadoMocks
frameContact rejects a tenant whose status is not activefindByTokenValue devuelve tenant con status: 'inactive'UnauthorizedException; no se llama a searchContactTenantService.findByTokenValue, ExternalRequestService.searchContact
frameLogin rejects a tenant whose status is not activeIgual, para frameLogin con type: 'user'UnauthorizedException; no se llama a findByExternalIdAndTenantTenantService.findByTokenValue, UserRepository.findByExternalIdAndTenant

AuthService / frameContact​

TestEscenarioResultado esperadoMocks
should return upserted contact when token and external search are validToken válido, la búsqueda externa devuelve datos, findOrCreateFromExternal devuelve { contact, created: true }Busca el tenant por token, llama searchContact(url, method, token, id), hace upsert con (tenantId, externalData) y devuelve { contact, created: true }TenantService.findByTokenValue, ExternalRequestService.searchContact, ContactService.findOrCreateFromExternal
should throw UnauthorizedException when token is not foundfindByTokenValue devuelve nullUnauthorizedException; no se consulta el endpoint externoTenantService.findByTokenValue, ExternalRequestService.searchContact
should throw UnauthorizedException when tenant is inactiveTenant con isActive: falseUnauthorizedException; no se consulta el endpoint externoTenantService.findByTokenValue, ExternalRequestService.searchContact
should throw BadRequestException when tenant has no externalIntegrationTenant con externalIntegration: nullBadRequestException; no se consulta el endpoint externoTenantService.findByTokenValue, ExternalRequestService.searchContact
should create a placeholder contact when external endpoint returns nullLa búsqueda externa devuelve nullHace upsert con un contacto placeholder (externalId = String(id), username que empieza por user-) y devuelve { contact, created: true }TenantService.findByTokenValue, ExternalRequestService.searchContact, ContactService.findOrCreateFromExternal

api-key.controller.spec.ts​

ApiKeyController / create​

TestEscenarioResultado esperadoMocks
should return message, record and plainKey on creationEl servicio devuelve { message, data: { record, plainKey } }Delega con (dto, lang), devuelve la respuesta del servicio y plainKey está definidoApiKeyService.create
should propagate NotFoundException when tenant does not existEl servicio rechaza con NotFoundException (tenant inexistente)El controlador propaga NotFoundExceptionApiKeyService.create

ApiKeyController / findAll​

TestEscenarioResultado esperadoMocks
should return message and array of api keysEl servicio devuelve una lista con una key{ message: 'apiKey.findAll', data: { results: [key] } }ApiKeyService.findAll, TranslateService.get
should return empty array when no keys existEl servicio devuelve []{ message: 'apiKey.findAll', data: { results: [] } }ApiKeyService.findAll

ApiKeyController / findOne​

TestEscenarioResultado esperadoMocks
should return message and api key when foundEl servicio devuelve la keyDelega con (id, lang); devuelve { message: 'apiKey.findOne', data: { results: [key] } }ApiKeyService.findOne
should propagate NotFoundException when key not foundEl servicio rechaza con NotFoundExceptionPropaga NotFoundExceptionApiKeyService.findOne

ApiKeyController / findByTenant​

TestEscenarioResultado esperadoMocks
should return all keys assigned to the tenantEl servicio devuelve las keys del tenantDelega con (tenantId, lang); devuelve { message: 'apiKey.findAll', data: { results: [key] } }ApiKeyService.findByTenant
should return empty array when tenant has no keysEl servicio devuelve []data.results es []ApiKeyService.findByTenant
should propagate NotFoundException when tenant does not existEl servicio rechaza con NotFoundExceptionPropaga NotFoundExceptionApiKeyService.findByTenant

ApiKeyController / update​

TestEscenarioResultado esperadoMocks
should return message and updated api keydto = { name, isActive: false }, el servicio devuelve la key actualizadaDelega con (id, dto, lang); devuelve { message: 'apiKey.updated', data: updated }ApiKeyService.update
should propagate NotFoundException when key not foundEl servicio rechaza con NotFoundExceptionPropaga NotFoundExceptionApiKeyService.update

ApiKeyController / remove​

TestEscenarioResultado esperadoMocks
should return message and removed api keyEl servicio devuelve la key eliminadaDelega con (id, lang); devuelve { message: 'apiKey.deleted', data: key }ApiKeyService.remove
should propagate NotFoundException when key not foundEl servicio rechaza con NotFoundExceptionPropaga NotFoundExceptionApiKeyService.remove

api-key.service.spec.ts​

ApiKeyService / hashApiKey​

TestEscenarioResultado esperadoMocks
should produce a consistent SHA-256 hex hashSe hashea dos veces la misma cadenaAmbos hashes son iguales y miden 64 caracteresNinguno
should produce different hashes for different inputsSe hashean key-a y key-bLos hashes difierenNinguno

ApiKeyService / create​

TestEscenarioResultado esperadoMocks
should throw NotFoundException when tenant does not existfindByIdentifier devuelve nullNotFoundException; no se persiste nadaTenantService.findByIdentifier, ApiKeyRepository.create
should generate a plain key, hash it, persist and return bothTenant existePersiste name, tenantId, code (string) y dateExpiration (Date); devuelve data.plainKey y data.recordTenantService.findByIdentifier, ApiKeyRepository.create, crypto.randomBytes
should store code as SHA-256 of the plain keySe captura el code enviado al repositoriocode es igual a hashApiKey(plainKey) (nunca se guarda la clave en claro)TenantService.findByIdentifier, ApiKeyRepository.create, crypto.randomBytes

ApiKeyService / findAll​

TestEscenarioResultado esperadoMocks
should return an array of api keysEl repositorio devuelve una keyDevuelve el arreglo del repositorioApiKeyRepository.findAll
should return an empty array when no keys existEl repositorio devuelve []Devuelve []ApiKeyRepository.findAll

ApiKeyService / findOne​

TestEscenarioResultado esperadoMocks
should return the api key when foundfindById devuelve la keyLlama findById(id) y devuelve la keyApiKeyRepository.findById
should throw NotFoundException when key does not existfindById devuelve nullNotFoundExceptionApiKeyRepository.findById

ApiKeyService / findByTenant​

TestEscenarioResultado esperadoMocks
should throw NotFoundException when tenant does not existTenant inexistenteNotFoundException; no se consulta findByTenantIdTenantService.findByIdentifier, ApiKeyRepository.findByTenantId
should return all keys for an existing tenantTenant existe y tiene keysValida el tenant, llama findByTenantId(tenantId) y devuelve las keysTenantService.findByIdentifier, ApiKeyRepository.findByTenantId
should return empty array when tenant has no keysTenant existe sin keysDevuelve []TenantService.findByIdentifier, ApiKeyRepository.findByTenantId

ApiKeyService / findByCode​

TestEscenarioResultado esperadoMocks
should delegate to repository findByCodeSe busca por un hashDelega a findByCode('some-hash') y devuelve su resultadoApiKeyRepository.findByCode

ApiKeyService / update​

TestEscenarioResultado esperadoMocks
should update name and isActive without touching codedto = { name, isActive: false }El repositorio recibe los datos sin el campo code; devuelve la key actualizadaApiKeyRepository.update
should convert dateExpiration string to Date on updatedto.dateExpiration es un string ISOEl repositorio recibe dateExpiration como DateApiKeyRepository.update
should throw NotFoundException when key does not existupdate devuelve nullNotFoundExceptionApiKeyRepository.update

ApiKeyService / remove​

TestEscenarioResultado esperadoMocks
should remove and return the api keyremove devuelve la keyLlama remove(id) y devuelve la keyApiKeyRepository.remove
should throw NotFoundException when key does not existremove devuelve nullNotFoundExceptionApiKeyRepository.remove