Files
plezy/test/services/update_service_test.dart

99 lines
4.0 KiB
Dart

import 'dart:async';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:plezy/utils/media_server_http_client.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:plezy/services/base_shared_preferences_service.dart';
import 'package:plezy/services/update_service.dart';
import '../test_helpers/prefs.dart';
void main() {
const lastCheckKey = 'update_last_check_time';
setUp(resetSharedPreferencesForTest);
PackageInfo.setMockInitialValues(
appName: 'Plezy',
packageName: 'com.plezy.test',
version: '1.0.0',
buildNumber: '1',
buildSignature: '',
);
test('malformed cooldown state fails open and removes the invalid value', () async {
final prefs = await BaseSharedPreferencesService.sharedCache();
await prefs.setString(lastCheckKey, 'not-an-instant');
expect(await UpdateService.shouldCheckForUpdates(), isTrue);
expect(prefs.getString(lastCheckKey), isNull);
});
test('future cooldown state fails open and removes the invalid value', () async {
final prefs = await BaseSharedPreferencesService.sharedCache();
await prefs.setString(lastCheckKey, DateTime.now().add(const Duration(days: 30)).toIso8601String());
expect(await UpdateService.shouldCheckForUpdates(), isTrue);
expect(prefs.getString(lastCheckKey), isNull);
});
test('recent valid cooldown state suppresses a duplicate check and remains stored', () async {
final prefs = await BaseSharedPreferencesService.sharedCache();
final recent = DateTime.now().subtract(const Duration(minutes: 5)).toIso8601String();
await prefs.setString(lastCheckKey, recent);
expect(await UpdateService.shouldCheckForUpdates(), isFalse);
expect(prefs.getString(lastCheckKey), recent);
});
test('old valid cooldown state permits a new check', () async {
final prefs = await BaseSharedPreferencesService.sharedCache();
final old = DateTime.now().subtract(const Duration(days: 2)).toIso8601String();
await prefs.setString(lastCheckKey, old);
expect(await UpdateService.shouldCheckForUpdates(), isTrue);
expect(prefs.getString(lastCheckKey), old);
});
final failedResponses = <String, Future<http.Response> Function()>{
'timeout': () async => throw TimeoutException('request timed out'),
'non-200 response': () async => http.Response('unavailable', 503),
'parse failure': () async => http.Response('not-json', 200, headers: {'content-type': 'application/json'}),
};
for (final failure in failedResponses.entries) {
test('startup ${failure.key} records cooldown before request and manual check bypasses it', () async {
final prefs = await BaseSharedPreferencesService.sharedCache();
final cooldownAtRequest = <String?>[];
var requestCount = 0;
final client = MediaServerHttpClient(
client: MockClient((_) async {
requestCount++;
cooldownAtRequest.add(prefs.getString(lastCheckKey));
return failure.value();
}),
);
addTearDown(client.close);
expect(await UpdateService.debugPerformUpdateCheck(respectCooldown: true, client: client), isNull);
expect(requestCount, 1);
expect(cooldownAtRequest.single, isNotNull);
final recordedCooldown = prefs.getString(lastCheckKey);
expect(recordedCooldown, cooldownAtRequest.single);
expect(DateTime.now().difference(DateTime.parse(recordedCooldown!)), lessThan(const Duration(minutes: 1)));
expect(await UpdateService.debugPerformUpdateCheck(respectCooldown: true, client: client), isNull);
expect(requestCount, 1, reason: 'a simulated next launch must honor the failed attempt cooldown');
expect(await UpdateService.debugPerformUpdateCheck(respectCooldown: false, client: client), isNull);
expect(requestCount, 2, reason: 'an explicit manual check must bypass a recent startup cooldown');
expect(
prefs.getString(lastCheckKey),
recordedCooldown,
reason: 'manual checks must not rewrite startup cooldown',
);
});
}
}