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 Function(AgentConfig)? onRun; @override Future 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 ')); 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 ')); 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 ')); }); }