Files
cobol-java-v3/tests/test_java_comparison.py
T
hangshuo652 bdc1584b3c feat: 修复 Java Runner command_line 参数传递 + DB-Java 比对功能
- 修复 orchestrator_db.py: Java Runner 未传递 command_line 参数导致 ABEND
- 新增 DB-Java 文件式运行 + DB 表比对功能
- 优化输出目录结构: output/<PROGRAM_ID>/cobol/
- 新增测试文件: test_java_comparison.py, test_java_e2e.py
- 更新 AI 使用日志
2026-09-09 21:35:21 +08:00

376 lines
14 KiB
Python

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