import 'dart:io'; import 'package:test/test.dart'; void main() { final scriptFile = File('assets/script/shell/oto_agent_bootstrap.sh'); group('Linux Bootstrap Script Contract Tests', () { late String content; setUpAll(() async { expect(await scriptFile.exists(), isTrue, reason: 'Bootstrap script file must exist'); content = await scriptFile.readAsString(); }); test('should contain required flags', () { expect(content, contains('--edge-url')); expect(content, contains('--agent-id')); expect(content, contains('--enrollment-token')); expect(content, contains('--release-base-url')); }); test('should contain optional/default flags and paths', () { expect(content, contains('--agent-alias')); expect(content, contains('--install-dir')); expect(content, contains('--config-path')); expect(content, contains('--workspace-root')); expect(content, contains('--log-dir')); expect(content, contains('--no-background')); expect(content, contains('.oto/bin')); expect(content, contains('.oto/agent/config.yaml')); }); test('should support correct architecture assets', () { expect(content, contains('oto-linux-x64.tar.gz')); expect(content, contains('oto-linux-arm64.tar.gz')); }); test('should write expected keys to configuration yaml', () { expect(content, contains('agent:')); expect(content, contains('id:')); expect(content, contains('alias:')); expect(content, contains('enrollment_token:')); expect(content, contains('edge:')); expect(content, contains('url:')); expect(content, contains('runtime:')); expect(content, contains('install_dir:')); expect(content, contains('workspace_root:')); expect(content, contains('log_dir:')); }); test('should contain agent runtime commands and background behavior', () { expect(content, contains('agent run')); expect(content, contains('--config')); expect(content, contains('nohup')); expect(content, contains('oto-agent.pid')); }); }); group('Linux Bootstrap Script Functional & Regressions Tests', () { test('should fail when release base URL is not HTTPS', () async { final result = await Process.run('bash', [ scriptFile.absolute.path, '--edge-url', 'https://edge.example.com', '--agent-id', 'test-agent', '--enrollment-token', 'secret-token-123', '--release-base-url', 'http://example.com/release', // HTTP URL ]); expect(result.exitCode, isNot(0)); expect(result.stderr.toString(), contains('--release-base-url must use https://')); expect(result.stdout.toString(), isNot(contains('Downloading OTO agent'))); // enrollment token이 출력에 노출되지 않는지 확인 expect(result.stdout.toString(), isNot(contains('secret-token-123'))); expect(result.stderr.toString(), isNot(contains('secret-token-123'))); }); test('should write PID to standard agent state path when custom config-path is specified', () async { final tempDir = await Directory.systemTemp.createTemp('oto_bootstrap_test_'); try { final tempHome = Directory('${tempDir.path}/home'); await tempHome.create(recursive: true); // 1. 가짜 OTO 실행 파일 생성 final fakeOtoSource = Directory('${tempDir.path}/fake_oto_src'); await fakeOtoSource.create(recursive: true); final fakeOtoFile = File('${fakeOtoSource.path}/oto'); await fakeOtoFile.writeAsString('#!/bin/sh\nwhile true; do sleep 1; done\n'); await Process.run('chmod', ['+x', fakeOtoFile.path]); // 2. 가짜 tar.gz 아카이브 생성 final fakeTarGz = File('${tempDir.path}/oto-linux-x64.tar.gz'); await Process.run('tar', ['-czf', fakeTarGz.path, '-C', fakeOtoSource.path, 'oto']); // 3. 가짜 curl 스크립트 생성 final fakeBinDir = Directory('${tempDir.path}/bin'); await fakeBinDir.create(recursive: true); final fakeCurl = File('${fakeBinDir.path}/curl'); await fakeCurl.writeAsString('''#!/bin/sh out_file="" while [ \$# -gt 0 ]; do if [ "\$1" = "-o" ]; then out_file="\$2" break fi shift done if [ -n "\$out_file" ]; then cp "${fakeTarGz.path}" "\$out_file" fi '''); await Process.run('chmod', ['+x', fakeCurl.path]); // 4. custom config path 지정 final customConfigDir = Directory('${tempDir.path}/custom_config'); await customConfigDir.create(recursive: true); final customConfigPath = '${customConfigDir.path}/my-config.yaml'; // PATH 환경변수에 fakeBinDir 등록, HOME을 tempHome으로 설정 final env = Map.from(Platform.environment); env['PATH'] = '${fakeBinDir.path}:${env['PATH']}'; env['HOME'] = tempHome.path; final result = await Process.run('bash', [ scriptFile.absolute.path, '--edge-url', 'https://edge.example.com', '--agent-id', 'test-agent', '--enrollment-token', 'secret-token-123', '--release-base-url', 'https://example.com/release', '--config-path', customConfigPath, ], environment: env); expect(result.exitCode, 0, reason: 'Script failed with: ${result.stderr}'); // 5. 검증 final configFile = File(customConfigPath); expect(await configFile.exists(), isTrue); final expectedPidFile = File('${tempHome.path}/.oto/agent/oto-agent.pid'); expect(await expectedPidFile.exists(), isTrue, reason: 'PID file should exist in standard agent state path'); final unexpectedPidFile = File('${customConfigDir.path}/oto-agent.pid'); expect(await unexpectedPidFile.exists(), isFalse, reason: 'PID file should not exist in custom config path directory'); final pidStr = await expectedPidFile.readAsString(); final pid = int.tryParse(pidStr.trim()); if (pid != null) { Process.killPid(pid); } } finally { await tempDir.delete(recursive: true); } }); }); }