import pytest from pathlib import Path from unittest.mock import Mock, patch, MagicMock import subprocess import json class TestNativeJavaRunner: """NativeJavaRunner 编译和执行测试""" def test_compile_success(self): """测试Java编译成功""" from runners.native_java_runner import NativeJavaRunner runner = NativeJavaRunner() with patch('subprocess.run') as mock_run: mock_run.return_value = Mock(returncode=0, stdout="", stderr="") result = runner.compile("tests/fixtures/java") assert result.success is True assert result.artifact_path != "" mock_run.assert_called_once() def test_compile_failure(self): """测试Java编译失败""" from runners.native_java_runner import NativeJavaRunner runner = NativeJavaRunner() with patch('subprocess.run') as mock_run: mock_run.return_value = Mock(returncode=1, stdout="", stderr="Compilation error") result = runner.compile("tests/fixtures/java") assert result.success is False assert "Compilation error" in result.log def test_compile_source_not_found(self): """测试编译时源代码目录不存在""" from runners.native_java_runner import NativeJavaRunner runner = NativeJavaRunner() result = runner.compile("nonexistent_dir") assert result.success is False assert "Source directory not found" in result.log def test_compile_pom_not_found(self): """测试编译时pom.xml不存在""" from runners.native_java_runner import NativeJavaRunner runner = NativeJavaRunner() result = runner.compile(".") assert result.success is False assert "pom.xml not found" in result.log def test_run_success(self): """测试Java执行成功""" from runners.native_java_runner import NativeJavaRunner runner = NativeJavaRunner() with patch('subprocess.run') as mock_run: mock_run.return_value = Mock( returncode=0, stdout='{"field1": "value1"}\n{"field2": "value2"}', stderr="" ) # 创建mock文件 mock_artifact = Path("test.jar") mock_input = Path("input.json") mock_artifact.touch() mock_input.touch() try: result = runner.run("test.jar", "input.json", "output") assert result.success is True assert len(result.records) == 2 assert result.records[0]["field1"] == "value1" finally: mock_artifact.unlink(missing_ok=True) mock_input.unlink(missing_ok=True) def test_run_artifact_not_found(self): """测试执行时JAR文件不存在""" from runners.native_java_runner import NativeJavaRunner runner = NativeJavaRunner() result = runner.run("nonexistent.jar", "input.json", "output") assert result.success is False assert "Artifact not found" in result.log def test_run_input_not_found(self): """测试执行时输入文件不存在""" from runners.native_java_runner import NativeJavaRunner runner = NativeJavaRunner() # 先创建artifact文件 mock_artifact = Path("test.jar") mock_artifact.touch() try: result = runner.run("test.jar", "nonexistent.json", "output") assert result.success is False assert "Input file not found" in result.log finally: mock_artifact.unlink(missing_ok=True) def test_run_timeout(self): """测试Java执行超时""" from runners.native_java_runner import NativeJavaRunner runner = NativeJavaRunner() with patch('subprocess.run') as mock_run: mock_run.side_effect = subprocess.TimeoutExpired(cmd="java", timeout=60) mock_artifact = Path("test.jar") mock_input = Path("input.json") mock_artifact.touch() mock_input.touch() try: result = runner.run("test.jar", "input.json", "output") assert result.success is False assert "timed out" in result.log.lower() finally: mock_artifact.unlink(missing_ok=True) mock_input.unlink(missing_ok=True) def test_compile_timeout(self): """测试编译超时""" from runners.native_java_runner import NativeJavaRunner runner = NativeJavaRunner() with patch('subprocess.run') as mock_run: mock_run.side_effect = subprocess.TimeoutExpired(cmd="mvn", timeout=120) result = runner.compile("tests/fixtures/java") assert result.success is False assert "timed out" in result.log.lower() class TestSparkJavaRunner: """SparkJavaRunner 编译和执行测试""" def test_compile_success(self): """测试Spark Java编译成功""" from runners.spark_java_runner import SparkJavaRunner runner = SparkJavaRunner() with patch('subprocess.run') as mock_run: mock_run.return_value = Mock(returncode=0, stdout="", stderr="") result = runner.compile("tests/fixtures/java") assert result.success is True assert result.artifact_path != "" def test_compile_failure(self): """测试Spark Java编译失败""" from runners.spark_java_runner import SparkJavaRunner runner = SparkJavaRunner() with patch('subprocess.run') as mock_run: mock_run.return_value = Mock(returncode=1, stdout="", stderr="Compilation error") result = runner.compile("tests/fixtures/java") assert result.success is False assert "Compilation error" in result.log def test_run_success(self): """测试Spark Java执行成功""" from runners.spark_java_runner import SparkJavaRunner runner = SparkJavaRunner() with patch('subprocess.run') as mock_run: mock_run.return_value = Mock( returncode=0, stdout='{"field1": "value1"}', stderr="" ) mock_artifact = Path("test.jar") mock_input = Path("input.json") mock_artifact.touch() mock_input.touch() try: result = runner.run("test.jar", "input.json", "output") assert result.success is True finally: mock_artifact.unlink(missing_ok=True) mock_input.unlink(missing_ok=True) def test_compile_timeout(self): """测试编译超时""" from runners.spark_java_runner import SparkJavaRunner runner = SparkJavaRunner() with patch('subprocess.run') as mock_run: mock_run.side_effect = subprocess.TimeoutExpired(cmd="mvn", timeout=120) result = runner.compile("tests/fixtures/java") assert result.success is False assert "timed out" in result.log.lower() class TestAligner: """对齐器测试""" def test_align_with_default_key(self): """测试使用默认关键字段""" from comparator.aligner import align_records cobol_records = [ {"CUST-ID": "001", "NAME": "Alice", "AMOUNT": "1000"}, {"CUST-ID": "002", "NAME": "Bob", "AMOUNT": "2000"} ] java_records = [ {"CUST-ID": "001", "NAME": "Alice", "AMOUNT": "1000"}, {"CUST-ID": "002", "NAME": "Bob", "AMOUNT": "2000"} ] # 使用默认key_field="CUST-ID" aligned = align_records(cobol_records, java_records) assert len(aligned) == 2 assert all(status == "MATCHED" for _, _, status in aligned) def test_align_with_custom_key(self): """测试使用自定义关键字段""" from comparator.aligner import align_records cobol_records = [ {"ID": "001", "NAME": "Alice", "AMOUNT": "1000"}, {"ID": "002", "NAME": "Bob", "AMOUNT": "2000"} ] java_records = [ {"ID": "001", "NAME": "Alice", "AMOUNT": "1000"}, {"ID": "002", "NAME": "Bob", "AMOUNT": "2000"} ] # 使用自定义key_field="ID" aligned = align_records(cobol_records, java_records, key_field="ID") assert len(aligned) == 2 assert all(status == "MATCHED" for _, _, status in aligned) def test_align_with_auto_detect(self): """测试自动检测关键字段""" from comparator.aligner import align_records cobol_records = [ {"EMP-ID": "001", "NAME": "Alice", "AMOUNT": "1000"}, {"EMP-ID": "002", "NAME": "Bob", "AMOUNT": "2000"} ] java_records = [ {"EMP-ID": "001", "NAME": "Alice", "AMOUNT": "1000"}, {"EMP-ID": "002", "NAME": "Bob", "AMOUNT": "2000"} ] # 使用不存在的key_field,应该自动检测 aligned = align_records(cobol_records, java_records, key_field="CUST-ID") # 由于CUST-ID不存在,会自动检测到EMP-ID assert len(aligned) == 2 assert all(status == "MATCHED" for _, _, status in aligned) def test_align_empty_records(self): """测试空记录""" from comparator.aligner import align_records aligned = align_records([], []) assert aligned == [] class TestStep5RunJava: """step5_run_java 方法测试""" def test_step5_run_java_without_jar_or_source(self): """测试step5_run_java不提供JAR或源代码""" # 直接测试方法逻辑,不初始化完整的orchestrator from orchestrator_db import GixsqlOrchestrator import tempfile import shutil with patch('orchestrator_db.load_schema') as mock_load: mock_load.return_value = Mock() # 创建临时目录 temp_dir = tempfile.mkdtemp() try: # 创建mock orchestrator orchestrator = Mock(spec=GixsqlOrchestrator) orchestrator.program_id = "TEST" orchestrator.java_input_path = Path(temp_dir) / "test_input.json" orchestrator.java_input_path.write_text("{}") orchestrator.work_dir = Path(temp_dir) orchestrator.runtime_dir = Path(temp_dir) / "output" / "TEST" / "cobol" orchestrator.runtime_dir.mkdir(parents=True, exist_ok=True) # 调用真实方法 result = GixsqlOrchestrator.step5_run_java(orchestrator) # 没有提供JAR或源代码,应该返回失败 assert result.success is False assert "No Java JAR or source" in result.message finally: # 清理 shutil.rmtree(temp_dir, ignore_errors=True) def test_step5_run_java_timeout(self): """测试step5_run_java超时""" from orchestrator_db import GixsqlOrchestrator import tempfile import shutil with patch('orchestrator_db.load_schema') as mock_load, \ patch('orchestrator_db.subprocess.run') as mock_run: mock_load.return_value = Mock() mock_run.side_effect = subprocess.TimeoutExpired(cmd="java", timeout=60) # 创建临时目录 temp_dir = tempfile.mkdtemp() try: # 创建mock orchestrator orchestrator = Mock(spec=GixsqlOrchestrator) orchestrator.program_id = "TEST" orchestrator.java_input_path = Path(temp_dir) / "test_input.json" orchestrator.java_input_path.write_text("{}") orchestrator.work_dir = Path(temp_dir) orchestrator.runtime_dir = Path(temp_dir) / "output" / "TEST" / "cobol" orchestrator.runtime_dir.mkdir(parents=True, exist_ok=True) # 调用真实方法 result = GixsqlOrchestrator.step5_run_java(orchestrator, java_jar="test.jar") assert result.success is False assert "timeout" in result.message.lower() finally: # 清理 shutil.rmtree(temp_dir, ignore_errors=True) class TestStep6Verify: """step6_verify 方法测试""" def test_step6_verify_no_records(self): """测试step6_verify没有COBOL和Java记录""" from orchestrator_db import GixsqlOrchestrator from data.diff_result import VerificationRun with patch('orchestrator_db.load_schema') as mock_load: mock_load.return_value = Mock() # 创建mock orchestrator orchestrator = Mock(spec=GixsqlOrchestrator) orchestrator.program_id = "TEST" orchestrator._current_db_path = None orchestrator.db_path = None orchestrator.java_output_path = None orchestrator.runner = Mock() orchestrator.schema = Mock() orchestrator.schema.db_tables = [] # 调用真实方法 vr = GixsqlOrchestrator.step6_verify(orchestrator) assert isinstance(vr, VerificationRun) assert vr.status == "PASS" assert vr.exit_code == 0