import 'dart:async'; import 'dart:io'; import 'package:dio/dio.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart' as http; import 'package:marianum_mobile/api/api_error.dart'; import 'package:marianum_mobile/api/errors/auth_exception.dart'; import 'package:marianum_mobile/api/errors/error_mapper.dart'; import 'package:marianum_mobile/api/errors/network_exception.dart'; import 'package:marianum_mobile/api/errors/parse_exception.dart'; void main() { group('errorToUserMessage', () { test('null falls back to the default message', () { expect(errorToUserMessage(null), contains('Etwas ist schiefgelaufen')); }); test('AppException returns its own userMessage', () { final exception = AuthException.unauthorized(); expect(errorToUserMessage(exception), exception.userMessage); }); test('SocketException maps to NetworkException message', () { expect( errorToUserMessage(const SocketException('boom')), const NetworkException().userMessage, ); }); test( 'TimeoutException maps to the timeout-specific NetworkException message', () { expect( errorToUserMessage(TimeoutException('slow')), NetworkException.timeout().userMessage, ); }, ); test('http.ClientException maps to NetworkException message', () { expect( errorToUserMessage(http.ClientException('failed')), const NetworkException().userMessage, ); }); test('HandshakeException maps to a TLS-specific message', () { final message = errorToUserMessage(const HandshakeException('bad cert')); expect(message, contains('sichere Verbindung')); expect(message, contains('Geräte-Uhrzeit')); }); test('FormatException maps to ParseException message', () { expect( errorToUserMessage(const FormatException('bad json')), const ParseException().userMessage, ); }); test('ApiError surfaces only the first line of its message', () { final err = ApiError('Boom\nGET https://example.com/foo'); expect(errorToUserMessage(err), 'Boom'); }); test('ApiError with empty message falls back to default', () { final err = ApiError(''); expect(errorToUserMessage(err), contains('Etwas ist schiefgelaufen')); }); test('unknown error type falls back', () { expect( errorToUserMessage(StateError('weird')), contains('Etwas ist schiefgelaufen'), ); }); test('custom fallback overrides the default', () { expect(errorToUserMessage(null, fallback: 'meins'), 'meins'); }); test('DioException connectionTimeout maps to timeout NetworkException', () { final ex = DioException( requestOptions: RequestOptions(path: '/x'), type: DioExceptionType.connectionTimeout, ); expect(errorToUserMessage(ex), NetworkException.timeout().userMessage); }); test('DioException connectionError maps to NetworkException', () { final ex = DioException( requestOptions: RequestOptions(path: '/x'), type: DioExceptionType.connectionError, ); expect(errorToUserMessage(ex), const NetworkException().userMessage); }); test('DioException badResponse maps to a server status message', () { final ex = DioException( requestOptions: RequestOptions(path: '/x'), type: DioExceptionType.badResponse, response: Response( requestOptions: RequestOptions(path: '/x'), statusCode: 503, ), ); expect(errorToUserMessage(ex), contains('503')); }); }); group('errorToTechnicalDetails', () { test('null returns null', () { expect(errorToTechnicalDetails(null), isNull); }); test('AppException uses its technicalDetails when set', () { final ex = AuthException.unauthorized(technicalDetails: 'http 401, foo'); expect(errorToTechnicalDetails(ex), 'http 401, foo'); }); test('AppException without details falls back to toString()', () { final ex = AuthException.unauthorized(); expect(errorToTechnicalDetails(ex), ex.toString()); }); test('arbitrary object stringifies', () { expect(errorToTechnicalDetails(StateError('x')), contains('x')); }); }); group('errorAllowsRetry', () { test('null allows retry by default', () { expect(errorAllowsRetry(null), isTrue); }); test('AuthException disallows retry (allowRetry=false)', () { expect(errorAllowsRetry(AuthException.unauthorized()), isFalse); }); test('NetworkException allows retry (allowRetry=true)', () { expect(errorAllowsRetry(const NetworkException()), isTrue); }); test('non-AppException allows retry by default', () { expect(errorAllowsRetry(StateError('x')), isTrue); }); }); }