import 'dart:io'; import 'package:flame_lua_runtime/runtime/packages/game_package.dart'; import 'package:flame_lua_runtime/runtime/packages/game_package_manifest.dart'; import 'package:flame_lua_runtime/runtime/packages/package_verifier.dart'; import 'package:flutter_test/flutter_test.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); group('PackageVerifier', () { test('accepts a valid file package', () async { final package = await _createPackage(); await expectLater( const PackageVerifier(runtimeApiVersion: 1).verify(package), completes, ); }); test('accepts runtime framework module paths', () async { final package = await _createPackage( modules: {'runtime_ui': 'runtime:runtime_ui.lua'}, ); await expectLater( const PackageVerifier(runtimeApiVersion: 1).verify(package), completes, ); }); test('rejects unsupported runtimeApiVersion', () async { final package = await _createPackage(runtimeApiVersion: 2); await expectLater( const PackageVerifier(runtimeApiVersion: 1).verify(package), throwsFormatException, ); }); test('rejects missing Lua entry functions', () async { final package = await _createPackage(script: 'function init(ctx) end'); await expectLater( const PackageVerifier(runtimeApiVersion: 1).verify(package), throwsFormatException, ); }); test('rejects unsafe declared modules', () async { final package = await _createPackage( modules: {'../theme': 'scripts/theme.lua'}, ); await expectLater( const PackageVerifier(runtimeApiVersion: 1).verify(package), throwsFormatException, ); }); test('rejects module paths outside scripts directory', () async { final package = await _createPackage( modules: {'theme': 'assets/theme.lua'}, ); await expectLater( const PackageVerifier(runtimeApiVersion: 1).verify(package), throwsFormatException, ); }); test('rejects missing declared resources', () async { final package = await _createPackage(writeResource: false); await expectLater( const PackageVerifier(runtimeApiVersion: 1).verify(package), throwsFormatException, ); }); test('rejects resource paths that escape package root', () async { final package = await _createPackage(resourcePath: '../outside.png'); await expectLater( const PackageVerifier(runtimeApiVersion: 1).verify(package), throwsFormatException, ); }); }); } Future _createPackage({ int runtimeApiVersion = 1, String script = _validScript, String resourcePath = 'assets/board.png', bool writeResource = true, Map modules = const {'theme': 'scripts/theme.lua'}, }) async { final root = await Directory.systemTemp.createTemp('package_verifier_test_'); Directory('${root.path}/scripts').createSync(recursive: true); Directory('${root.path}/assets').createSync(recursive: true); File('${root.path}/scripts/main.lua').writeAsStringSync(script); File('${root.path}/scripts/theme.lua').writeAsStringSync('return {}'); if (writeResource && !resourcePath.contains('..')) { File('${root.path}/$resourcePath') ..createSync(recursive: true) ..writeAsBytesSync([1, 2, 3]); } final package = GamePackage.file( rootPath: root.path, manifest: GamePackageManifest( gameId: 'ludo', name: 'Ludo', version: '0.1.0', runtimeApiVersion: runtimeApiVersion, entry: 'scripts/main.lua', assetsBase: 'assets', resources: {'board': GameResource(type: 'image', path: resourcePath)}, modules: modules, ), ); addTearDown(() { if (root.existsSync()) { root.deleteSync(recursive: true); } }); return package; } const _validScript = ''' function smoke_test(ctx) return true end function init(ctx) return {} end function on_event(event) return {} end ''';