import 'package:dio/dio.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:social_app/data/network/i_api_client.dart'; import 'package:social_app/data/cache/cache_store.dart'; import 'package:social_app/features/calendar/data/repositories/calendar_repository.dart'; import 'package:social_app/features/contacts/data/repositories/friend_repository.dart'; import 'package:social_app/features/messages/data/repositories/inbox_repository.dart'; import 'package:social_app/features/contacts/data/models/friend_request.dart'; import 'package:social_app/features/messages/data/models/inbox_message.dart'; import 'package:social_app/features/calendar/data/models/schedule_item_model.dart'; import 'package:social_app/features/contacts/data/repositories/user_repository.dart'; class _FakeApiClient implements IApiClient { final Map _getResponses = {}; final Map _postResponses = {}; final Map _patchResponses = {}; void setGet(String path, dynamic data) => _getResponses[path] = data; void setPost(String path, dynamic data) => _postResponses[path] = data; void setPatch(String path, dynamic data) => _patchResponses[path] = data; @override Future> get( String path, { Map? queryParameters, Options? options, }) async { if (!_getResponses.containsKey(path)) { throw StateError('missing GET mock for $path'); } return Response( requestOptions: RequestOptions(path: path), data: _getResponses[path] as T, ); } @override Future> post( String path, { dynamic data, Options? options, }) async { if (!_postResponses.containsKey(path)) { throw StateError('missing POST mock for $path'); } return Response( requestOptions: RequestOptions(path: path), data: _postResponses[path] as T, ); } @override Future> patch( String path, { dynamic data, Options? options, }) async { if (!_patchResponses.containsKey(path)) { throw StateError('missing PATCH mock for $path'); } return Response( requestOptions: RequestOptions(path: path), data: _patchResponses[path] as T, ); } @override Future> delete(String path, {dynamic data, Options? options}) { throw UnimplementedError(); } @override Future> getSseLines( String path, { Map? headers, }) { throw UnimplementedError(); } @override Future> put(String path, {dynamic data, Options? options}) { throw UnimplementedError(); } } void main() { test('InboxRepository maps message type and status', () async { final client = _FakeApiClient(); client.setGet('/api/v1/inbox/messages?is_read=false', [ { 'id': 'm1', 'recipient_id': 'u1', 'sender_id': 'u2', 'message_type': 'calendar', 'schedule_item_id': 's1', 'friendship_id': null, 'content': {'type': 'invite'}, 'is_read': false, 'status': 'pending', 'created_at': '2026-03-27T08:00:00Z', }, ]); final repository = InboxRepositoryImpl( apiClient: client, store: HybridCacheStore( memory: MemoryCacheStore(), persistent: PersistentCacheStore(), ), ); final result = await repository.getMessages(isRead: false); expect(result.single.messageType, InboxMessageType.calendar); expect(result.single.status, InboxMessageStatus.pending); expect(result.single.scheduleItemId, 's1'); }); test('FriendRepository maps request status', () async { final client = _FakeApiClient(); client.setGet('/api/v1/friends/requests/f1', { 'id': 'f1', 'sender': {'id': 'u1', 'username': 'alice', 'avatar_url': null}, 'recipient': {'id': 'u2', 'username': 'bob', 'avatar_url': null}, 'content': {'message': 'hi'}, 'status': 'accepted', 'created_at': '2026-03-27T08:00:00Z', }); final repository = FriendRepositoryImpl( apiClient: client, store: HybridCacheStore( memory: MemoryCacheStore(), persistent: PersistentCacheStore(), ), ); final request = await repository.getRequestById('f1'); expect(request.status, FriendRequestStatus.accepted); expect(request.sender.username, 'alice'); }); test( 'FriendRepository batch lookup fails when any request is missing', () async { final client = _FakeApiClient(); client.setGet('/api/v1/friends/requests/f1', { 'id': 'f1', 'sender': {'id': 'u1', 'username': 'alice', 'avatar_url': null}, 'recipient': {'id': 'u2', 'username': 'bob', 'avatar_url': null}, 'content': {'message': 'hi'}, 'status': 'pending', 'created_at': '2026-03-27T08:00:00Z', }); final repository = FriendRepositoryImpl( apiClient: client, store: HybridCacheStore( memory: MemoryCacheStore(), persistent: PersistentCacheStore(), ), ); await expectLater( repository.getRequestsByIds(['f1', 'missing']), throwsStateError, ); }, ); test('CalendarRepository maps archived status', () async { final client = _FakeApiClient(); client.setGet('/api/v1/schedule-items/e1', { 'id': 'e1', 'owner_id': 'u1', 'permission': 1, 'is_owner': true, 'title': 'event', 'description': null, 'start_at': '2026-03-27T08:00:00Z', 'end_at': null, 'timezone': 'UTC', 'metadata': null, 'source_type': 'manual', 'status': 'archived', 'created_at': '2026-03-27T08:00:00Z', 'updated_at': '2026-03-27T09:00:00Z', }); final repository = CalendarRepository( store: HybridCacheStore( memory: MemoryCacheStore(), persistent: PersistentCacheStore(), ), apiClient: client, ); final event = await repository.getById('e1'); expect(event.status, ScheduleStatus.archived); }); test('UserRepository returns shared user summary', () async { final client = _FakeApiClient(); client.setGet('/api/v1/users/me', { 'id': 'u1', 'username': 'alice', 'phone': null, 'avatar_url': 'https://img', 'bio': null, }); final repository = UserRepositoryImpl(client); final me = await repository.getMe(); expect(me.id, 'u1'); expect(me.username, 'alice'); expect(me.avatarUrl, 'https://img'); }); }