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 readRefresh() async => null; @override Future 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); }, ); }); }