OTO Server가 runner 등록 상태를 직접 소유하고 Dart runner가 Server registration 계약으로 전환되어야 Control Plane 분리 마이그레이션을 이어갈 수 있다.
197 lines
5.3 KiB
Dart
197 lines
5.3 KiB
Dart
import 'dart:io';
|
|
import 'package:test/test.dart';
|
|
import 'package:oto/cli/cli.dart';
|
|
import 'package:oto/cli/commands/command_agent.dart';
|
|
import 'package:oto/oto/agent/agent_config.dart';
|
|
import 'package:oto/oto/agent/agent_runner.dart';
|
|
|
|
class FakeAgentRunner implements AgentRunner {
|
|
AgentConfig? lastConfig;
|
|
int callCount = 0;
|
|
Future<void> Function(AgentConfig)? onRun;
|
|
|
|
@override
|
|
Future<void> run(AgentConfig config) async {
|
|
lastConfig = config;
|
|
callCount++;
|
|
if (onRun != null) {
|
|
await onRun!(config);
|
|
}
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
const validBootstrapYaml = '''
|
|
agent:
|
|
id: "agent-abc"
|
|
alias: "my-agent"
|
|
enrollment_token: "token-123"
|
|
edge:
|
|
url: "https://edge.toki-labs.com:8443"
|
|
runtime:
|
|
install_dir: "/usr/bin"
|
|
workspace_root: "/var/oto"
|
|
log_dir: "/var/log/oto"
|
|
''';
|
|
|
|
const validServerYaml = '''
|
|
agent:
|
|
id: "agent-abc"
|
|
alias: "my-agent"
|
|
enrollment_token: "token-123"
|
|
server:
|
|
url: "https://oto-core.toki-labs.com:8443"
|
|
runtime:
|
|
install_dir: "/usr/bin"
|
|
workspace_root: "/var/oto"
|
|
log_dir: "/var/log/oto"
|
|
''';
|
|
|
|
late File tempFile;
|
|
|
|
setUp(() {
|
|
tempFile = File('${Directory.systemTemp.path}/agent_config_cli_test.yaml');
|
|
tempFile.writeAsStringSync(validBootstrapYaml);
|
|
CLI.logFunc = (msg) {};
|
|
CLI.initialize('oto', [], []);
|
|
});
|
|
|
|
tearDown(() {
|
|
if (tempFile.existsSync()) {
|
|
tempFile.deleteSync();
|
|
}
|
|
});
|
|
|
|
test('CommandAgent parses run config and calls runner', () async {
|
|
final runner = FakeAgentRunner();
|
|
final command = CommandAgent(runner: runner);
|
|
|
|
await command.execute(['run', '--config', tempFile.path]);
|
|
|
|
expect(runner.callCount, 1);
|
|
expect(runner.lastConfig, isNotNull);
|
|
expect(runner.lastConfig!.agent.id, 'agent-abc');
|
|
expect(runner.lastConfig!.edge.url, 'https://edge.toki-labs.com:8443');
|
|
expect(runner.lastConfig!.runtime.installDir, '/usr/bin');
|
|
});
|
|
|
|
test('CommandAgent parses server config and calls runner', () async {
|
|
tempFile.writeAsStringSync(validServerYaml);
|
|
final runner = FakeAgentRunner();
|
|
final command = CommandAgent(runner: runner);
|
|
|
|
await command.execute(['run', '--config', tempFile.path]);
|
|
|
|
expect(runner.callCount, 1);
|
|
expect(runner.lastConfig, isNotNull);
|
|
expect(
|
|
runner.lastConfig!.server.url,
|
|
'https://oto-core.toki-labs.com:8443',
|
|
);
|
|
});
|
|
|
|
test(
|
|
'CommandAgent parses run config with equals option and calls runner',
|
|
() async {
|
|
final runner = FakeAgentRunner();
|
|
final command = CommandAgent(runner: runner);
|
|
|
|
await command.execute(['run', '--config=${tempFile.path}']);
|
|
|
|
expect(runner.callCount, 1);
|
|
expect(runner.lastConfig!.agent.id, 'agent-abc');
|
|
},
|
|
);
|
|
|
|
test('CommandAgent rejects unknown subcommand', () async {
|
|
final runner = FakeAgentRunner();
|
|
final command = CommandAgent(runner: runner);
|
|
|
|
await command.execute(['unknown_sub']);
|
|
expect(runner.callCount, 0);
|
|
});
|
|
|
|
test('CommandAgent rejects missing config parameter', () async {
|
|
final runner = FakeAgentRunner();
|
|
final command = CommandAgent(runner: runner);
|
|
|
|
await command.execute(['run']);
|
|
expect(runner.callCount, 0);
|
|
});
|
|
|
|
test('CommandAgent sets exit code zero on accepted registration', () async {
|
|
final runner = FakeAgentRunner();
|
|
runner.onRun = (config) async {
|
|
// success
|
|
};
|
|
final command = CommandAgent(runner: runner);
|
|
|
|
final initialExitCode = exitCode;
|
|
exitCode = 0;
|
|
|
|
await command.execute(['run', '--config', tempFile.path]);
|
|
|
|
expect(runner.callCount, 1);
|
|
expect(exitCode, 0);
|
|
|
|
exitCode = initialExitCode;
|
|
});
|
|
|
|
test('CommandAgent sets exit code ten on rejected registration', () async {
|
|
final runner = FakeAgentRunner();
|
|
runner.onRun = (config) async {
|
|
throw RegistrationException('Rejected by server');
|
|
};
|
|
final command = CommandAgent(runner: runner);
|
|
|
|
final initialExitCode = exitCode;
|
|
exitCode = 0;
|
|
|
|
await command.execute(['run', '--config', tempFile.path]);
|
|
|
|
expect(runner.callCount, 1);
|
|
expect(exitCode, 10);
|
|
|
|
exitCode = initialExitCode;
|
|
});
|
|
|
|
// Integration tests calling actual binary
|
|
test('actual bin agent help lists run usage', () async {
|
|
final result = await Process.run('dart', [
|
|
'run',
|
|
'bin/main.dart',
|
|
'agent',
|
|
'-h',
|
|
]);
|
|
|
|
expect(result.exitCode, 0, reason: result.stderr.toString());
|
|
final stdoutStr = result.stdout.toString();
|
|
expect(stdoutStr, contains('agent'));
|
|
expect(stdoutStr, contains('run --config <path>'));
|
|
expect(stdoutStr, contains('Manage the OTO agent'));
|
|
});
|
|
|
|
test('actual bin agent run help works', () async {
|
|
final result = await Process.run('dart', [
|
|
'run',
|
|
'bin/main.dart',
|
|
'agent',
|
|
'run',
|
|
'-h',
|
|
]);
|
|
|
|
expect(result.exitCode, 0, reason: result.stderr.toString());
|
|
final stdoutStr = result.stdout.toString();
|
|
expect(stdoutStr, contains('agent run --config <path>'));
|
|
expect(stdoutStr, contains('Run the OTO agent daemon'));
|
|
});
|
|
|
|
test('actual bin agent with no parameters shows root help', () async {
|
|
final result = await Process.run('dart', ['run', 'bin/main.dart', 'agent']);
|
|
|
|
expect(result.exitCode, 0, reason: result.stderr.toString());
|
|
final stdoutStr = result.stdout.toString();
|
|
expect(stdoutStr, contains('Usage:'));
|
|
expect(stdoutStr, contains('agent run --config <path>'));
|
|
});
|
|
}
|