MotorSignal-App/test/ownership_isolation_test.dart

106 lines
4.8 KiB
Dart

import 'package:drift/native.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:motorcycle_dash/database/database.dart';
import 'package:motorcycle_dash/services/auth/auth_service.dart';
import 'package:motorcycle_dash/services/auth/auth_state.dart';
import 'package:motorcycle_dash/services/auth/auth_user.dart';
import 'package:motorcycle_dash/services/sync/sync_manager.dart';
import 'package:motorcycle_dash/services/sync/sync_outbox.dart';
import 'package:motorcycle_dash/services/sync/sync_provider.dart';
import 'package:motorcycle_dash/services/sync/sync_models.dart';
import 'package:motorcycle_dash/services/sync/firebase_sync_provider.dart';
class _Auth implements AuthService {
@override bool get isEmailVerified => false;
@override Future<void> sendEmailVerification() async {}
@override Future<void> reloadCurrentUser() async {}
@override Future<void> reauthenticate({String? password}) async {}
@override Future<void> deleteCurrentUser() async {}
AuthUser? user;
@override bool get isConfigured => true;
@override AuthUser? get currentUser => user;
@override AuthState get currentState => user == null ? const AuthState.unauthenticated() : AuthState.authenticated(user!);
@override Stream<AuthState> get authStateChanges => Stream.value(currentState);
@override Future<void> createUserWithEmailAndPassword(String e, String p, {String? displayName}) async {}
@override Future<void> sendPasswordResetEmail(String email) async {}
@override Future<void> signInAnonymously() async {}
@override Future<void> signInWithEmailAndPassword(String e, String p) async {}
@override Future<void> signInWithGoogle() async {}
@override Future<String?> getIdToken([bool forceRefresh = false]) async => user == null ? null : 'token';
@override Future<void> signOut() async { user = null; }
@override void dispose() {}
}
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
test('new rides are unclaimed and claim is immutable', () async {
final db = AppDatabase.forTesting(NativeDatabase.memory());
addTearDown(db.close);
final rideId = await db.startRide();
expect(await db.getCloudOwnerUid(rideId), isNull);
expect(await db.claimRideForCloudOwner(rideId, 'user_a'), isTrue);
expect(await db.getCloudOwnerUid(rideId), 'user_a');
expect(await db.claimRideForCloudOwner(rideId, 'user_b'), isFalse);
expect(await db.getCloudOwnerUid(rideId), 'user_a');
expect(await db.getCloudRideId(rideId), startsWith('ride_'));
});
test('authenticated enqueue claims only for the current permanent account', () async {
SharedPreferences.setMockInitialValues({});
final prefs = await SharedPreferences.getInstance();
final db = AppDatabase.forTesting(NativeDatabase.memory());
addTearDown(db.close);
final auth = _Auth()..user = const AuthUser(uid: 'user_a', email: 'a@test.com');
final rideId = await db.startRide();
final manager = SyncManager(
SyncOutbox(prefs),
db: db,
authService: auth,
initialProviders: [FakeSyncProvider(providerId: 'firebase')],
defaultProviderId: 'firebase',
);
addTearDown(manager.dispose);
await manager.enqueueRide(rideId, autoProcess: false);
expect(await db.getCloudOwnerUid(rideId), 'user_a');
auth.user = const AuthUser(uid: 'user_b', email: 'b@test.com');
await manager.enqueueRide(rideId, autoProcess: false);
expect(await db.getCloudOwnerUid(rideId), 'user_a');
});
test('signed-out enqueue leaves ride unclaimed', () async {
SharedPreferences.setMockInitialValues({});
final prefs = await SharedPreferences.getInstance();
final db = AppDatabase.forTesting(NativeDatabase.memory());
addTearDown(db.close);
final rideId = await db.startRide();
final manager = SyncManager(
SyncOutbox(prefs),
db: db,
authService: _Auth(),
initialProviders: [FakeSyncProvider(providerId: 'firebase')],
defaultProviderId: 'firebase',
);
addTearDown(manager.dispose);
await manager.enqueueRide(rideId, autoProcess: false);
expect(await db.getCloudOwnerUid(rideId), isNull);
});
test('Firebase provider rejects another account before any cloud call', () async {
final db = AppDatabase.forTesting(NativeDatabase.memory());
addTearDown(db.close);
final rideId = await db.startRide();
await db.claimRideForCloudOwner(rideId, 'user_a');
final auth = _Auth()..user = const AuthUser(uid: 'user_b', email: 'b@test.com');
final result = await FirebaseSyncProvider(db: db, authService: auth).uploadRide(
SyncItem(localRideId: rideId, providerId: 'firebase', createdAt: DateTime.now()),
);
expect(result.success, isFalse);
expect(result.isRetryable, isFalse);
expect(await db.getCloudOwnerUid(rideId), 'user_a');
});
}