This commit is contained in:
2026-08-17 20:25:08 +04:00
parent b64dffca9a
commit 25e7ad156b
12 changed files with 368 additions and 13 deletions
@@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:moonwell_launcher/features/launcher/data/game_installation_service.dart';
// import 'package:moonwell_launcher/features/launcher/domain/entities/client_hash_entry.dart';
import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_exception.dart';
import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_game_ticket.dart';
void main() {
group('GameInstallationService', () {
@@ -125,5 +126,45 @@ void main() {
throwsA(isA<LauncherSyncException>()),
);
});
test(
'launchGame passes authorization only through child environment',
() async {
final recordingService = _RecordingGameInstallationService();
final authorization = LauncherGameTicket(
account: 'PLAYERONE',
ticket: 'A1B2C3D4E5F6G7H8',
expiresAt: DateTime.now().toUtc().add(const Duration(seconds: 60)),
);
await recordingService.launchGame(
rootDirectory.path,
authorization: authorization,
);
expect(recordingService.arguments, isEmpty);
expect(recordingService.environment, <String, String>{
'MOONWELL_LAUNCH_ACCOUNT': 'PLAYERONE',
'MOONWELL_LAUNCH_TICKET': 'A1B2C3D4E5F6G7H8',
});
},
);
});
}
final class _RecordingGameInstallationService extends GameInstallationService {
Map<String, String>? environment;
List<String>? arguments;
@override
Future<GameProcessHandle> startGameProcess({
required String executablePath,
required List<String> arguments,
required String workingDirectory,
required Map<String, String> environment,
}) async {
this.arguments = List<String>.from(arguments);
this.environment = Map<String, String>.from(environment);
return GameProcessHandle(pid: 42, exitCode: Future<int>.value(0));
}
}
@@ -0,0 +1,61 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:moonwell_launcher/features/launcher/domain/entities/launcher_game_ticket.dart';
void main() {
group('LauncherGameTicket', () {
test('parses the launcher authorization contract', () {
final ticket = LauncherGameTicket.fromJson(<String, Object?>{
'account': 'PLAYERONE',
'ticket': 'A1B2C3D4E5F6G7H8',
'expires_at': '2026-08-16T12:01:00Z',
});
expect(ticket.account, 'PLAYERONE');
expect(ticket.ticket, 'A1B2C3D4E5F6G7H8');
expect(ticket.expiresAt, DateTime.utc(2026, 8, 16, 12, 1));
});
test('rejects tickets outside the native client format', () {
expect(
() => LauncherGameTicket.fromJson(<String, Object?>{
'account': 'PLAYERONE',
'ticket': 'lowercase-ticket',
'expires_at': '2026-08-16T12:01:00Z',
}),
throwsFormatException,
);
});
test('does not normalize credentials from the API', () {
expect(
() => LauncherGameTicket.fromJson(<String, Object?>{
'account': 'PLAYER ',
'ticket': ' A1B2C3D4E5F6G7H8',
'expires_at': '2026-08-16T12:01:00Z',
}),
throwsFormatException,
);
});
test('requires enough validity to start the game', () {
final ticket = LauncherGameTicket(
account: 'PLAYERONE',
ticket: 'A1B2C3D4E5F6G7H8',
expiresAt: DateTime.utc(2026, 8, 16, 12, 0, 4),
);
expect(ticket.isUsable(now: DateTime.utc(2026, 8, 16, 12)), isFalse);
});
});
test('rejects non-string payload fields with a format exception', () {
expect(
() => LauncherGameTicket.fromJson(<String, dynamic>{
'account': 42,
'ticket': 'ABCDEFGH12345678',
'expires_at': '2026-08-16T20:00:00Z',
}),
throwsFormatException,
);
});
}