mirror of
https://github.com/Auties00/Reboot-Launcher.git
synced 2026-01-13 03:02:22 +01:00
Renamed backend into auth_backend and added server_browser_backend implementation to replace Supabase.
This commit is contained in:
@@ -3,7 +3,6 @@ import 'dart:io';
|
||||
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter_acrylic/flutter_acrylic.dart';
|
||||
import 'package:flutter_gen/gen_l10n/reboot_localizations.dart';
|
||||
import 'package:flutter_localized_locales/flutter_localized_locales.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get_storage/get_storage.dart';
|
||||
@@ -24,6 +23,8 @@ import 'package:system_theme/system_theme.dart';
|
||||
import 'package:version/version.dart';
|
||||
import 'package:window_manager/window_manager.dart';
|
||||
|
||||
import 'l10n/reboot_localizations.dart';
|
||||
|
||||
const double kDefaultWindowWidth = 1164;
|
||||
const double kDefaultWindowHeight = 864;
|
||||
const String kCustomUrlSchema = "Reboot";
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:fluent_ui/fluent_ui.dart';
|
||||
import 'package:flutter_gen/gen_l10n/reboot_localizations.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:reboot_launcher/l10n/reboot_localizations.dart';
|
||||
|
||||
AppLocalizations? _translations;
|
||||
bool _init = false;
|
||||
|
||||
@@ -3,10 +3,10 @@ import 'dart:math';
|
||||
import 'package:async/async.dart';
|
||||
import 'package:fluent_ui/fluent_ui.dart' hide FluentIcons;
|
||||
import 'package:fluentui_system_icons/fluentui_system_icons.dart';
|
||||
import 'package:flutter_gen/gen_l10n/reboot_localizations.dart';
|
||||
import 'package:flutter_localized_locales/flutter_localized_locales.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:reboot_common/common.dart';
|
||||
import 'package:reboot_launcher/l10n/reboot_localizations.dart';
|
||||
import 'package:reboot_launcher/src/controller/dll_controller.dart';
|
||||
import 'package:reboot_launcher/src/controller/settings_controller.dart';
|
||||
import 'package:reboot_launcher/src/messenger/dialog.dart';
|
||||
|
||||
3
server_browser_backend/.gitignore
vendored
Normal file
3
server_browser_backend/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
# https://dart.dev/guides/libraries/private-files
|
||||
# Created by `dart pub`
|
||||
.dart_tool/
|
||||
2
server_browser_backend/README.md
Normal file
2
server_browser_backend/README.md
Normal file
@@ -0,0 +1,2 @@
|
||||
A sample command-line application with an entrypoint in `bin/`, library code
|
||||
in `lib/`, and example unit test in `test/`.
|
||||
30
server_browser_backend/analysis_options.yaml
Normal file
30
server_browser_backend/analysis_options.yaml
Normal file
@@ -0,0 +1,30 @@
|
||||
# This file configures the static analysis results for your project (errors,
|
||||
# warnings, and lints).
|
||||
#
|
||||
# This enables the 'recommended' set of lints from `package:lints`.
|
||||
# This set helps identify many issues that may lead to problems when running
|
||||
# or consuming Dart code, and enforces writing Dart using a single, idiomatic
|
||||
# style and format.
|
||||
#
|
||||
# If you want a smaller set of lints you can change this to specify
|
||||
# 'package:lints/core.yaml'. These are just the most critical lints
|
||||
# (the recommended set includes the core lints).
|
||||
# The core lints are also what is used by pub.dev for scoring packages.
|
||||
|
||||
include: package:lints/recommended.yaml
|
||||
|
||||
# Uncomment the following section to specify additional rules.
|
||||
|
||||
# linter:
|
||||
# rules:
|
||||
# - camel_case_types
|
||||
|
||||
# analyzer:
|
||||
# exclude:
|
||||
# - path/to/excluded/files/**
|
||||
|
||||
# For more information about the core and recommended set of lints, see
|
||||
# https://dart.dev/go/core-lints
|
||||
|
||||
# For additional information about configuring this file, see
|
||||
# https://dart.dev/guides/language/analysis-options
|
||||
6
server_browser_backend/bin/main.dart
Normal file
6
server_browser_backend/bin/main.dart
Normal file
@@ -0,0 +1,6 @@
|
||||
import 'package:server_browser_backend/server_browser_backend.dart';
|
||||
|
||||
void main() async {
|
||||
final server = WebSocketServer();
|
||||
await server.start(port: 8080);
|
||||
}
|
||||
4
server_browser_backend/lib/server_browser_backend.dart
Normal file
4
server_browser_backend/lib/server_browser_backend.dart
Normal file
@@ -0,0 +1,4 @@
|
||||
library;
|
||||
|
||||
export 'src/server_entry.dart';
|
||||
export 'src/web_socket.dart';
|
||||
51
server_browser_backend/lib/src/server_entry.dart
Normal file
51
server_browser_backend/lib/src/server_entry.dart
Normal file
@@ -0,0 +1,51 @@
|
||||
class ServerEntry {
|
||||
final String id;
|
||||
final String name;
|
||||
final String description;
|
||||
final String version;
|
||||
final String password;
|
||||
final DateTime timestamp;
|
||||
final String ip;
|
||||
final String author;
|
||||
final bool discoverable;
|
||||
|
||||
ServerEntry({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.version,
|
||||
required this.password,
|
||||
required this.timestamp,
|
||||
required this.ip,
|
||||
required this.author,
|
||||
required this.discoverable,
|
||||
});
|
||||
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'description': description,
|
||||
'version': version,
|
||||
'password': password,
|
||||
'timestamp': timestamp.toIso8601String(),
|
||||
'ip': ip,
|
||||
'author': author,
|
||||
'discoverable': discoverable,
|
||||
};
|
||||
}
|
||||
|
||||
static ServerEntry fromJson(Map<String, dynamic> json) {
|
||||
return ServerEntry(
|
||||
id: json['id'],
|
||||
name: json['name'],
|
||||
description: json['description'],
|
||||
version: json['version'],
|
||||
password: json['password'],
|
||||
timestamp: DateTime.parse(json['timestamp']),
|
||||
ip: json['ip'],
|
||||
author: json['author'],
|
||||
discoverable: json['discoverable'],
|
||||
);
|
||||
}
|
||||
}
|
||||
126
server_browser_backend/lib/src/web_socket.dart
Normal file
126
server_browser_backend/lib/src/web_socket.dart
Normal file
@@ -0,0 +1,126 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:server_browser_backend/src/server_entry.dart';
|
||||
|
||||
class WebSocketServer {
|
||||
static const String addEvent = 'add';
|
||||
static const String removeEvent = 'remove';
|
||||
|
||||
final Map<String, ServerEntry> _entries = {};
|
||||
final Set<WebSocket> _clients = {};
|
||||
|
||||
late HttpServer _server;
|
||||
|
||||
Future<void> start({int port = 8080}) async {
|
||||
_server = await HttpServer.bind('0.0.0.0', port);
|
||||
_listen();
|
||||
}
|
||||
|
||||
Future<void> _listen() async {
|
||||
await for (HttpRequest request in _server) {
|
||||
if (WebSocketTransformer.isUpgradeRequest(request)) {
|
||||
_handleWebSocketUpgrade(request);
|
||||
} else {
|
||||
request.response.statusCode = 404;
|
||||
request.response.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _handleWebSocketUpgrade(HttpRequest request) async {
|
||||
final client = await WebSocketTransformer.upgrade(request);
|
||||
_clients.add(client);
|
||||
await _sendAllEntriesToClient(client);
|
||||
client.listen(
|
||||
(message) => _handleMessage(client, message),
|
||||
onDone: () => _removeClient(client),
|
||||
onError: (error) => _removeClient(client)
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _sendAllEntriesToClient(WebSocket client) async {
|
||||
final message = {
|
||||
'type': addEvent,
|
||||
'data': _entries.values.map((entry) => entry.toJson()).toList()
|
||||
};
|
||||
client.add(json.encode(message));
|
||||
}
|
||||
|
||||
void _handleMessage(WebSocket client, dynamic message) {
|
||||
String? type;
|
||||
try {
|
||||
final data = jsonDecode(message);
|
||||
type = data['type'];
|
||||
final payload = data['data'];
|
||||
switch (type) {
|
||||
case addEvent:
|
||||
final entry = ServerEntry.fromJson(payload);
|
||||
_entries[entry.id] = entry;
|
||||
_broadcastEvent(addEvent, entry.toJson());
|
||||
break;
|
||||
case removeEvent:
|
||||
final deletedEntry = _entries.remove(payload);
|
||||
if (deletedEntry != null) {
|
||||
_broadcastEvent(removeEvent, {'id': deletedEntry.id});
|
||||
}else {
|
||||
_answer(client, removeEvent, "Invalid server entry");
|
||||
}
|
||||
break;
|
||||
default:
|
||||
_answer(client, type, 'Unknown type');
|
||||
break;
|
||||
}
|
||||
} catch(error) {
|
||||
_answer(client, type, error.toString());
|
||||
}
|
||||
}
|
||||
|
||||
void _broadcastEvent(String eventType, Map<String, dynamic> eventData) {
|
||||
final message = {
|
||||
'type': eventType,
|
||||
'data': [
|
||||
eventData
|
||||
]
|
||||
};
|
||||
|
||||
final messageJson = json.encode(message);
|
||||
final clientsToRemove = <WebSocket>[];
|
||||
|
||||
for (final client in _clients) {
|
||||
try {
|
||||
client.add(messageJson);
|
||||
} catch (e) {
|
||||
clientsToRemove.add(client);
|
||||
}
|
||||
}
|
||||
|
||||
for (final client in clientsToRemove) {
|
||||
_removeClient(client);
|
||||
}
|
||||
}
|
||||
|
||||
void _removeClient(WebSocket client) {
|
||||
client.close();
|
||||
_clients.remove(client);
|
||||
}
|
||||
|
||||
void _answer(WebSocket client, String? eventType, [String? error]) {
|
||||
final message = {};
|
||||
if(eventType != null) {
|
||||
message['type'] = eventType;
|
||||
}
|
||||
if(error != null) {
|
||||
message['success'] = false;
|
||||
message['message'] = error;
|
||||
}else {
|
||||
message['success'] = true;
|
||||
}
|
||||
client.add(json.encode(message));
|
||||
}
|
||||
|
||||
Future<void> stop() async {
|
||||
await _server.close(force: true);
|
||||
_clients.clear();
|
||||
}
|
||||
}
|
||||
15
server_browser_backend/pubspec.yaml
Normal file
15
server_browser_backend/pubspec.yaml
Normal file
@@ -0,0 +1,15 @@
|
||||
name: server_browser_backend
|
||||
description: A sample command-line application.
|
||||
version: 1.0.0
|
||||
# repository: https://github.com/my_org/my_repo
|
||||
|
||||
environment:
|
||||
sdk: ^3.8.1
|
||||
|
||||
# Add regular dependencies here.
|
||||
dependencies:
|
||||
# path: ^1.8.0
|
||||
|
||||
dev_dependencies:
|
||||
lints: ^5.0.0
|
||||
test: ^1.24.0
|
||||
437
server_browser_backend/test/test.dart
Normal file
437
server_browser_backend/test/test.dart
Normal file
@@ -0,0 +1,437 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:server_browser_backend/server_browser_backend.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
group('WebSocket Server Tests', () {
|
||||
late WebSocketServer server;
|
||||
final int testPort = 8081;
|
||||
|
||||
setUp(() async {
|
||||
server = WebSocketServer();
|
||||
await server.start(port: testPort);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await server.stop();
|
||||
});
|
||||
|
||||
test('should allow client connection and receive initial empty data', () async {
|
||||
final client = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final messagesFuture = client.toList();
|
||||
|
||||
await client.close();
|
||||
|
||||
final messages = await messagesFuture;
|
||||
|
||||
expect(messages.length, equals(1));
|
||||
|
||||
final firstMessage = jsonDecode(messages[0]);
|
||||
expect(firstMessage['type'], equals('add'));
|
||||
expect(firstMessage['data'], equals([]));
|
||||
});
|
||||
|
||||
test('should add server entry and broadcast to all clients', () async {
|
||||
final client1 = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final client1MessagesFuture = client1.toList();
|
||||
final client2 = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final client2MessagesFuture = client2.toList();
|
||||
|
||||
final testEntry = {
|
||||
'id': 'test-server-1',
|
||||
'name': 'Test Server',
|
||||
'description': 'A test server',
|
||||
'version': '1.0.0',
|
||||
'password': 'secret123',
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
'ip': '127.0.0.1',
|
||||
'author': 'Test Author',
|
||||
'discoverable': true,
|
||||
};
|
||||
|
||||
final addMessage = {
|
||||
'type': 'add',
|
||||
'data': testEntry,
|
||||
};
|
||||
|
||||
client1.add(jsonEncode(addMessage));
|
||||
|
||||
await client1.close();
|
||||
await client2.close();
|
||||
|
||||
final client1Messages = await client1MessagesFuture;
|
||||
final client2Messages = await client2MessagesFuture;
|
||||
|
||||
expect(client1Messages.length, equals(2));
|
||||
expect(client2Messages.length, equals(2));
|
||||
|
||||
final client1Initial = jsonDecode(client1Messages[0]);
|
||||
final client2Initial = jsonDecode(client2Messages[0]);
|
||||
expect(client1Initial['type'], equals('add'));
|
||||
expect(client1Initial['data'], equals([]));
|
||||
expect(client2Initial['type'], equals('add'));
|
||||
expect(client2Initial['data'], equals([]));
|
||||
|
||||
final client1Broadcast = jsonDecode(client1Messages[1]);
|
||||
final client2Broadcast = jsonDecode(client2Messages[1]);
|
||||
|
||||
expect(client1Broadcast['type'], equals('add'));
|
||||
expect(client1Broadcast['data'], isA<List>());
|
||||
expect(client1Broadcast['data'].length, equals(1));
|
||||
expect(client1Broadcast['data'][0]['id'], equals('test-server-1'));
|
||||
|
||||
expect(client2Broadcast['type'], equals('add'));
|
||||
expect(client2Broadcast['data'], isA<List>());
|
||||
expect(client2Broadcast['data'].length, equals(1));
|
||||
expect(client2Broadcast['data'][0]['id'], equals('test-server-1'));
|
||||
});
|
||||
|
||||
test('should send existing entries to new client', () async {
|
||||
final client1 = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final client1MessagesFuture = client1.toList();
|
||||
|
||||
final testEntry = {
|
||||
'id': 'existing-server',
|
||||
'name': 'Existing Server',
|
||||
'description': 'Already exists',
|
||||
'version': '2.0.0',
|
||||
'password': 'existing123',
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
'ip': '192.168.1.1',
|
||||
'author': 'Existing Author',
|
||||
'discoverable': false,
|
||||
};
|
||||
|
||||
final addMessage = {
|
||||
'type': 'add',
|
||||
'data': testEntry,
|
||||
};
|
||||
|
||||
client1.add(jsonEncode(addMessage));
|
||||
|
||||
final client2 = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final client2MessagesFuture = client2.toList();
|
||||
|
||||
await client1.close();
|
||||
await client2.close();
|
||||
|
||||
final client1Messages = await client1MessagesFuture;
|
||||
final client2Messages = await client2MessagesFuture;
|
||||
|
||||
expect(client1Messages.length, equals(2));
|
||||
expect(client2Messages.length, equals(1));
|
||||
|
||||
final client2InitialMessage = jsonDecode(client2Messages[0]);
|
||||
expect(client2InitialMessage['type'], equals('add'));
|
||||
expect(client2InitialMessage['data'], isA<List>());
|
||||
expect(client2InitialMessage['data'].length, equals(1));
|
||||
expect(client2InitialMessage['data'][0]['id'], equals('existing-server'));
|
||||
});
|
||||
|
||||
test('should remove server entry and broadcast removal', () async {
|
||||
final client1 = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final client1MessagesFuture = client1.toList();
|
||||
final client2 = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final client2MessagesFuture = client2.toList();
|
||||
|
||||
final testEntry = {
|
||||
'id': 'server-to-remove',
|
||||
'name': 'Server To Remove',
|
||||
'description': 'Will be removed',
|
||||
'version': '1.0.0',
|
||||
'password': 'remove123',
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
'ip': '10.0.0.1',
|
||||
'author': 'Remove Author',
|
||||
'discoverable': true,
|
||||
};
|
||||
|
||||
final addMessage = {
|
||||
'type': 'add',
|
||||
'data': testEntry,
|
||||
};
|
||||
client1.add(jsonEncode(addMessage));
|
||||
|
||||
final removeMessage = {
|
||||
'type': 'remove',
|
||||
'data': 'server-to-remove',
|
||||
};
|
||||
client1.add(jsonEncode(removeMessage));
|
||||
|
||||
await client1.close();
|
||||
await client2.close();
|
||||
|
||||
final client1Messages = await client1MessagesFuture;
|
||||
final client2Messages = await client2MessagesFuture;
|
||||
|
||||
expect(client1Messages.length, equals(3));
|
||||
expect(client2Messages.length, equals(3));
|
||||
|
||||
final client1RemoveResponse = jsonDecode(client1Messages[2]);
|
||||
final client2RemoveResponse = jsonDecode(client2Messages[2]);
|
||||
|
||||
expect(client1RemoveResponse['type'], equals('remove'));
|
||||
expect(client1RemoveResponse['data'], isA<List>());
|
||||
expect(client1RemoveResponse['data'].length, equals(1));
|
||||
expect(client1RemoveResponse['data'][0]['id'], equals('server-to-remove'));
|
||||
|
||||
expect(client2RemoveResponse['type'], equals('remove'));
|
||||
expect(client2RemoveResponse['data'], isA<List>());
|
||||
expect(client2RemoveResponse['data'].length, equals(1));
|
||||
expect(client2RemoveResponse['data'][0]['id'], equals('server-to-remove'));
|
||||
});
|
||||
|
||||
test('should handle removal of non-existent entry', () async {
|
||||
final client = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final messagesFuture = client.toList();
|
||||
|
||||
final removeMessage = {
|
||||
'type': 'remove',
|
||||
'data': 'non-existent-id',
|
||||
};
|
||||
|
||||
client.add(jsonEncode(removeMessage));
|
||||
|
||||
await client.close();
|
||||
final messages = await messagesFuture;
|
||||
|
||||
expect(messages.length, equals(2));
|
||||
|
||||
final response = jsonDecode(messages[1]);
|
||||
expect(response['type'], equals('remove'));
|
||||
expect(response['success'], equals(false));
|
||||
expect(response['message'], equals('Invalid server entry'));
|
||||
});
|
||||
|
||||
test('should handle unknown message type', () async {
|
||||
final client = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final messagesFuture = client.toList();
|
||||
|
||||
final unknownMessage = {
|
||||
'type': 'unknown',
|
||||
'data': {'some': 'data'},
|
||||
};
|
||||
|
||||
client.add(jsonEncode(unknownMessage));
|
||||
|
||||
await client.close();
|
||||
final messages = await messagesFuture;
|
||||
|
||||
expect(messages.length, equals(2));
|
||||
|
||||
final response = jsonDecode(messages[1]);
|
||||
expect(response['type'], equals('unknown'));
|
||||
expect(response['success'], equals(false));
|
||||
expect(response['message'], equals('Unknown type'));
|
||||
});
|
||||
|
||||
test('should handle invalid JSON', () async {
|
||||
final client = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final messagesFuture = client.toList();
|
||||
|
||||
client.add('invalid json string');
|
||||
|
||||
await client.close();
|
||||
final messages = await messagesFuture;
|
||||
|
||||
expect(messages.length, equals(2));
|
||||
|
||||
final response = jsonDecode(messages[1]);
|
||||
expect(response['success'], equals(false));
|
||||
expect(response.containsKey('message'), isTrue);
|
||||
});
|
||||
|
||||
test('should handle multiple clients adding different entries', () async {
|
||||
final client1 = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final client1MessagesFuture = client1.toList();
|
||||
final client2 = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final client2MessagesFuture = client2.toList();
|
||||
final client3 = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final client3MessagesFuture = client3.toList();
|
||||
|
||||
final entry1 = {
|
||||
'id': 'server-1',
|
||||
'name': 'Server One',
|
||||
'description': 'First server',
|
||||
'version': '1.0.0',
|
||||
'password': 'pass1',
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
'ip': '192.168.1.1',
|
||||
'author': 'Author 1',
|
||||
'discoverable': true,
|
||||
};
|
||||
|
||||
final entry2 = {
|
||||
'id': 'server-2',
|
||||
'name': 'Server Two',
|
||||
'description': 'Second server',
|
||||
'version': '2.0.0',
|
||||
'password': 'pass2',
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
'ip': '192.168.1.2',
|
||||
'author': 'Author 2',
|
||||
'discoverable': false,
|
||||
};
|
||||
|
||||
client1.add(jsonEncode({'type': 'add', 'data': entry1}));
|
||||
client2.add(jsonEncode({'type': 'add', 'data': entry2}));
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 200));
|
||||
|
||||
await client1.close();
|
||||
await client2.close();
|
||||
await client3.close();
|
||||
|
||||
final client1Messages = await client1MessagesFuture;
|
||||
final client2Messages = await client2MessagesFuture;
|
||||
final client3Messages = await client3MessagesFuture;
|
||||
|
||||
expect(client1Messages.length, equals(3));
|
||||
expect(client2Messages.length, equals(3));
|
||||
expect(client3Messages.length, equals(3));
|
||||
|
||||
final allServerIds = <String>{};
|
||||
|
||||
for (final messages in [client1Messages, client2Messages, client3Messages]) {
|
||||
for (int i = 1; i < messages.length; i++) {
|
||||
final parsed = jsonDecode(messages[i]);
|
||||
if (parsed['type'] == 'add' && parsed['data'] is List) {
|
||||
for (final entry in parsed['data']) {
|
||||
allServerIds.add(entry['id']);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
expect(allServerIds, containsAll(['server-1', 'server-2']));
|
||||
});
|
||||
|
||||
test('should handle client disconnection gracefully', () async {
|
||||
final client1 = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final client1MessagesFuture = client1.toList();
|
||||
final client2 = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final client2MessagesFuture = client2.toList();
|
||||
|
||||
await client1.close();
|
||||
final client1Messages = await client1MessagesFuture;
|
||||
expect(client1Messages.length, equals(1));
|
||||
|
||||
final testEntry = {
|
||||
'id': 'after-disconnect',
|
||||
'name': 'After Disconnect',
|
||||
'description': 'Added after client disconnect',
|
||||
'version': '1.0.0',
|
||||
'password': 'disconnect123',
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
'ip': '172.16.0.1',
|
||||
'author': 'Disconnect Author',
|
||||
'discoverable': true,
|
||||
};
|
||||
|
||||
client2.add(jsonEncode({'type': 'add', 'data': testEntry}));
|
||||
|
||||
await client2.close();
|
||||
final client2Messages = await client2MessagesFuture;
|
||||
|
||||
expect(client2Messages.length, equals(2));
|
||||
|
||||
final response = jsonDecode(client2Messages[1]);
|
||||
expect(response['type'], equals('add'));
|
||||
expect(response['data'][0]['id'], equals('after-disconnect'));
|
||||
});
|
||||
|
||||
test('should handle ServerEntry serialization correctly', () async {
|
||||
final client = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final messagesFuture = client.toList();
|
||||
|
||||
final testEntry = {
|
||||
'id': 'serialization-test',
|
||||
'name': 'Serialization Test',
|
||||
'description': 'Testing serialization',
|
||||
'version': '3.1.4',
|
||||
'password': 'serialize123',
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
'ip': '203.0.113.1',
|
||||
'author': 'Serialization Author',
|
||||
'discoverable': true,
|
||||
};
|
||||
|
||||
client.add(jsonEncode({'type': 'add', 'data': testEntry}));
|
||||
|
||||
await client.close();
|
||||
final messages = await messagesFuture;
|
||||
|
||||
expect(messages.length, equals(2));
|
||||
|
||||
final broadcastMessage = jsonDecode(messages[1]);
|
||||
final receivedEntry = broadcastMessage['data'][0];
|
||||
|
||||
expect(receivedEntry['id'], equals('serialization-test'));
|
||||
expect(receivedEntry['name'], equals('Serialization Test'));
|
||||
expect(receivedEntry['description'], equals('Testing serialization'));
|
||||
expect(receivedEntry['version'], equals('3.1.4'));
|
||||
expect(receivedEntry['password'], equals('serialize123'));
|
||||
expect(receivedEntry['ip'], equals('203.0.113.1'));
|
||||
expect(receivedEntry['author'], equals('Serialization Author'));
|
||||
expect(receivedEntry['discoverable'], equals(true));
|
||||
expect(receivedEntry['timestamp'], isA<String>());
|
||||
|
||||
expect(() => DateTime.parse(receivedEntry['timestamp']), returnsNormally);
|
||||
});
|
||||
|
||||
test('should handle rapid sequential operations', () async {
|
||||
final client1 = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final client1MessagesFuture = client1.toList();
|
||||
final client2 = await WebSocket.connect('ws://localhost:$testPort');
|
||||
final client2MessagesFuture = client2.toList();
|
||||
|
||||
final entry1 = {
|
||||
'id': 'rapid-1',
|
||||
'name': 'Rapid Server 1',
|
||||
'description': 'First rapid server',
|
||||
'version': '1.0.0',
|
||||
'password': 'rapid123',
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
'ip': '10.0.0.1',
|
||||
'author': 'Rapid Author',
|
||||
'discoverable': true,
|
||||
};
|
||||
|
||||
final entry2 = {
|
||||
'id': 'rapid-2',
|
||||
'name': 'Rapid Server 2',
|
||||
'description': 'Second rapid server',
|
||||
'version': '1.0.1',
|
||||
'password': 'rapid456',
|
||||
'timestamp': DateTime.now().toIso8601String(),
|
||||
'ip': '10.0.0.2',
|
||||
'author': 'Rapid Author',
|
||||
'discoverable': false,
|
||||
};
|
||||
|
||||
client1.add(jsonEncode({'type': 'add', 'data': entry1}));
|
||||
client1.add(jsonEncode({'type': 'add', 'data': entry2}));
|
||||
client1.add(jsonEncode({'type': 'remove', 'data': 'rapid-1'}));
|
||||
|
||||
await Future.delayed(Duration(milliseconds: 200));
|
||||
|
||||
await client1.close();
|
||||
await client2.close();
|
||||
|
||||
final client1Messages = await client1MessagesFuture;
|
||||
final client2Messages = await client2MessagesFuture;
|
||||
|
||||
expect(client1Messages.length, equals(4));
|
||||
expect(client2Messages.length, equals(4));
|
||||
|
||||
final lastAddMessage = jsonDecode(client1Messages[2]);
|
||||
expect(lastAddMessage['type'], equals('add'));
|
||||
expect(lastAddMessage['data'][0]['id'], equals('rapid-2'));
|
||||
|
||||
final removeMessage = jsonDecode(client1Messages[3]);
|
||||
expect(removeMessage['type'], equals('remove'));
|
||||
expect(removeMessage['data'][0]['id'], equals('rapid-1'));
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user