// 功能描述:验证统一请求鉴权、会话失效通知和二进制资源处理。 // 版本:1.2.0 import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart' as http; import 'package:http/testing.dart'; import 'package:user_app/data/services/api_client.dart'; /// 验证头像字节、鉴权请求头和资源不存在状态。 void main() { test('头像接口携带令牌并返回二进制内容', () async { final expectedBytes = Uint8List.fromList(const [1, 2, 3]); final client = ApiClient( () => 'JWT test-token', baseUrl: 'https://api.example.com', client: MockClient((request) async { expect(request.url.path, '/heqi/client/v1/user/auth/avatar'); expect(request.headers['authorization'], 'JWT test-token'); return http.Response.bytes(expectedBytes, 200); }), ); expect( await client.getBytes('/heqi/client/v1/user/auth/avatar'), expectedBytes, ); }); test('头像不存在时返回空值', () async { final client = ApiClient( () => 'JWT test-token', baseUrl: 'https://api.example.com', client: MockClient((request) async => http.Response('', 404)), ); expect(await client.getBytes('/avatar'), isNull); }); test('头像接口异常时返回中文错误', () async { final client = ApiClient( () => 'JWT test-token', baseUrl: 'https://api.example.com', client: MockClient((request) async => http.Response('', 500)), ); expect( () => client.getBytes('/avatar'), throwsA( isA().having((error) => error.message, 'message', '头像加载失败'), ), ); }); test('受保护接口返回 HTTP 401 时通知会话并抛出专用异常', () async { final rejectedTokens = []; final client = ApiClient( () => 'JWT expired-token', baseUrl: 'https://api.example.com', onUnauthorized: rejectedTokens.add, client: MockClient((request) async => http.Response('', 401)), ); await expectLater( client.get('/protected'), throwsA(isA()), ); expect(rejectedTokens, ['JWT expired-token']); }); test('受保护接口返回鉴权业务码时通知会话', () async { final rejectedTokens = []; final client = ApiClient( () => 'JWT expired-token', baseUrl: 'https://api.example.com', onUnauthorized: rejectedTokens.add, client: MockClient( (request) async => http.Response( '{"code":1308,"message":"Token Expired","details":null}', 200, ), ), ); await expectLater( client.get('/protected'), throwsA(isA()), ); expect(rejectedTokens, ['JWT expired-token']); }); test('未鉴权接口返回 401 时不得清理已有会话', () async { final rejectedTokens = []; final client = ApiClient( () => 'JWT current-token', baseUrl: 'https://api.example.com', onUnauthorized: rejectedTokens.add, client: MockClient((request) async => http.Response('', 401)), ); await expectLater( client.post('/login', authenticated: false), throwsA( isA() .having((error) => error.code, 'code', 401) .having((error) => error is SessionExpiredException, 'session expired', isFalse), ), ); expect(rejectedTokens, isEmpty); }); test('头像接口返回 401 时同样触发统一会话失效', () async { final rejectedTokens = []; final client = ApiClient( () => 'JWT expired-token', baseUrl: 'https://api.example.com', onUnauthorized: rejectedTokens.add, client: MockClient((request) async => http.Response('', 401)), ); await expectLater( client.getBytes('/avatar'), throwsA(isA()), ); expect(rejectedTokens, ['JWT expired-token']); }); test('头像接口在 HTTP 200 错误体中返回鉴权码时触发会话失效', () async { final rejectedTokens = []; final client = ApiClient( () => 'JWT expired-token', baseUrl: 'https://api.example.com', onUnauthorized: rejectedTokens.add, client: MockClient( (request) async => http.Response( '{"code":1715,"message":"Unauthorized","details":null}', 200, headers: {'content-type': 'application/json'}, ), ), ); await expectLater( client.getBytes('/avatar'), throwsA(isA()), ); expect(rejectedTokens, ['JWT expired-token']); }); }