Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion modules/common/lib/core/result_type.dart
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,16 @@ extension ResultTypeExtension<T> on ResultType<T> {
};
}

/// Invokes [error] when this is a [TError]. If the callback returns an
/// [Exception], it replaces the original error; otherwise the original
/// error is kept, so side-effect callbacks don't discard it.
ResultType<T> mapError(Function(Exception? error) error) {
return switch (this) {
TSuccess<T> e => TSuccess(e.data),
TError e => TError(e.error),
TError e => _mapErrorValue(error(e.error), e.error),
};
}

TError<T> _mapErrorValue(dynamic mapped, Exception? original) =>
TError(mapped is Exception ? mapped : original);
}
61 changes: 61 additions & 0 deletions modules/common/test/core/result_type_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import 'package:common/core/failure/failure.dart';
import 'package:common/core/result_type.dart';
import 'package:flutter_test/flutter_test.dart';

void main() {
group('ResultType.mapError', () {
test('invokes the callback with the error of a TError', () {
final failure = UnexpectedFailure('boom');
Exception? received;

TError<int>(failure).mapError((error) => received = error);

expect(received, same(failure));
});

test('keeps the original error when the callback returns no Exception',
() {
final failure = UnexpectedFailure('boom');

final result = TError<int>(failure).mapError((_) {});

expect(result, isA<TError<int>>());
expect((result as TError<int>).error, same(failure));
});

test('replaces the error when the callback returns an Exception', () {
final mapped = ConnectionFailure('offline');

final result = TError<int>(UnexpectedFailure()).mapError((_) => mapped);

expect((result as TError<int>).error, same(mapped));
});

test('does not invoke the callback for a TSuccess', () {
var called = false;

final result = TSuccess(1).mapError((_) => called = true);

expect(called, isFalse);
expect((result as TSuccess<int>).data, 1);
});
});

group('ResultType.mapSuccess', () {
test('invokes the callback with the data of a TSuccess', () {
int? received;

TSuccess(42).mapSuccess((data) => received = data);

expect(received, 42);
});

test('does not invoke the callback for a TError', () {
var called = false;

TError<int>(UnexpectedFailure()).mapSuccess((_) => called = true);

expect(called, isFalse);
});
});
}
4 changes: 2 additions & 2 deletions sonar-project.properties
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ sonar.dart.analyzer.report.mode=LEGACY
sonar.exclusions=**/*.g.dart,**/generated/**,**/*.freezed.dart, pubspec.yaml, coverage/**
sonar.coverage.exclusions=**/*.g.dart,**/generated/**,**/*.freezed.dart, pubspec.yaml, coverage/**

# common & data have no tests yet
sonar.tests=app/test,modules/domain/test
# domain & data have no tests yet
sonar.tests=app/test,modules/domain/test,modules/common/test

# Coverage report – property understood by the **sonar-flutter** plugin
sonar.flutter.coverage.reportPath=coverage/lcov.info
Expand Down