import 'dart:async'; import 'dart:io'; 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', () { expect(errorToUserMessage(const HandshakeException('bad cert')), 'Sichere Verbindung konnte nicht hergestellt werden.'); }); 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'); }); }); 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); }); }); }