import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; import 'package:flutter_test/flutter_test.dart'; import 'package:integration_test/integration_test.dart'; import 'package:spotiflac_android/services/network_storage_service.dart'; import 'package:spotiflac_android/services/platform_bridge.dart'; void main() { final binding = IntegrationTestWidgetsFlutterBinding.ensureInitialized(); HttpOverrides.global = null; var completed = 0; tearDownAll(() { binding.reportData ??= {}; binding.reportData!['webdav_encoding'] = { 'completed_cases': completed, 'platform': Platform.operatingSystem, 'note': 'Real native parser and local WebDAV transport; no live NAS.', }; }); String response(String href, {bool directory = false}) => '$href' '${directory ? '' : '123'}' 'HTTP/1.1 200 OK' ''; String listing(String body) => '$body'; testWidgets('native WebDAV accepts equivalent parent URI encodings', ( tester, ) async { // Direct native calls must fail if the platform artifact is stale or the // channel is unavailable. A Dart compatibility fallback cannot hide it. for (final encodedParent in [false, true]) { final baseParent = encodedParent ? 'Rock%20%26%20Roll' : 'Rock%20&%20Roll'; final hrefParent = encodedParent ? 'Rock%20&%20Roll' : 'Rock%20%26%20Roll'; final result = await PlatformBridge.runNativeDataJob( { 'operation': 'parse_network_listing', 'protocol': 'webdav', 'base_url': 'https://nas.test/dav/$baseParent/', 'path': 'Rock & Roll/', }, bytes: Uint8List.fromList( utf8.encode( listing( response('/dav/$hrefParent/A%20%26%20B.flac') + response('/dav/$hrefParent/Literal%252F.mp3') + response('/dav/$hrefParent/nested%2Fsong.ogg') + response('/dav/Rock%20%26%20Roller/other.flac'), ), ), ), ); expect(result['entries'], [ { 'path': 'Rock & Roll/A & B.flac', 'name': 'A & B.flac', 'directory': false, 'size': 123, }, { 'path': 'Rock & Roll/Literal%2F.mp3', 'name': 'Literal%2F.mp3', 'directory': false, 'size': 123, }, ]); } completed++; }); testWidgets('WebDAV transport lists an encoded album through native worker', ( tester, ) async { final server = await HttpServer.bind(InternetAddress.loopbackIPv4, 0); addTearDown(() => server.close(force: true)); final requests = []; server.listen((request) async { expect(request.method, 'PROPFIND'); expect(request.headers.value('depth'), '1'); await request.drain(); requests.add(request.uri.pathSegments.join('/')); request.response.statusCode = HttpStatus.multiStatus; request.response.write( listing( request.uri.pathSegments.length == 2 ? response('/dav/Artist%20%26%20Band/', directory: true) : response('/dav/Artist%20%26%20Band/A%20%26%20B.flac') + response('/dav/Artist%20%26%20Band/Album%20%2B%20Mix.mp3') + response('/dav/Artist%20%26%20Band/%E9%9F%B3%E6%A5%BD.ogg'), ), ); await request.response.close(); }); final storage = NetworkStorageService(); addTearDown(storage.dispose); final connection = NetworkConnection( id: 'dav-encoding', name: 'NAS fixture', protocol: NetworkProtocol.webdav, address: 'http://127.0.0.1:${server.port}/dav/', ); final albums = await storage.list(connection); expect(albums.single.path, 'Artist & Band/'); final tracks = await storage.list(connection, albums.single.path); expect(tracks.map((entry) => entry.name), [ 'A & B.flac', 'Album + Mix.mp3', '音楽.ogg', ]); expect(tracks.every((entry) => entry.audio), isTrue); expect(requests, ['dav/', 'dav/Artist & Band/']); completed++; }); }