test(frontend): add comprehensive ErrorHandlerService tests
- Test all GraphQL error codes (MAX_ENROLLMENTS, SAME_PROFESSOR, etc.) - Test network errors with different status codes - Test validation errors with field details - Test HTTP error handling (500, 0, 400) - Test unknown/null/undefined error handling - Test notification display with suggestions Increases code coverage for the error handling layer.
This commit is contained in:
parent
3d4a8b56ff
commit
dfb1341b2c
|
|
@ -0,0 +1,308 @@
|
|||
import { TestBed } from '@angular/core/testing';
|
||||
import { ErrorHandlerService } from './error-handler.service';
|
||||
import { NotificationService } from './notification.service';
|
||||
|
||||
describe('ErrorHandlerService', () => {
|
||||
let service: ErrorHandlerService;
|
||||
let notificationSpy: jasmine.SpyObj<NotificationService>;
|
||||
|
||||
beforeEach(() => {
|
||||
notificationSpy = jasmine.createSpyObj('NotificationService', ['error', 'success', 'info']);
|
||||
|
||||
TestBed.configureTestingModule({
|
||||
providers: [
|
||||
ErrorHandlerService,
|
||||
{ provide: NotificationService, useValue: notificationSpy },
|
||||
],
|
||||
});
|
||||
|
||||
service = TestBed.inject(ErrorHandlerService);
|
||||
});
|
||||
|
||||
describe('parseError', () => {
|
||||
describe('Apollo GraphQL errors', () => {
|
||||
it('should parse MAX_ENROLLMENTS error code', () => {
|
||||
const error = {
|
||||
graphQLErrors: [
|
||||
{
|
||||
message: 'Maximum enrollments reached',
|
||||
extensions: { code: 'MAX_ENROLLMENTS' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = service.parseError(error);
|
||||
|
||||
expect(result.code).toBe('MAX_ENROLLMENTS');
|
||||
expect(result.userMessage).toBe('Has alcanzado el límite máximo de 3 materias inscritas');
|
||||
expect(result.suggestion).toBe('Debes cancelar una inscripción antes de agregar otra materia');
|
||||
});
|
||||
|
||||
it('should parse SAME_PROFESSOR error code', () => {
|
||||
const error = {
|
||||
graphQLErrors: [
|
||||
{
|
||||
message: 'Already have subject with professor',
|
||||
extensions: { code: 'SAME_PROFESSOR' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = service.parseError(error);
|
||||
|
||||
expect(result.code).toBe('SAME_PROFESSOR');
|
||||
expect(result.userMessage).toBe('Ya tienes una materia con este profesor');
|
||||
});
|
||||
|
||||
it('should parse STUDENT_NOT_FOUND error code', () => {
|
||||
const error = {
|
||||
graphQLErrors: [
|
||||
{
|
||||
message: 'Student not found',
|
||||
extensions: { code: 'STUDENT_NOT_FOUND' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = service.parseError(error);
|
||||
|
||||
expect(result.code).toBe('STUDENT_NOT_FOUND');
|
||||
expect(result.userMessage).toBe('El estudiante no existe en el sistema');
|
||||
});
|
||||
|
||||
it('should parse DUPLICATE_ENROLLMENT error code', () => {
|
||||
const error = {
|
||||
graphQLErrors: [
|
||||
{
|
||||
message: 'Already enrolled',
|
||||
extensions: { code: 'DUPLICATE_ENROLLMENT' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = service.parseError(error);
|
||||
|
||||
expect(result.code).toBe('DUPLICATE_ENROLLMENT');
|
||||
expect(result.userMessage).toBe('Ya estás inscrito en esta materia');
|
||||
});
|
||||
|
||||
it('should parse VALIDATION_ERROR with field details', () => {
|
||||
const error = {
|
||||
graphQLErrors: [
|
||||
{
|
||||
message: 'Validation failed',
|
||||
extensions: {
|
||||
code: 'VALIDATION_ERROR',
|
||||
errors: [
|
||||
{ PropertyName: 'Email', ErrorMessage: 'Invalid email format' },
|
||||
{ PropertyName: 'Name', ErrorMessage: 'Name is required' },
|
||||
],
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = service.parseError(error);
|
||||
|
||||
expect(result.code).toBe('VALIDATION_ERROR');
|
||||
expect(result.userMessage).toBe('Los datos ingresados no son válidos');
|
||||
expect(result.devMessage).toContain('Email: Invalid email format');
|
||||
expect(result.devMessage).toContain('Name: Name is required');
|
||||
});
|
||||
|
||||
it('should handle unknown error code with UNKNOWN message', () => {
|
||||
const error = {
|
||||
graphQLErrors: [
|
||||
{
|
||||
message: 'Some unknown error',
|
||||
extensions: { code: 'WEIRD_ERROR' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = service.parseError(error);
|
||||
|
||||
// Service preserves original code for logging but uses UNKNOWN's message
|
||||
expect(result.userMessage).toBe('Ocurrió un error inesperado');
|
||||
});
|
||||
|
||||
it('should handle missing error code as UNKNOWN', () => {
|
||||
const error = {
|
||||
graphQLErrors: [
|
||||
{
|
||||
message: 'Error without code',
|
||||
extensions: {},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = service.parseError(error);
|
||||
|
||||
expect(result.code).toBe('UNKNOWN');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Network errors', () => {
|
||||
it('should parse network error with status 0', () => {
|
||||
const error = {
|
||||
networkError: { status: 0, message: 'Network failed' },
|
||||
message: 'Network error',
|
||||
};
|
||||
|
||||
const result = service.parseError(error);
|
||||
|
||||
expect(result.code).toBe('NETWORK_ERROR');
|
||||
expect(result.userMessage).toBe('No se pudo conectar con el servidor');
|
||||
expect(result.suggestion).toContain('Verifica tu conexión');
|
||||
});
|
||||
|
||||
it('should parse network error with HTTP status', () => {
|
||||
const error = {
|
||||
networkError: { status: 503, message: 'Service Unavailable' },
|
||||
message: 'Network error',
|
||||
};
|
||||
|
||||
const result = service.parseError(error);
|
||||
|
||||
expect(result.code).toBe('NETWORK_ERROR');
|
||||
expect(result.devMessage).toContain('503');
|
||||
});
|
||||
});
|
||||
|
||||
describe('HTTP errors', () => {
|
||||
it('should parse HTTP 500 as SERVER_ERROR', () => {
|
||||
const error = { status: 500, message: 'Internal Server Error' };
|
||||
|
||||
const result = service.parseError(error);
|
||||
|
||||
expect(result.code).toBe('SERVER_ERROR');
|
||||
expect(result.userMessage).toBe('Error interno del servidor');
|
||||
});
|
||||
|
||||
it('should parse HTTP 502 as SERVER_ERROR', () => {
|
||||
const error = { status: 502, message: 'Bad Gateway' };
|
||||
|
||||
const result = service.parseError(error);
|
||||
|
||||
expect(result.code).toBe('SERVER_ERROR');
|
||||
});
|
||||
|
||||
it('should parse HTTP 0 as NETWORK_ERROR', () => {
|
||||
const error = { status: 0, message: 'No response' };
|
||||
|
||||
const result = service.parseError(error);
|
||||
|
||||
expect(result.code).toBe('NETWORK_ERROR');
|
||||
});
|
||||
|
||||
it('should parse HTTP 400 as UNKNOWN', () => {
|
||||
const error = { status: 400, message: 'Bad Request' };
|
||||
|
||||
const result = service.parseError(error);
|
||||
|
||||
expect(result.code).toBe('UNKNOWN');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Unknown errors', () => {
|
||||
it('should handle string error', () => {
|
||||
const error = 'Something went wrong';
|
||||
|
||||
const result = service.parseError(error);
|
||||
|
||||
expect(result.code).toBe('UNKNOWN');
|
||||
expect(result.devMessage).toBe('Something went wrong');
|
||||
});
|
||||
|
||||
it('should handle null error', () => {
|
||||
const result = service.parseError(null);
|
||||
|
||||
expect(result.code).toBe('UNKNOWN');
|
||||
});
|
||||
|
||||
it('should handle undefined error', () => {
|
||||
const result = service.parseError(undefined);
|
||||
|
||||
expect(result.code).toBe('UNKNOWN');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('handle', () => {
|
||||
it('should show notification with user message', () => {
|
||||
const error = {
|
||||
graphQLErrors: [
|
||||
{
|
||||
message: 'Maximum reached',
|
||||
extensions: { code: 'MAX_ENROLLMENTS' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
service.handle(error);
|
||||
|
||||
expect(notificationSpy.error).toHaveBeenCalledWith(
|
||||
'Has alcanzado el límite máximo de 3 materias inscritas. Debes cancelar una inscripción antes de agregar otra materia'
|
||||
);
|
||||
});
|
||||
|
||||
it('should show notification without suggestion when not available', () => {
|
||||
const error = {
|
||||
graphQLErrors: [
|
||||
{
|
||||
message: 'Duplicate',
|
||||
extensions: { code: 'DUPLICATE_ENROLLMENT' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
service.handle(error);
|
||||
|
||||
expect(notificationSpy.error).toHaveBeenCalledWith('Ya estás inscrito en esta materia');
|
||||
});
|
||||
|
||||
it('should return ErrorInfo', () => {
|
||||
const error = {
|
||||
graphQLErrors: [
|
||||
{
|
||||
message: 'Not found',
|
||||
extensions: { code: 'STUDENT_NOT_FOUND' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const result = service.handle(error);
|
||||
|
||||
expect(result.code).toBe('STUDENT_NOT_FOUND');
|
||||
expect(result.userMessage).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should include context in log output', () => {
|
||||
spyOn(console, 'group'); // Prevent console output during test
|
||||
const error = { status: 500, message: 'Error' };
|
||||
|
||||
service.handle(error, 'StudentService.create');
|
||||
|
||||
// In dev mode, console.group is called
|
||||
// This test verifies the method doesn't throw
|
||||
expect(true).toBeTruthy();
|
||||
});
|
||||
});
|
||||
|
||||
describe('logError', () => {
|
||||
it('should not show notification', () => {
|
||||
const error = {
|
||||
graphQLErrors: [
|
||||
{
|
||||
message: 'Error',
|
||||
extensions: { code: 'STUDENT_NOT_FOUND' },
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
service.logError(error);
|
||||
|
||||
expect(notificationSpy.error).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue