feat: surface read-only load errors
This commit is contained in:
121
test/read_only_load_state_test.dart
Normal file
121
test/read_only_load_state_test.dart
Normal file
@@ -0,0 +1,121 @@
|
||||
import 'package:dio/dio.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:ignis_app/app/load_state.dart';
|
||||
import 'package:ignis_app/providers/providers.dart';
|
||||
import 'package:ignis_app/services/api_client.dart';
|
||||
|
||||
class FakeIgnisApi extends IgnisApi {
|
||||
Object? statsData;
|
||||
Object? eventLogData;
|
||||
Object? statsError;
|
||||
Object? eventLogError;
|
||||
int? requestedDays;
|
||||
int? requestedLimit;
|
||||
|
||||
FakeIgnisApi({this.statsData, this.eventLogData});
|
||||
|
||||
@override
|
||||
Future<Response> getStatsSummary({int days = 7}) async {
|
||||
requestedDays = days;
|
||||
final error = statsError;
|
||||
if (error != null) throw error;
|
||||
return Response(
|
||||
requestOptions: RequestOptions(path: '/stats/summary'),
|
||||
data: statsData,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Response> getStatsLog({int limit = 100}) async {
|
||||
requestedLimit = limit;
|
||||
final error = eventLogError;
|
||||
if (error != null) throw error;
|
||||
return Response(
|
||||
requestOptions: RequestOptions(path: '/stats/log'),
|
||||
data: eventLogData,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
ProviderContainer containerWith(FakeIgnisApi api) {
|
||||
final container = ProviderContainer(
|
||||
overrides: [apiProvider.overrideWithValue(api)],
|
||||
);
|
||||
addTearDown(container.dispose);
|
||||
return container;
|
||||
}
|
||||
|
||||
test('stats load exposes data state', () async {
|
||||
final api = FakeIgnisApi(
|
||||
statsData: {
|
||||
'groups': [
|
||||
{'id': 'kitchen', 'total_commands': 3},
|
||||
],
|
||||
},
|
||||
);
|
||||
final container = containerWith(api);
|
||||
|
||||
await container.read(statsProvider.notifier).load(days: 14);
|
||||
|
||||
final state = container.read(statsProvider);
|
||||
expect(state.status, LoadStatus.data);
|
||||
expect(state.data['groups'], hasLength(1));
|
||||
expect(api.requestedDays, 14);
|
||||
});
|
||||
|
||||
test('stats load exposes empty state for empty groups', () async {
|
||||
final api = FakeIgnisApi(statsData: {'groups': <Object>[]});
|
||||
final container = containerWith(api);
|
||||
|
||||
await container.read(statsProvider.notifier).load();
|
||||
|
||||
final state = container.read(statsProvider);
|
||||
expect(state.status, LoadStatus.empty);
|
||||
expect(state.data['groups'], isEmpty);
|
||||
});
|
||||
|
||||
test('event log load accepts map response and exposes data state', () async {
|
||||
final api = FakeIgnisApi(
|
||||
eventLogData: {
|
||||
'events': [
|
||||
{'action': 'toggle', 'target_id': 'kitchen'},
|
||||
],
|
||||
},
|
||||
);
|
||||
final container = containerWith(api);
|
||||
|
||||
await container.read(eventLogProvider.notifier).load(limit: 50);
|
||||
|
||||
final state = container.read(eventLogProvider);
|
||||
expect(state.status, LoadStatus.data);
|
||||
expect(state.data, hasLength(1));
|
||||
expect(api.requestedLimit, 50);
|
||||
});
|
||||
|
||||
test('load error keeps previous stats data and exposes message', () async {
|
||||
final api = FakeIgnisApi(
|
||||
statsData: {
|
||||
'groups': [
|
||||
{'id': 'kitchen'},
|
||||
],
|
||||
},
|
||||
);
|
||||
final container = containerWith(api);
|
||||
|
||||
await container.read(statsProvider.notifier).load();
|
||||
api.statsError = DioException(
|
||||
requestOptions: RequestOptions(path: '/stats/summary'),
|
||||
type: DioExceptionType.connectionError,
|
||||
message: 'No route to host',
|
||||
);
|
||||
|
||||
await container.read(statsProvider.notifier).load();
|
||||
|
||||
final state = container.read(statsProvider);
|
||||
expect(state.status, LoadStatus.error);
|
||||
expect(state.data['groups'], hasLength(1));
|
||||
expect(state.errorMessage, contains('Backend недоступен'));
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user