import 'dart:async'; import 'package:http/http.dart' as http; import 'package:http/testing.dart'; import 'package:flutter_test/flutter_test.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/cloud/account_cloud_cleanup_service.dart'; class _Auth implements AuthService { @override Future ensureBackendIdentity() async {} final _states = StreamController.broadcast(); String? token = 'test-token'; AuthUser? user = const AuthUser( uid: 'cleanup-test', email: 'cleanup@test.com', providerId: 'password', emailVerified: true, ); @override Stream get authStateChanges => _states.stream; @override AuthState get currentState => user == null ? const AuthState.unauthenticated(isFirebaseConfigured: true) : AuthState.authenticated(user!, isFirebaseConfigured: true); @override AuthUser? get currentUser => user; @override bool get isConfigured => true; @override bool get isEmailVerified => true; @override Future reauthenticate({String? password}) async {} @override Future deleteCurrentUser() async {} @override Future getIdToken([bool forceRefresh = false]) async => token; @override Future signInWithEmailAndPassword(String email, String password) async {} @override Future createUserWithEmailAndPassword(String email, String password, {String? displayName}) async {} @override Future signInWithGoogle() async {} @override Future signInAnonymously() async {} @override Future sendPasswordResetEmail(String email) async {} @override Future sendEmailVerification() async {} @override Future reloadCurrentUser() async {} @override Future signOut() async {} @override void dispose() => _states.close(); } AccountCloudCleanupService _service(_Auth auth, int status, String body) { return AccountCloudCleanupService( auth: auth, client: MockClient((request) async { expect(request.method, 'DELETE'); expect(request.url.path, '/account/cloud-data'); expect(request.headers['authorization'], 'Bearer test-token'); return http.Response(body, status); }), workerUrl: 'https://cleanup.test', ); } void main() { test('accepts a valid Worker 200 response before Firestore cleanup', () async { final auth = _Auth(); addTearDown(auth.dispose); await expectLater( _service(auth, 200, '{"success":true,"deletedObjects":0}').deleteCloudData(), throwsA(isA().having( (error) => error.message, 'message', 'MotorCloud database is unavailable.', )), ); }); test('accepts an idempotent empty-prefix 204 response', () async { final auth = _Auth(); addTearDown(auth.dispose); await expectLater( _service(auth, 204, '').deleteCloudData(), throwsA(isA().having( (error) => error.message, 'message', 'MotorCloud database is unavailable.', )), ); }); test('rejects a malformed Worker success response', () async { final auth = _Auth(); addTearDown(auth.dispose); await expectLater( _service(auth, 200, '{"deleted":0}').deleteCloudData(), throwsA(isA().having( (error) => error.message, 'message', 'MotorCloud cleanup returned an invalid response.', )), ); }); test('rejects an empty Worker success response', () async { final auth = _Auth(); addTearDown(auth.dispose); await expectLater( _service(auth, 200, '').deleteCloudData(), throwsA(isA().having( (error) => error.message, 'message', 'MotorCloud cleanup returned an invalid response.', )), ); }); for (final status in [401, 403, 500]) { test('surfaces Worker HTTP $status as controlled cleanup failure', () async { final auth = _Auth(); addTearDown(auth.dispose); await expectLater( _service(auth, status, '{"error":"failure"}').deleteCloudData(), throwsA(isA().having( (error) => error.message, 'message', 'MotorCloud cleanup was not completed ($status).', )), ); }); } test('rejects an empty token before making a Worker request', () async { final auth = _Auth()..token = null; addTearDown(auth.dispose); await expectLater( _service(auth, 200, '').deleteCloudData(), throwsA(isA().having( (error) => error.message, 'message', 'A valid account session is required.', )), ); }); test('rejects an anonymous account before making a Worker request', () async { final auth = _Auth() ..user = const AuthUser(uid: 'guest', isAnonymous: true); addTearDown(auth.dispose); var requests = 0; final service = AccountCloudCleanupService( auth: auth, client: MockClient((request) async { requests++; return http.Response('', 500); }), workerUrl: 'https://cleanup.test', ); await expectLater( service.deleteCloudData(), throwsA(isA().having( (error) => error.message, 'message', 'A permanent account is required.', )), ); expect(requests, 0); }); test('rejects invalid Worker configuration before making a request', () async { final auth = _Auth(); addTearDown(auth.dispose); final service = AccountCloudCleanupService( auth: auth, client: MockClient((request) async => http.Response('', 200)), workerUrl: '', ); await expectLater( service.deleteCloudData(), throwsA(isA().having( (error) => error.message, 'message', 'MotorCloud cleanup endpoint is unavailable.', )), ); }); }