Адрес бэкенда хранится в shared_preferences и читается до runApp; switchApiBaseUrl сбрасывает токены и кэш старого сервера. Выход стирает refresh-токен и sqlite-кэш ответов. Экран настроек переделан под разделы.
121 lines
3.8 KiB
Dart
121 lines
3.8 KiB
Dart
import 'package:drift/native.dart';
|
|
import 'package:fintracker_app/core/auth/auth_controller.dart';
|
|
import 'package:fintracker_app/core/auth/switch_server.dart';
|
|
import 'package:fintracker_app/core/auth/token_store.dart';
|
|
import 'package:fintracker_app/core/cache/response_cache.dart';
|
|
import 'package:fintracker_app/core/cache/response_cache_provider.dart';
|
|
import 'package:fintracker_app/core/config.dart';
|
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
/// No stored refresh token, so `logout()` never touches the network.
|
|
class _NoTokenStore extends TokenStore {
|
|
bool cleared = false;
|
|
|
|
@override
|
|
Future<String?> readRefresh() async => null;
|
|
|
|
@override
|
|
Future<void> clear() async => cleared = true;
|
|
}
|
|
|
|
void main() {
|
|
late ResponseCacheDatabase db;
|
|
late _NoTokenStore tokens;
|
|
|
|
ProviderContainer container({String? storedUrl}) {
|
|
final c = ProviderContainer(
|
|
overrides: [
|
|
responseCacheDbProvider.overrideWithValue(db),
|
|
tokenStoreProvider.overrideWithValue(tokens),
|
|
initialApiBaseUrlProvider.overrideWithValue(storedUrl),
|
|
],
|
|
);
|
|
addTearDown(c.dispose);
|
|
return c;
|
|
}
|
|
|
|
setUp(() async {
|
|
SharedPreferences.setMockInitialValues({});
|
|
db = ResponseCacheDatabase.forTesting(NativeDatabase.memory());
|
|
tokens = _NoTokenStore();
|
|
await db.put('/networth/breakdown', {'totalRub': '100'});
|
|
});
|
|
|
|
tearDown(() => db.close());
|
|
|
|
test('logout drops the token and every cached response', () async {
|
|
final c = container();
|
|
|
|
await c.read(authControllerProvider.notifier).logout();
|
|
|
|
expect(tokens.cleared, isTrue);
|
|
expect(await db.get('/networth/breakdown'), isNull);
|
|
expect(c.read(authControllerProvider).status, AuthStatus.signedOut);
|
|
});
|
|
|
|
group('API base URL', () {
|
|
test('a saved address wins over the default', () {
|
|
expect(
|
|
container(storedUrl: 'https://fin.example.com')
|
|
.read(apiBaseUrlProvider),
|
|
'https://fin.example.com',
|
|
);
|
|
expect(container().read(apiBaseUrlProvider), defaultApiBaseUrl());
|
|
});
|
|
|
|
test('normalize accepts http(s) origins and trims the trailing slash', () {
|
|
expect(
|
|
ApiBaseUrlController.normalize(' https://fin.example.com/ '),
|
|
'https://fin.example.com',
|
|
);
|
|
expect(
|
|
ApiBaseUrlController.normalize('http://10.0.0.5:8000'),
|
|
'http://10.0.0.5:8000',
|
|
);
|
|
expect(ApiBaseUrlController.normalize('fin.example.com'), isNull);
|
|
expect(ApiBaseUrlController.normalize('ftp://fin.example.com'), isNull);
|
|
expect(ApiBaseUrlController.normalize(''), isNull);
|
|
});
|
|
|
|
test(
|
|
'switching servers signs out, clears the cache and persists the address',
|
|
() async {
|
|
final c = container();
|
|
|
|
await switchApiBaseUrl(c, 'https://other.example.com');
|
|
|
|
expect(c.read(apiBaseUrlProvider), 'https://other.example.com');
|
|
expect(await loadStoredApiBaseUrl(), 'https://other.example.com');
|
|
expect(tokens.cleared, isTrue);
|
|
expect(await db.get('/networth/breakdown'), isNull);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'saving the address already in use keeps the session and the cache',
|
|
() async {
|
|
final c = container(storedUrl: 'https://fin.example.com');
|
|
|
|
await switchApiBaseUrl(c, 'https://fin.example.com');
|
|
|
|
expect(tokens.cleared, isFalse);
|
|
expect(await db.get('/networth/breakdown'), isNotNull);
|
|
},
|
|
);
|
|
|
|
test(
|
|
'null goes back to the default and forgets the saved address',
|
|
() async {
|
|
final c = container(storedUrl: 'https://fin.example.com');
|
|
|
|
await switchApiBaseUrl(c, null);
|
|
|
|
expect(c.read(apiBaseUrlProvider), defaultApiBaseUrl());
|
|
expect(await loadStoredApiBaseUrl(), isNull);
|
|
},
|
|
);
|
|
});
|
|
}
|