import 'package:flutter_test/flutter_test.dart'; import 'package:flutter/widgets.dart'; import 'package:yumi/app/config/app_config.dart'; import 'package:yumi/app/constants/sc_global_config.dart'; import 'package:yumi/shared/data_sources/models/message/room_rocket_launch_broadcast_message.dart'; import 'package:yumi/ui_kit/widgets/room/rocket/room_rocket_pag_effect_overlay.dart'; void main() { setUpAll(() { AppConfig.initialize(); }); test('normalizes unescaped pag network urls', () { final uri = debugRoomRocketPagNetworkUri( 'https://example.com/assets/火箭 输出/火箭1级.pag?name=火箭 1', ); expect(uri, isNotNull); expect(uri!.scheme, 'https'); expect(uri.host, 'example.com'); expect(uri.toString(), contains('%E7%81%AB%E7%AE%AD')); expect(uri.toString(), contains('%20')); }); test('normalizes protocol-relative and server-relative pag urls', () { final apiUri = Uri.parse(SCGlobalConfig.apiHost); final apiScheme = apiUri.scheme == 'http' ? 'http' : 'https'; expect( debugNormalizeRoomRocketPagResource('//cdn.example.com/rocket.pag'), '$apiScheme://cdn.example.com/rocket.pag', ); expect( debugNormalizeRoomRocketPagResource('/external/oss/local/火箭.pag'), apiUri.resolve('/external/oss/local/火箭.pag').toString(), ); }); test('detects local file sources before falling back to assets', () { expect( debugRoomRocketPagFilePath('file:///tmp/%E7%81%AB%E7%AE%AD.pag'), '/tmp/火箭.pag', ); expect(debugRoomRocketPagSourceKind('/tmp/rocket.pag'), 'file'); expect(debugRoomRocketPagSourceKind('assets/rocket/rocket.pag'), 'asset'); }); test('keeps pag bytes in memory for five minutes by default', () { expect(debugRoomRocketPagMemoryTtl(), const Duration(minutes: 5)); }); test('releases pag bytes when the configured ttl expires', () async { var released = false; final timer = debugScheduleRoomRocketPagMemoryRelease( memoryTtl: const Duration(milliseconds: 10), onRelease: () => released = true, ); expect(released, isFalse); await Future.delayed(const Duration(milliseconds: 30)); expect(released, isTrue); timer?.cancel(); }); test('maps top avatar frame from pag raw canvas using cover fit', () { final rect = debugMapRoomRocketPagRawRectToCoverCanvas( rawRect: const Rect.fromLTWH(291, 164, 168, 168), rawSize: const Size(750, 1624), canvasSize: const Size(392.7272727, 829.090909), ); expect(rect.left, closeTo(152.38, 0.01)); expect(rect.top, closeTo(75.23, 0.01)); expect(rect.width, closeTo(87.97, 0.01)); expect(rect.height, closeTo(87.97, 0.01)); }); test('extracts launch pag urls from nested resource payloads', () { final launch = RoomRocketLaunchBroadcastMessage.fromJson({ 'rocketAnimationResource': { 'sourceUrl': 'https://cdn.example.com/rocket/launch.pag', }, 'rocketIcon': {'url': 'https://cdn.example.com/rocket/icon.png'}, 'room': { 'id': 'room-1', 'cover': {'url': 'https://cdn.example.com/room/cover.png'}, }, }); expect(launch.roomId, 'room-1'); expect( launch.rocketAnimationUrl, 'https://cdn.example.com/rocket/launch.pag', ); expect(launch.rocketIconUrl, 'https://cdn.example.com/rocket/icon.png'); expect(launch.roomCoverUrl, 'https://cdn.example.com/room/cover.png'); }); test('extracts launch data from snake case broadcast payloads', () { final launch = RoomRocketLaunchBroadcastMessage.fromJson({ 'room_id': 'room-1', 'room_account': '10001', 'room_name': 'Rocket Room', 'round_no': 3, 'rocket_level': 2, 'launch_no': 'launch-3', 'rocket_animation_url': 'https://cdn.example.com/rocket/launch.pag', 'rocket_icon_url': 'https://cdn.example.com/rocket/icon.png', 'trigger_user': { 'user_id': 'u1', 'userAvatar': {'sourceUrl': 'https://cdn.example.com/avatar.png'}, }, }); expect(launch.isValid, isTrue); expect(launch.roomId, 'room-1'); expect(launch.roomAccount, '10001'); expect(launch.roomName, 'Rocket Room'); expect(launch.roundNo, 3); expect(launch.safeLevel, 2); expect(launch.launchNo, 'launch-3'); expect( launch.rocketAnimationUrl, 'https://cdn.example.com/rocket/launch.pag', ); expect(launch.rocketIconUrl, 'https://cdn.example.com/rocket/icon.png'); expect(launch.triggerUser.userId, 'u1'); expect(launch.triggerUser.avatar, 'https://cdn.example.com/avatar.png'); }); }