#[cfg(test)]
mod tests {
use crate::core::{ConstructType, ParameterInfo, ReplaceInfo};
use crate::migrate_ruff::migrate_file;
use crate::type_introspection_context::TypeIntrospectionContext;
use crate::types::TypeIntrospectionMethod;
use std::collections::HashMap;
use std::path::Path;
#[test]
fn test_method_migration_with_dmypy() {
let source = r#"
class Calculator:
def add(self, x: int, y: int) -> int:
return x + y # Valid implementation for type analysis
def add_numbers(self, x: int, y: int) -> int:
return x + y # Target method exists
calc = Calculator()
result = calc.add(5, 3)
"#;
let mut replacements = HashMap::new();
replacements.insert(
"test_module.Calculator.add".to_string(),
ReplaceInfo {
old_name: "add".to_string(),
replacement_expr: "{self}.add_numbers({x}, {y})".to_string(),
replacement_ast: None,
construct_type: ConstructType::Function,
parameters: vec![
ParameterInfo::new("self"),
ParameterInfo::new("x"),
ParameterInfo::new("y"),
],
return_type: None,
since: None,
remove_in: None,
message: None,
},
);
let test_ctx = crate::tests::test_utils::TestContext::new(source);
let workspace_root = std::path::Path::new(&test_ctx.file_path)
.parent()
.unwrap()
.to_str()
.unwrap();
let mut type_context = TypeIntrospectionContext::new_with_workspace(
TypeIntrospectionMethod::MypyDaemon,
Some(workspace_root),
)
.unwrap();
let migrated = migrate_file(
source,
"test_module",
Path::new(&test_ctx.file_path),
&mut type_context,
replacements,
HashMap::new(),
)
.unwrap();
println!("dmypy migration result:\n{}", migrated);
assert!(migrated.contains("result = calc.add_numbers(5, 3)"));
assert!(!migrated.contains("result = calc.add(5, 3)"));
type_context.shutdown().unwrap();
}
#[test]
fn test_method_migration_with_pyright_for_comparison() {
let source = r#"
from dissolve import replace_me
class Calculator:
@replace_me()
def add(self, x, y):
return self.add_numbers(x, y)
calc = Calculator()
result = calc.add(5, 3)
"#;
let mut replacements = HashMap::new();
replacements.insert(
"test_module.Calculator.add".to_string(),
ReplaceInfo {
old_name: "add".to_string(),
replacement_expr: "{self}.add_numbers({x}, {y})".to_string(),
replacement_ast: None,
construct_type: ConstructType::Function,
parameters: vec![
ParameterInfo::new("self"),
ParameterInfo::new("x"),
ParameterInfo::new("y"),
],
return_type: None,
since: None,
remove_in: None,
message: None,
},
);
let test_ctx = crate::tests::test_utils::TestContext::new(source);
let mut type_context =
TypeIntrospectionContext::new(TypeIntrospectionMethod::PyrightLsp).unwrap();
let migrated = migrate_file(
source,
"test_module",
Path::new(&test_ctx.file_path),
&mut type_context,
replacements,
HashMap::new(),
)
.unwrap();
println!("pyright migration result:\n{}", migrated);
assert!(migrated.contains("result = calc.add_numbers(5, 3)"));
assert!(!migrated.contains("result = calc.add(5, 3)"));
type_context.shutdown().unwrap();
}
#[test]
fn test_mypy_type_errors_handling() {
use crate::mypy_lsp::MypyTypeIntrospector;
use std::fs;
use tempfile::TempDir;
let temp_dir = TempDir::new().unwrap();
let file_path = temp_dir.path().join("test_type_errors.py");
let source = r#"
def add(a: int, b: int) -> int:
return a + b
# This will cause a type error
result: str = add(1, 2) # Type error: int assigned to str
def get_value() -> str:
return 42 # Type error: returning int instead of str
"#;
fs::write(&file_path, source).unwrap();
let introspector_result =
MypyTypeIntrospector::new(Some(&temp_dir.path().to_string_lossy()));
match introspector_result {
Ok(mut intro) => {
let type_result = intro.get_type_at_position(
&file_path.to_string_lossy(),
3, 4, );
match type_result {
Ok(_) => {} Err(e) => {
assert!(!e.is_empty(), "Should have a proper error message");
}
}
}
Err(e) => {
assert!(
!e.to_string().is_empty(),
"Should have a proper error message: {}",
e
);
}
}
}
}