It works baby, seems like timing could be improved or something, but it freakin works
This commit is contained in:
@@ -0,0 +1,272 @@
|
||||
import 'dart:async';
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:mumbullet/mumbullet.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
/// Integration test for Mumble connection
|
||||
///
|
||||
/// This test:
|
||||
/// - Requires Docker to be running
|
||||
/// - Uses a dedicated docker-compose.test.yml file
|
||||
/// - Creates a Mumble server with predefined channels
|
||||
/// - Tests connection, authentication, and channel navigation
|
||||
///
|
||||
/// The server is automatically started before tests and stopped after tests
|
||||
/// using docker-compose commands.
|
||||
@Tags(['docker'])
|
||||
void main() {
|
||||
// Use setUpAll and tearDownAll to ensure Docker is only started/stopped once for all tests
|
||||
late MumbleConnection connection;
|
||||
late AppConfig config;
|
||||
|
||||
const rootPath = '/home/nate/source/non-work/mumbullet';
|
||||
const configPath = '$rootPath/test/fixtures/test_config.json';
|
||||
|
||||
setUpAll(() async {
|
||||
// Create cache directory if it doesn't exist
|
||||
final cacheDir = Directory('$rootPath/test/fixtures/cache');
|
||||
if (!await cacheDir.exists()) {
|
||||
await cacheDir.create(recursive: true);
|
||||
}
|
||||
|
||||
// Check if Docker is running
|
||||
try {
|
||||
final checkDocker = await Process.run('docker', ['info']);
|
||||
if (checkDocker.exitCode != 0) {
|
||||
fail('Docker is not running. Please start Docker and try again.');
|
||||
}
|
||||
} catch (e) {
|
||||
fail('Failed to check Docker status: $e. Is Docker installed?');
|
||||
}
|
||||
|
||||
// Start Docker container using test-specific compose file
|
||||
print('Starting Mumble server container for tests...');
|
||||
final dockerProcess = await Process.run(
|
||||
'docker-compose',
|
||||
['-f', 'test/integration/docker-compose.test.yml', 'up', '-d'],
|
||||
workingDirectory: rootPath
|
||||
);
|
||||
|
||||
if (dockerProcess.exitCode != 0) {
|
||||
fail('Failed to start Docker container: ${dockerProcess.stderr}');
|
||||
}
|
||||
|
||||
// Wait for server to start
|
||||
print('Waiting for Mumble server to start...');
|
||||
await Future.delayed(Duration(seconds: 5));
|
||||
});
|
||||
|
||||
tearDownAll(() async {
|
||||
// Stop Docker container
|
||||
print('Stopping Mumble server container...');
|
||||
final stopProcess = await Process.run(
|
||||
'docker-compose',
|
||||
['-f', 'test/integration/docker-compose.test.yml', 'down', '-v'],
|
||||
workingDirectory: rootPath
|
||||
);
|
||||
|
||||
if (stopProcess.exitCode != 0) {
|
||||
print('Warning: Failed to stop Docker container: ${stopProcess.stderr}');
|
||||
}
|
||||
});
|
||||
|
||||
setUp(() async {
|
||||
// Load test configuration
|
||||
config = AppConfig.fromFile(configPath);
|
||||
|
||||
// Create connection
|
||||
connection = MumbleConnection(config.mumble);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
// Disconnect from server
|
||||
if (connection.isConnected) {
|
||||
await connection.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
test('Should connect to Mumble server with password', () async {
|
||||
// Setup test expectations
|
||||
final connectionCompleter = Completer<bool>();
|
||||
|
||||
// Listen for connection state changes
|
||||
connection.connectionState.listen((isConnected) {
|
||||
if (isConnected && !connectionCompleter.isCompleted) {
|
||||
connectionCompleter.complete(true);
|
||||
}
|
||||
});
|
||||
|
||||
// Attempt to connect with password
|
||||
await connection.connect();
|
||||
|
||||
// Wait for connection or timeout
|
||||
expect(
|
||||
await connectionCompleter.future.timeout(Duration(seconds: 10), onTimeout: () => false),
|
||||
isTrue,
|
||||
reason: 'Failed to connect to Mumble server with password',
|
||||
);
|
||||
|
||||
// Verify connection is established
|
||||
expect(connection.isConnected, isTrue);
|
||||
|
||||
// Verify we can get channel information
|
||||
expect(connection.client, isNotNull);
|
||||
expect(connection.client!.rootChannel, isNotNull);
|
||||
|
||||
// Verify we can send a message
|
||||
await connection.sendChannelMessage('Integration test message');
|
||||
});
|
||||
|
||||
test('Should join different channels', () async {
|
||||
// Setup test expectations
|
||||
final connectionCompleter = Completer<bool>();
|
||||
|
||||
// Listen for connection state changes
|
||||
connection.connectionState.listen((isConnected) {
|
||||
if (isConnected && !connectionCompleter.isCompleted) {
|
||||
connectionCompleter.complete(true);
|
||||
}
|
||||
});
|
||||
|
||||
// Connect to server
|
||||
await connection.connect();
|
||||
|
||||
// Wait for connection
|
||||
expect(await connectionCompleter.future.timeout(Duration(seconds: 10), onTimeout: () => false), isTrue);
|
||||
|
||||
expect(connection.client, isNotNull);
|
||||
final channels = connection.client?.getChannels();
|
||||
print(channels?.values);
|
||||
expect(channels, isNotNull);
|
||||
expect(channels?.length, greaterThan(1));
|
||||
|
||||
// Test joining the Music channel
|
||||
await connection.joinChannel('Music');
|
||||
expect(connection.currentChannel?.name, equals('Music'));
|
||||
|
||||
// Test joining a subchannel
|
||||
await connection.joinChannel('Subroom1');
|
||||
expect(connection.currentChannel?.name, equals('Subroom1'));
|
||||
|
||||
// Test joining another top-level channel
|
||||
await connection.joinChannel('General');
|
||||
expect(connection.currentChannel?.name, equals('General'));
|
||||
|
||||
// Test joining the Gaming channel
|
||||
await connection.joinChannel('Gaming');
|
||||
expect(connection.currentChannel?.name, equals('Gaming'));
|
||||
|
||||
// Test joining the root channel
|
||||
await connection.joinChannel('');
|
||||
expect(connection.currentChannel?.name, equals('Root'));
|
||||
});
|
||||
|
||||
test('Should handle authentication failure', () async {
|
||||
// Create a connection with incorrect password
|
||||
final badConnection = MumbleConnection(
|
||||
MumbleConfig(
|
||||
server: config.mumble.server,
|
||||
port: config.mumble.port,
|
||||
username: config.mumble.username,
|
||||
password: 'wrongpassword',
|
||||
channel: config.mumble.channel,
|
||||
),
|
||||
);
|
||||
|
||||
// Try to connect with wrong password
|
||||
try {
|
||||
await badConnection.connect();
|
||||
fail('Should have failed to connect with wrong password');
|
||||
} catch (e) {
|
||||
// Expected failure
|
||||
expect(badConnection.isConnected, isFalse);
|
||||
} finally {
|
||||
// Clean up
|
||||
await badConnection.disconnect();
|
||||
}
|
||||
});
|
||||
|
||||
// test('Should allow multiple bots to connect simultaneously', () async {
|
||||
// // Load additional config
|
||||
// final adminConfig = AppConfig.fromFile('${rootPath}/test/fixtures/test_configs/admin_config.json');
|
||||
// final userConfig = AppConfig.fromFile('${rootPath}/test/fixtures/test_configs/user_config.json');
|
||||
|
||||
// // Create additional connections
|
||||
// final adminConnection = MumbleConnection(adminConfig.mumble);
|
||||
// final userConnection = MumbleConnection(userConfig.mumble);
|
||||
|
||||
// try {
|
||||
// // Connect all bots
|
||||
// await Future.wait([adminConnection.connect(), userConnection.connect()]);
|
||||
|
||||
// // Verify all connections are established
|
||||
// expect(adminConnection.isConnected, isTrue);
|
||||
// expect(userConnection.isConnected, isTrue);
|
||||
|
||||
// // Test sending messages from different bots
|
||||
// await adminConnection.sendChannelMessage('Admin bot test message');
|
||||
// await userConnection.sendChannelMessage('User bot test message');
|
||||
|
||||
// // Test joining different channels with each bot
|
||||
// await adminConnection.joinChannel('Gaming');
|
||||
// await userConnection.joinChannel('General');
|
||||
|
||||
// expect(adminConnection.currentChannel?.name, equals('Gaming'));
|
||||
// expect(userConnection.currentChannel?.name, equals('General'));
|
||||
// } finally {
|
||||
// // Clean up
|
||||
// await adminConnection.disconnect();
|
||||
// await userConnection.disconnect();
|
||||
// }
|
||||
// });
|
||||
|
||||
test('Should handle reconnection', () async {
|
||||
// Setup test expectations
|
||||
final connectionCompleter = Completer<bool>();
|
||||
final reconnectionCompleter = Completer<bool>();
|
||||
var initiallyConnected = false;
|
||||
|
||||
// Listen for connection state changes
|
||||
connection.connectionState.listen((isConnected) {
|
||||
if (isConnected && !initiallyConnected) {
|
||||
initiallyConnected = true;
|
||||
connectionCompleter.complete(true);
|
||||
} else if (isConnected && initiallyConnected && !reconnectionCompleter.isCompleted) {
|
||||
reconnectionCompleter.complete(true);
|
||||
}
|
||||
});
|
||||
|
||||
// Connect initially
|
||||
await connection.connect();
|
||||
|
||||
// Wait for initial connection
|
||||
expect(
|
||||
await connectionCompleter.future.timeout(Duration(seconds: 10), onTimeout: () => false),
|
||||
isTrue,
|
||||
reason: 'Failed to connect to Mumble server initially',
|
||||
);
|
||||
|
||||
// Restart the Mumble server
|
||||
print('Restarting Mumble server container to test reconnection...');
|
||||
final restartProcess = await Process.run(
|
||||
'docker-compose',
|
||||
['-f', 'test/integration/docker-compose.test.yml', 'restart', 'mumble'],
|
||||
workingDirectory: rootPath
|
||||
);
|
||||
|
||||
if (restartProcess.exitCode != 0) {
|
||||
fail('Failed to restart Mumble server: ${restartProcess.stderr}');
|
||||
}
|
||||
|
||||
// Wait for reconnection
|
||||
await expectLater(
|
||||
reconnectionCompleter.future.timeout(Duration(seconds: 20), onTimeout: () => false),
|
||||
isTrue,
|
||||
reason: 'Failed to reconnect to Mumble server after restart',
|
||||
);
|
||||
|
||||
// Verify connection is re-established
|
||||
expect(connection.isConnected, isTrue);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user