OTO Server에서 runner 설치 명령과 bootstrap script를 제공해야 하므로 서버 endpoint, runner proto, bootstrap config naming, smoke 검증을 함께 정리한다.
225 lines
8.8 KiB
Dart
225 lines
8.8 KiB
Dart
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('--server-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('server:'));
|
|
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,
|
|
'--server-url', 'https://server.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<String, String>.from(Platform.environment);
|
|
env['PATH'] = '${fakeBinDir.path}:${env['PATH']}';
|
|
env['HOME'] = tempHome.path;
|
|
|
|
final result = await Process.run('bash', [
|
|
scriptFile.absolute.path,
|
|
'--server-url', 'https://server.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);
|
|
}
|
|
});
|
|
|
|
test('should support --edge-url as alias compatibility', () async {
|
|
final tempDir = await Directory.systemTemp.createTemp('oto_bootstrap_alias_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]);
|
|
|
|
final customConfigPath = '${tempDir.path}/alias-config.yaml';
|
|
|
|
final env = Map<String, String>.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-alias',
|
|
'--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}');
|
|
|
|
final configFile = File(customConfigPath);
|
|
expect(await configFile.exists(), isTrue);
|
|
final configContent = await configFile.readAsString();
|
|
expect(configContent, contains('server:'));
|
|
expect(configContent, contains('url: "https://edge.example.com"'));
|
|
|
|
final expectedPidFile = File('${tempHome.path}/.oto/agent/oto-agent.pid');
|
|
expect(await expectedPidFile.exists(), isTrue);
|
|
final pidStr = await expectedPidFile.readAsString();
|
|
final pid = int.tryParse(pidStr.trim());
|
|
if (pid != null) {
|
|
Process.killPid(pid);
|
|
}
|
|
} finally {
|
|
await tempDir.delete(recursive: true);
|
|
}
|
|
});
|
|
});
|
|
}
|