Files
ReelShort/test/account/account_api_test.dart

175 lines
5.6 KiB
Dart

// Author: fruit.com.cn
import 'dart:convert';
import 'package:flutter_test/flutter_test.dart';
import 'package:fruitshorts/core/data/models/account_profile.dart';
import 'package:fruitshorts/core/data/remote/account_api.dart';
import 'package:fruitshorts/core/data/remote/api_client.dart';
import '../support/fake_http_adapter.dart';
void main() {
String envelope(Object? data, {int code = 0, String message = 'ok'}) =>
jsonEncode({'code': code, 'message': message, 'data': data});
AccountApi apiWith(FakeHttpAdapter adapter) =>
AccountApi(ApiClient(baseUrl: 'http://127.0.0.1:8081', adapter: adapter));
test('should_get_profile_and_parse_numeric_vip_level', () async {
final adapter = FakeHttpAdapter(
(_) async => FakeHttpResponse(
envelope({
'uid': 'U-K19',
'nickname': 'Nadia',
'avatarUrl': 'https://cdn/avatar.png',
'gender': 'female',
'countryCode': 'ID',
'regionCode': 'JK',
'language': 'id',
'timezone': 'Asia/Jakarta',
'currency': 'IDR',
'status': 'normal',
'maskedTarget': 'n***@example.com',
'createdAt': '2026-10-19T08:00:00',
'lastLoginAt': '2026-10-19T09:00:00',
'vipLevel': 2,
'vipExpireAt': null,
}),
),
);
final profile = await apiWith(adapter).profile();
expect(adapter.requests.single.method, 'GET');
expect(adapter.requests.single.uri.path, '/app/me');
expect(profile.uid, 'U-K19');
expect(profile.nickname, 'Nadia');
expect(profile.vipLevel, 2);
expect(profile.createdAt, DateTime(2026, 10, 19, 8));
});
test('should_send_only_changed_profile_fields_when_updating', () async {
final adapter = FakeHttpAdapter(
(_) async => FakeHttpResponse(
envelope({'uid': 'U-K19', 'nickname': 'Nadia Baru'}),
),
);
final profile = await apiWith(adapter).updateProfile(
const AccountProfileUpdate(
nickname: 'Nadia Baru',
gender: 'female',
birthday: '1999-01-02',
),
);
expect(adapter.requests.single.method, 'PUT');
expect(adapter.requests.single.uri.path, '/app/me');
expect(adapter.requests.single.data, {
'nickname': 'Nadia Baru',
'gender': 'female',
'birthday': '1999-01-02',
});
expect(profile.nickname, 'Nadia Baru');
});
test('should_parse_device_ids_from_strings_and_numbers', () async {
final adapter = FakeHttpAdapter(
(_) async => FakeHttpResponse(
envelope([
{
'id': '2111012753262120962',
'userId': '2111012753262120000',
'deviceId': 'd-device-1234',
'platform': 'IOS',
'deviceModel': 'iPhone 15',
'firstSeenAt': '2026-10-18T08:00:00',
'lastSeenAt': '2026-10-19T09:00:00',
'status': 'active',
'deleted': 0,
},
{'id': 7, 'deviceId': 'd-android-5678', 'platform': 'ANDROID'},
]),
),
);
final devices = await apiWith(adapter).devices();
expect(adapter.requests.single.uri.path, '/app/me/devices');
expect(devices.first.id, '2111012753262120962');
expect(devices.first.lastSeenAt, DateTime(2026, 10, 19, 9));
expect(devices.last.id, '7');
expect(devices.last.deviceModel, '');
});
test('should_parse_export_counts_and_keep_complete_json', () async {
final data = {
'profile': {'uid': 'U-K19', 'nickname': 'Nadia'},
'credentials': [
{'type': 'EMAIL', 'masked': 'n***@example.com', 'verified': true},
],
'consents': [
{'scene': 'privacy', 'version': '2026-10-01'},
],
'devices': [
{'id': '9', 'deviceId': 'd-device-1234', 'status': 'active'},
],
};
final adapter = FakeHttpAdapter(
(_) async => FakeHttpResponse(envelope(data)),
);
final export = await apiWith(adapter).export();
expect(adapter.requests.single.uri.path, '/app/me/export');
expect(export.profile.nickname, 'Nadia');
expect(export.credentials.single.masked, 'n***@example.com');
expect(export.credentials.single.verified, isTrue);
expect(export.consentCount, 1);
expect(export.deviceCount, 1);
expect(export.rawJson, contains('"credentials"'));
});
test('should_request_deactivation_with_reason_and_parse_schedule', () async {
final adapter = FakeHttpAdapter(
(_) async => FakeHttpResponse(envelope('2026-11-03T09:30:00')),
);
final scheduledAt = await apiWith(adapter).deactivate(reason: '不再使用');
expect(adapter.requests.single.method, 'POST');
expect(adapter.requests.single.uri.path, '/app/me/deactivate');
expect(adapter.requests.single.data, {'reason': '不再使用'});
expect(scheduledAt, DateTime(2026, 11, 3, 9, 30));
});
test('should_cancel_deactivation_with_post_and_no_body', () async {
final adapter = FakeHttpAdapter(
(_) async => FakeHttpResponse(envelope(null)),
);
await apiWith(adapter).cancelDeactivation();
expect(adapter.requests.single.method, 'POST');
expect(adapter.requests.single.uri.path, '/app/me/deactivate/cancel');
expect(adapter.requests.single.data, isNull);
});
test('should_keep_backend_message_when_cancel_fails', () async {
final adapter = FakeHttpAdapter(
(_) async =>
FakeHttpResponse(envelope(null, code: 100009, message: '没有待处理的注销申请')),
);
await expectLater(
apiWith(adapter).cancelDeactivation(),
throwsA(
isA<ApiException>()
.having((error) => error.code, 'code', 100009)
.having((error) => error.message, 'message', '没有待处理的注销申请'),
),
);
});
}