Casos de prueba — Settings y Queue
Catálogo de los tests de los módulos settings y queue. Specs fuente (relativas a src/app/modules/):
settings/controllers/tenant-settings.controller.spec.ts
settings/repositories/tenant-settings.repository.spec.ts
settings/services/tenant-settings.service.spec.ts
settings/services/auto-resolution-precedence.service.spec.ts
settings/services/business-hours-evaluator.service.spec.ts
settings/settings-module-wiring.integration.spec.ts
queue/queue.module.spec.ts
Resumen: 7 specs y 36 tests (it): 30 de settings (controlador 3, repositorio 5, servicio 7, precedencia 7, horario 7, wiring 1) y 6 de queue. El spec del controlador define 2 it dentro de un describe.each de 6 rutas, por lo que en ejecución corren 12 casos parametrizados más 1 adicional.
settings/controllers/tenant-settings.controller.spec.ts
Integración HTTP (supertest) con PermissionsGuard real y ResponseInterceptor global; AuthGuard se reemplaza por un FakeAuthGuard que inyecta tenantId y roleId.
TenantSettingsController — permission guard integration › $method $path (describe.each)
Se ejecuta para cada ruta: GET /v1/settings/get-all (SETTINGS_READ), PATCH /v1/settings/general, PATCH /v1/settings/attention/schedule, PATCH /v1/settings/attention/auto-resolution, PATCH /v1/settings/attention/csat y PATCH /v1/settings/attention/scheduled-messages (todas SETTINGS_UPDATE).
| Test | Escenario | Resultado esperado | Mocks |
|---|
returns 403 Forbidden when the resolved acronym set is missing the required permission | El set de acrónimos resuelto está vacío | La ruta responde 403 | TenantSettingsService, TranslateService, PermissionResolverService, FakeAuthGuard |
returns 200 OK when the resolved acronym set includes the required permission | El set incluye el acrónimo requerido por la ruta | Responde 200 y el resolver se invoca con (tenantId, roleId) | Los mismos |
TenantSettingsController — permission guard integration › GET /v1/settings/get-all — response body through the real global interceptor
| Test | Escenario | Resultado esperado | Mocks |
|---|
returns the actual settings payload, not an empty paginated shell | GET /get-all con SETTINGS_READ pasando por el ResponseInterceptor real | data.results tiene 1 elemento y sus general y autoResolution coinciden con los settings por defecto (el payload no se descarta) | Los mismos |
settings/repositories/tenant-settings.repository.spec.ts
Integración con MongoDB real en memoria (mongodb-memory-server).
TenantSettingsRepository — integration (mongodb-memory-server) › findByTenantId
| Test | Escenario | Resultado esperado | Mocks |
|---|
returns null when no settings exist for the tenant | Tenant sin documento | Devuelve null | Ninguno (MongoDB en memoria) |
returns the document scoped to the tenant | Se crea el documento con valores por defecto para el tenant A | Devuelve el documento con el tenantId del tenant A | Ninguno |
TenantSettingsRepository — integration (mongodb-memory-server) › create
| Test | Escenario | Resultado esperado | Mocks |
|---|
stores the document with defaults for the tenant | create con los valores por defecto del dominio | Documento con tenantId, general.language = 'es' y autoResolution.resolveInMinutes = 5 | Ninguno |
TenantSettingsRepository — integration (mongodb-memory-server) › updateSection
| Test | Escenario | Resultado esperado | Mocks |
|---|
replaces only the requested section, scoped to tenant | updateSection(A, 'general', { language: 'en', timezone: 'UTC' }) | Se actualiza general y las demás secciones (p. ej. autoResolution.resolveInMinutes = 5) quedan intactas | Ninguno |
returns null when the tenant has no settings document | updateSection sobre el tenant B sin documento | Devuelve null | Ninguno |
settings/services/tenant-settings.service.spec.ts
TenantSettingsService › getOrCreate
| Test | Escenario | Resultado esperado | Mocks |
|---|
returns existing settings mapped to TenantSettingsDomain when found | findByTenantId devuelve un documento con general en inglés/UTC | No llama create; devuelve una instancia de TenantSettingsDomain con general = { language: 'en', timezone: 'UTC' } | TenantSettingsRepository |
creates default settings when none exist for the tenant | findByTenantId devuelve null | Llama create(tenantId, defaults) con los valores por defecto (idioma es, zona America/Mexico_City, secciones deshabilitadas, resolveInMinutes 5, límites de mensajes programados 5 / 30 / 10 / 50) y devuelve un TenantSettingsDomain | TenantSettingsRepository |
TenantSettingsService › updateGeneral
| Test | Escenario | Resultado esperado | Mocks |
|---|
ensures settings exist then replaces the general section | Settings existentes; nuevo general en/UTC | updateSection(tenantId, 'general', newGeneral) y el resultado refleja el nuevo general | TenantSettingsRepository |
TenantSettingsService › updateBusinessHours
| Test | Escenario | Resultado esperado | Mocks |
|---|
replaces the full businessHours section | Nuevo horario con un día habilitado (lunes 08:00-17:00) | updateSection(tenantId, 'businessHours', newSchedule) y el resultado refleja la sección | TenantSettingsRepository |
TenantSettingsService › updateAutoResolution
| Test | Escenario | Resultado esperado | Mocks |
|---|
replaces the full autoResolution section | Nueva configuración de auto-resolución | updateSection(tenantId, 'autoResolution', ...) y el resultado refleja la sección | TenantSettingsRepository |
TenantSettingsService › updateCsat
| Test | Escenario | Resultado esperado | Mocks |
|---|
replaces the full csat section | Nueva configuración CSAT con mensajes por calificación | updateSection(tenantId, 'csat', ...) y el resultado refleja la sección | TenantSettingsRepository |
TenantSettingsService › updateScheduledMessages
| Test | Escenario | Resultado esperado | Mocks |
|---|
replaces the full scheduledMessages section | Nueva configuración de mensajes programados (notas no permitidas, límites reducidos, políticas send) | updateSection(tenantId, 'scheduledMessages', ...) y el resultado refleja la sección | TenantSettingsRepository |
settings/services/auto-resolution-precedence.service.spec.ts
Servicio puro, sin mocks.
AutoResolutionPrecedenceService
| Test | Escenario | Resultado esperado | Mocks |
|---|
uses the tenant config when there is no department | resolve(null, tenantConfig) | Devuelve la configuración del tenant (enabled, resolveInMinutes, notifyInMinutes, notificationMessage, sendFarewellMessage) | Ninguno |
uses the tenant config when the department does not override | Departamento con overrideGlobalResolution: false | Prevalece la configuración del tenant (10 min, mensaje y despedida del tenant) | Ninguno |
uses the department config when overrideGlobalResolution is true | Departamento con overrideGlobalResolution: true | Prevalece la configuración del departamento (2 min, su mensaje, sin despedida) | Ninguno |
reports disabled when the effective source has enabled=false | Tenant con enabled: false y sin departamento | enabled es false | Ninguno |
respects department precedence even when the department disables auto-resolution and the tenant enables it | Departamento con override y enabled: false, tenant habilitado | enabled es false | Ninguno |
takes sendFarewellMessage from the department when it overrides, from the tenant otherwise | Departamento con override, departamento sin override y sin departamento | false con override; true en los otros dos casos (valor del tenant) | Ninguno |
does not expose a farewell text: the copy lives only on the connection | resolve(null, config) | El resultado no tiene la propiedad farewellMessage | Ninguno |
settings/services/business-hours-evaluator.service.spec.ts
Servicio puro con zonas horarias IANA (Luxon), sin mocks.
BusinessHoursEvaluatorService
| Test | Escenario | Resultado esperado | Mocks |
|---|
returns isWithinHours=true when current local time falls inside an enabled day range | Miércoles 09:00 local (America/Mexico_City) con rango 09:00-18:00 habilitado | isWithinHours es true | Ninguno |
returns isWithinHours=false when the current day is not enabled | Mismo instante pero el día está deshabilitado | false | Ninguno |
returns isWithinHours=false when the current local time is before the configured range | 07:00 local, rango 09:00-18:00 | false | Ninguno |
returns isWithinHours=false at/after the range end (end boundary is exclusive) | Exactamente 18:00 local | false (el fin es exclusivo) | Ninguno |
returns isWithinHours=false when there is no configured entry for the current weekday | Solo hay entrada para lunes y la fecha es miércoles | false | Ninguno |
evaluates using the tenant IANA timezone via Luxon, correctly across a DST boundary | America/New_York, mismo 13:00 UTC en julio (DST) y en enero (sin DST) | true en julio (09:00 local) y false en enero (08:00 local) | Ninguno |
does NOT support cross-midnight ranges (startTime > endTime never matches) — documented model limitation | Rango 22:00-02:00 evaluado a las 17:00 local | false; documenta que no hay soporte para rangos que cruzan medianoche | Ninguno |
settings/settings-module-wiring.integration.spec.ts
Regresión de la dependencia circular UserModule / SettingsModule: compila el grafo real de DI sin sobrescribir providers.
UserModule y SettingsModule wiring — integration (real DI graph)
| Test | Escenario | Resultado esperado | Mocks |
|---|
compiles the real module graph without any provider overrides | Se importan ConfigModule, MongooseModule (Mongo en memoria), CoreAuthModule, CorePermissionsModule, StorageModule, MediaModule, UserModule y SettingsModule y se llama .compile() | El módulo compila sin errores de DI y se cierra correctamente | Ninguno (MongoDB en memoria; sin overrides) |
queue/queue.module.spec.ts
createRedisConnectionOptions (W13 — resilient Redis connection)
ConfigService simulado con redis.host, redis.port, redis.password y redis.db.
| Test | Escenario | Resultado esperado | Mocks |
|---|
sets maxRetriesPerRequest to null so a BullMQ Worker can attach without throwing | Opciones con config por defecto | maxRetriesPerRequest es null | ConfigService.get |
never gives up reconnecting (retryStrategy always returns a finite delay, never false/undefined) | retryStrategy con intentos 1, 5, 50 y 1000 | Siempre devuelve un número mayor que 0 | ConfigService.get |
caps the reconnect backoff instead of growing unbounded (no log-spam-then-thundering-herd) | retryStrategy(100000) | Devuelve REDIS_RECONNECT.MAX_DELAY_MS | ConfigService.get |
keeps commands queued while disconnected instead of rejecting immediately | Opciones por defecto | enableOfflineQueue es true | ConfigService.get |
still forwards host/port/password/db from config | Config con password secret y db 2 | Las opciones reflejan host redis, puerto 6379, password y db | ConfigService.get |
never sets commandTimeout on the shared connection (CRITICAL-B — would race the Worker idle poll) | Opciones por defecto | commandTimeout es undefined (guarda de regresión) | ConfigService.get |