mirror of
https://github.com/lollipopkit/flutter_server_box.git
synced 2025-12-17 07:14:28 +01:00
feat: import servers from ~/.ssh/config (#873)
This commit is contained in:
355
test/ssh_config_test.dart
Normal file
355
test/ssh_config_test.dart
Normal file
@@ -0,0 +1,355 @@
|
||||
import 'dart:io';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:server_box/core/utils/ssh_config.dart';
|
||||
|
||||
void main() {
|
||||
group('SSHConfig Tests', () {
|
||||
late Directory tempDir;
|
||||
late File configFile;
|
||||
|
||||
setUp(() async {
|
||||
// Create temporary directory for test SSH config files
|
||||
tempDir = await Directory.systemTemp.createTemp('ssh_config_test');
|
||||
configFile = File('${tempDir.path}/config');
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
// Clean up temporary files
|
||||
if (tempDir.existsSync()) {
|
||||
await tempDir.delete(recursive: true);
|
||||
}
|
||||
});
|
||||
|
||||
test('configExists returns false for non-existent file', () async {
|
||||
final (_, exists) = SSHConfig.configExists('/non/existent/path');
|
||||
expect(exists, false);
|
||||
});
|
||||
|
||||
test('configExists returns true for existing file', () async {
|
||||
await configFile.writeAsString('Host example\n HostName example.com\n');
|
||||
final (_, exists) = SSHConfig.configExists(configFile.path);
|
||||
expect(exists, true);
|
||||
});
|
||||
|
||||
test('parseConfig handles empty file', () async {
|
||||
await configFile.writeAsString('');
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, isEmpty);
|
||||
});
|
||||
|
||||
test('parseConfig handles file with only comments', () async {
|
||||
await configFile.writeAsString('''
|
||||
# This is a comment
|
||||
# Another comment
|
||||
''');
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, isEmpty);
|
||||
});
|
||||
|
||||
test('parseConfig parses single host correctly', () async {
|
||||
await configFile.writeAsString('''
|
||||
Host myserver
|
||||
HostName 192.168.1.100
|
||||
User admin
|
||||
Port 2222
|
||||
''');
|
||||
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, hasLength(1));
|
||||
|
||||
final server = servers.first;
|
||||
expect(server.name, 'myserver');
|
||||
expect(server.ip, '192.168.1.100');
|
||||
expect(server.user, 'admin');
|
||||
expect(server.port, 2222);
|
||||
});
|
||||
|
||||
test('parseConfig handles missing HostName', () async {
|
||||
await configFile.writeAsString('''
|
||||
Host myserver
|
||||
User admin
|
||||
Port 2222
|
||||
''');
|
||||
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, isEmpty); // Should skip hosts without HostName
|
||||
});
|
||||
|
||||
test('parseConfig uses defaults for missing optional fields', () async {
|
||||
await configFile.writeAsString('''
|
||||
Host simple
|
||||
HostName example.com
|
||||
''');
|
||||
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, hasLength(1));
|
||||
|
||||
final server = servers.first;
|
||||
expect(server.name, 'simple');
|
||||
expect(server.ip, 'example.com');
|
||||
expect(server.user, 'root'); // default user
|
||||
expect(server.port, 22); // default port
|
||||
});
|
||||
|
||||
test('parseConfig handles multiple hosts', () async {
|
||||
await configFile.writeAsString('''
|
||||
Host server1
|
||||
HostName 192.168.1.100
|
||||
User alice
|
||||
Port 22
|
||||
|
||||
Host server2
|
||||
HostName 192.168.1.200
|
||||
User bob
|
||||
Port 2222
|
||||
|
||||
Host server3
|
||||
HostName example.com
|
||||
User charlie
|
||||
''');
|
||||
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, hasLength(3));
|
||||
|
||||
expect(servers[0].name, 'server1');
|
||||
expect(servers[0].ip, '192.168.1.100');
|
||||
expect(servers[0].user, 'alice');
|
||||
expect(servers[0].port, 22);
|
||||
|
||||
expect(servers[1].name, 'server2');
|
||||
expect(servers[1].ip, '192.168.1.200');
|
||||
expect(servers[1].user, 'bob');
|
||||
expect(servers[1].port, 2222);
|
||||
|
||||
expect(servers[2].name, 'server3');
|
||||
expect(servers[2].ip, 'example.com');
|
||||
expect(servers[2].user, 'charlie');
|
||||
expect(servers[2].port, 22);
|
||||
});
|
||||
|
||||
test('parseConfig handles case insensitive keywords', () async {
|
||||
await configFile.writeAsString('''
|
||||
host myserver
|
||||
hostname 192.168.1.100
|
||||
user admin
|
||||
port 2222
|
||||
''');
|
||||
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, hasLength(1));
|
||||
|
||||
final server = servers.first;
|
||||
expect(server.name, 'myserver');
|
||||
expect(server.ip, '192.168.1.100');
|
||||
expect(server.user, 'admin');
|
||||
expect(server.port, 2222);
|
||||
});
|
||||
|
||||
test('parseConfig handles comments and empty lines', () async {
|
||||
await configFile.writeAsString('''
|
||||
# Global settings
|
||||
Host *
|
||||
ServerAliveInterval 60
|
||||
|
||||
# My development server
|
||||
Host devserver
|
||||
HostName 192.168.1.50
|
||||
User developer # development user
|
||||
Port 22
|
||||
|
||||
# Empty line below
|
||||
|
||||
Host prodserver
|
||||
HostName 10.0.0.100
|
||||
User production
|
||||
''');
|
||||
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, hasLength(2));
|
||||
|
||||
expect(servers[0].name, 'devserver');
|
||||
expect(servers[0].ip, '192.168.1.50');
|
||||
expect(servers[0].user, 'developer');
|
||||
|
||||
expect(servers[1].name, 'prodserver');
|
||||
expect(servers[1].ip, '10.0.0.100');
|
||||
expect(servers[1].user, 'production');
|
||||
});
|
||||
|
||||
test('parseConfig handles wildcard hosts', () async {
|
||||
await configFile.writeAsString('''
|
||||
Host *
|
||||
User defaultuser
|
||||
Port 2222
|
||||
|
||||
Host myserver
|
||||
HostName 192.168.1.100
|
||||
User admin
|
||||
''');
|
||||
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, hasLength(1)); // Only named hosts, not wildcards
|
||||
|
||||
final server = servers.first;
|
||||
expect(server.name, 'myserver');
|
||||
expect(server.ip, '192.168.1.100');
|
||||
expect(server.user, 'admin');
|
||||
expect(server.port, 22); // Uses default, not wildcard setting
|
||||
});
|
||||
|
||||
test('parseConfig handles IdentityFile', () async {
|
||||
await configFile.writeAsString('''
|
||||
Host keyserver
|
||||
HostName 192.168.1.100
|
||||
User admin
|
||||
IdentityFile ~/.ssh/special_key
|
||||
''');
|
||||
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, hasLength(1));
|
||||
|
||||
final server = servers.first;
|
||||
expect(server.keyId, '~/.ssh/special_key');
|
||||
});
|
||||
|
||||
test('parseConfig handles quoted values', () async {
|
||||
await configFile.writeAsString('''
|
||||
Host "server with spaces"
|
||||
HostName "192.168.1.100"
|
||||
User "admin user"
|
||||
IdentityFile "~/.ssh/key with spaces"
|
||||
''');
|
||||
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, hasLength(1));
|
||||
|
||||
final server = servers.first;
|
||||
expect(server.name, 'server with spaces');
|
||||
expect(server.ip, '192.168.1.100');
|
||||
expect(server.user, 'admin user');
|
||||
expect(server.keyId, '~/.ssh/key with spaces');
|
||||
});
|
||||
|
||||
test('parseConfig handles invalid port values', () async {
|
||||
await configFile.writeAsString('''
|
||||
Host badport
|
||||
HostName 192.168.1.100
|
||||
Port notanumber
|
||||
|
||||
Host goodserver
|
||||
HostName 192.168.1.200
|
||||
Port 2222
|
||||
''');
|
||||
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, hasLength(2));
|
||||
|
||||
// First server should use default port due to invalid port
|
||||
expect(servers[0].name, 'badport');
|
||||
expect(servers[0].port, 22); // default port
|
||||
|
||||
// Second server should use specified port
|
||||
expect(servers[1].name, 'goodserver');
|
||||
expect(servers[1].port, 2222);
|
||||
});
|
||||
|
||||
test('parseConfig skips hosts with multiple host patterns', () async {
|
||||
await configFile.writeAsString('''
|
||||
Host server1 server2
|
||||
HostName 192.168.1.100
|
||||
|
||||
Host singleserver
|
||||
HostName 192.168.1.200
|
||||
''');
|
||||
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, hasLength(1)); // Only single host patterns
|
||||
|
||||
expect(servers[0].name, 'singleserver');
|
||||
});
|
||||
|
||||
test('parseConfig handles ProxyJump (ignored)', () async {
|
||||
await configFile.writeAsString('''
|
||||
Host jumpserver
|
||||
HostName 192.168.1.100
|
||||
User admin
|
||||
ProxyJump bastion.example.com
|
||||
''');
|
||||
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, hasLength(1));
|
||||
|
||||
final server = servers.first;
|
||||
expect(server.name, 'jumpserver');
|
||||
expect(server.ip, '192.168.1.100');
|
||||
expect(server.user, 'admin');
|
||||
// ProxyJump is ignored in current implementation
|
||||
});
|
||||
|
||||
test('parseConfig returns empty list for non-existent file', () async {
|
||||
final servers = await SSHConfig.parseConfig('/non/existent/path');
|
||||
expect(servers, isEmpty);
|
||||
});
|
||||
|
||||
test('parseConfig handles real-world SSH config example', () async {
|
||||
await configFile.writeAsString('''
|
||||
# Default settings for all hosts
|
||||
Host *
|
||||
ServerAliveInterval 60
|
||||
ServerAliveCountMax 3
|
||||
TCPKeepAlive yes
|
||||
|
||||
# Production servers
|
||||
Host prod-web-01
|
||||
HostName 10.0.1.100
|
||||
User deploy
|
||||
Port 22
|
||||
IdentityFile ~/.ssh/production.pem
|
||||
|
||||
Host prod-db-01
|
||||
HostName 10.0.1.200
|
||||
User ubuntu
|
||||
Port 2222
|
||||
IdentityFile ~/.ssh/production.pem
|
||||
|
||||
# Development environment
|
||||
Host dev
|
||||
HostName dev.example.com
|
||||
User developer
|
||||
Port 22
|
||||
|
||||
# Jump host configuration
|
||||
Host bastion
|
||||
HostName bastion.example.com
|
||||
User ec2-user
|
||||
IdentityFile ~/.ssh/bastion.pem
|
||||
|
||||
Host internal-server
|
||||
HostName 172.16.0.50
|
||||
User admin
|
||||
ProxyJump bastion
|
||||
''');
|
||||
|
||||
final servers = await SSHConfig.parseConfig(configFile.path);
|
||||
expect(servers, hasLength(5));
|
||||
|
||||
// Check specific servers
|
||||
final prodWeb = servers.firstWhere((s) => s.name == 'prod-web-01');
|
||||
expect(prodWeb.ip, '10.0.1.100');
|
||||
expect(prodWeb.user, 'deploy');
|
||||
expect(prodWeb.port, 22);
|
||||
expect(prodWeb.keyId, '~/.ssh/production.pem');
|
||||
|
||||
final prodDb = servers.firstWhere((s) => s.name == 'prod-db-01');
|
||||
expect(prodDb.ip, '10.0.1.200');
|
||||
expect(prodDb.user, 'ubuntu');
|
||||
expect(prodDb.port, 2222);
|
||||
|
||||
final dev = servers.firstWhere((s) => s.name == 'dev');
|
||||
expect(dev.ip, 'dev.example.com');
|
||||
expect(dev.user, 'developer');
|
||||
expect(dev.port, 22);
|
||||
expect(dev.keyId, isNull);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user