import 'dart:convert'; import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart' as http; import 'package:http/testing.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:tdc114plus/src/features/auth/data/auth_session_store.dart'; import 'package:tdc114plus/src/features/auth/domain/auth_models.dart'; import 'package:tdc114plus/src/features/directory/data/profile_image_api_client.dart'; import 'package:tdc114plus/src/features/directory/domain/employee.dart'; void main() { setUp(() { SharedPreferences.setMockInitialValues({}); }); test('keeps explicit profileImageUrl without auth lookup', () async { var requestCount = 0; final client = ProfileImageApiClient( httpClient: MockClient((request) async { requestCount += 1; return http.Response('{}', 500); }), baseUri: Uri.parse('https://114-auth.hmac.kr'), sessionStore: const AuthSessionStore(), ); const employee = Employee( id: 'user-1', name: '강경훈', phoneNumber: '01012345678', email: 'khkang@samaneng.com', tenantId: 'tenant-1', tenantName: '삼안', tenantSlug: 'saman', profileImageUrl: 'https://cdn.example.test/khkang.jpg', ); final imageUrl = await client.resolveImageUrl(employee); expect(imageUrl, 'https://cdn.example.test/khkang.jpg'); expect(requestCount, 0); }); test('uses auth profile-image endpoint when explicit profile image is absent', () async { const store = AuthSessionStore(); await store.save( PhoneLoginResponse( status: 'ok', token: 'app-session-token', expiresAt: DateTime.now().toUtc().add(const Duration(hours: 1)), user: const LoginUser( id: 'user-1', name: '강경훈', phoneNumber: '01012345678', tenantId: 'tenant-1', tenantName: '삼안', tenantSlug: 'saman', ), ), ); final client = ProfileImageApiClient( httpClient: MockClient((request) async { expect(request.url.host, '114-auth.hmac.kr'); expect(request.url.path, '/api/v1/profile-image'); expect(request.url.queryParameters['comp'], 'SAMAN'); expect(request.url.queryParameters['email'], 'khkang@samaneng.com'); expect(request.headers['Authorization'], 'Bearer app-session-token'); return http.Response( jsonEncode({ 'found': true, 'source': 'BARON_UUID_R2', 'imageUrl': 'https://baroncs.co.kr/employee_img/bebb832c-052e-493d-b5a9-732518d67685.jpg', }), 200, headers: {'content-type': 'application/json'}, ); }), baseUri: Uri.parse('https://114-auth.hmac.kr'), sessionStore: store, ); const employee = Employee( id: 'bebb832c-052e-493d-b5a9-732518d67685', name: '강경훈', phoneNumber: '01012345678', email: 'khkang@samaneng.com', tenantId: 'tenant-1', tenantName: '삼안', tenantSlug: 'saman', ); final imageUrl = await client.resolveImageUrl(employee); expect( imageUrl, 'https://baroncs.co.kr/employee_img/bebb832c-052e-493d-b5a9-732518d67685.jpg', ); }); test('returns null when employee email is missing', () async { final client = ProfileImageApiClient( httpClient: MockClient((request) async { fail('auth lookup should not be sent without email'); }), baseUri: Uri.parse('https://114-auth.hmac.kr'), sessionStore: const AuthSessionStore(), ); const employee = Employee( id: 'user-2', name: '직원', phoneNumber: '01000000000', tenantId: 'tenant-2', tenantName: '한맥', tenantSlug: 'hanmac', ); expect(await client.resolveImageUrl(employee), isNull); }); test('falls back to public uuid image when employee email is missing', () async { final client = ProfileImageApiClient( httpClient: MockClient((request) async { fail('auth lookup should not be sent without email'); }), baseUri: Uri.parse('https://114-auth.hmac.kr'), sessionStore: const AuthSessionStore(), ); const employee = Employee( id: 'bebb832c-052e-493d-b5a9-732518d67685', name: '강경훈', phoneNumber: '01012345678', tenantId: 'tenant-1', tenantName: '삼안', tenantSlug: 'saman', ); expect( await client.resolveImageUrl(employee), 'https://baroncs.co.kr/employee_img/bebb832c-052e-493d-b5a9-732518d67685.jpg', ); }); test('allows auth lookup without company code when tenant mapping is unknown', () async { const store = AuthSessionStore(); await store.save( PhoneLoginResponse( status: 'ok', token: 'app-session-token', expiresAt: DateTime.now().toUtc().add(const Duration(hours: 1)), user: const LoginUser( id: 'user-3', name: '직원', phoneNumber: '01011112222', tenantId: 'tenant-3', tenantName: 'Unknown', tenantSlug: 'unknown', ), ), ); final client = ProfileImageApiClient( httpClient: MockClient((request) async { expect(request.url.queryParameters['comp'], isNull); expect(request.url.queryParameters['email'], 'person@example.com'); return http.Response( jsonEncode({'found': false, 'source': 'DEFAULT'}), 200, headers: {'content-type': 'application/json'}, ); }), baseUri: Uri.parse('https://114-auth.hmac.kr'), sessionStore: store, ); const employee = Employee( id: 'user-3', name: '직원', phoneNumber: '01011112222', email: 'person@example.com', tenantId: 'tenant-3', tenantName: 'Unknown', tenantSlug: 'unknown', ); expect(await client.resolveImageUrl(employee), isNull); }); test('falls back to public uuid image when auth lookup returns not found', () async { const store = AuthSessionStore(); await store.save( PhoneLoginResponse( status: 'ok', token: 'app-session-token', expiresAt: DateTime.now().toUtc().add(const Duration(hours: 1)), user: const LoginUser( id: 'user-4', name: '강경훈', phoneNumber: '01012345678', tenantId: 'tenant-1', tenantName: '삼안', tenantSlug: 'saman', ), ), ); final client = ProfileImageApiClient( httpClient: MockClient((request) async { return http.Response( jsonEncode({'found': false, 'source': 'DEFAULT'}), 200, headers: {'content-type': 'application/json'}, ); }), baseUri: Uri.parse('https://114-auth.hmac.kr'), sessionStore: store, ); const employee = Employee( id: 'bebb832c-052e-493d-b5a9-732518d67685', name: '강경훈', phoneNumber: '01012345678', email: 'khkang@samaneng.com', tenantId: 'tenant-1', tenantName: '삼안', tenantSlug: 'saman', ); expect( await client.resolveImageUrl(employee), 'https://baroncs.co.kr/employee_img/bebb832c-052e-493d-b5a9-732518d67685.jpg', ); }); test('uses uuid fallback even when auth not-found result is cached', () async { const store = AuthSessionStore(); await store.save( PhoneLoginResponse( status: 'ok', token: 'app-session-token', expiresAt: DateTime.now().toUtc().add(const Duration(hours: 1)), user: const LoginUser( id: 'user-5', name: '직원', phoneNumber: '01012345678', tenantId: 'tenant-1', tenantName: '삼안', tenantSlug: 'saman', ), ), ); var requestCount = 0; final client = ProfileImageApiClient( httpClient: MockClient((request) async { requestCount += 1; return http.Response( jsonEncode({'found': false, 'source': 'DEFAULT'}), 200, headers: {'content-type': 'application/json'}, ); }), baseUri: Uri.parse('https://114-auth.hmac.kr'), sessionStore: store, ); const firstEmployee = Employee( id: 'legacy-user-id', name: '직원', phoneNumber: '01012345678', email: 'cached-null@example.com', tenantId: 'tenant-1', tenantName: '삼안', tenantSlug: 'saman', ); const secondEmployee = Employee( id: 'bebb832c-052e-493d-b5a9-732518d67685', name: '직원', phoneNumber: '01012345678', email: 'cached-null@example.com', tenantId: 'tenant-1', tenantName: '삼안', tenantSlug: 'saman', ); expect(await client.resolveImageUrl(firstEmployee), isNull); expect( await client.resolveImageUrl(secondEmployee), 'https://baroncs.co.kr/employee_img/bebb832c-052e-493d-b5a9-732518d67685.jpg', ); expect(requestCount, 1); }); }