use crate::migrate_ruff::migrate_file;
use crate::{RuffDeprecatedFunctionCollector, TypeIntrospectionMethod};
use std::collections::HashMap;
use std::path::Path;
#[test]
fn test_magic_method_with_no_arguments() {
let source = r#"
from dissolve import replace_me
class MyClass:
@replace_me()
def __str__(self):
return self.display()
# These should not be migrated - wrong number of arguments
result1 = str() # No arguments
result2 = str(1, 2) # Too many arguments
"#;
let collector = RuffDeprecatedFunctionCollector::new("test_module".to_string(), None);
let result = collector.collect_from_source(source.to_string()).unwrap();
let test_ctx = crate::tests::test_utils::TestContext::new(source);
let mut type_context = test_ctx.create_type_context(TypeIntrospectionMethod::PyrightLsp);
let migrated = migrate_file(
source,
"test_module",
Path::new(&test_ctx.file_path),
&mut type_context,
result.replacements,
HashMap::new(),
)
.unwrap();
type_context.shutdown().unwrap();
assert!(migrated.contains("result1 = str()"));
assert!(migrated.contains("result2 = str(1, 2)"));
}
#[test]
fn test_len_builtin_magic_method() {
let source = r#"
from dissolve import replace_me
class MyClass:
@replace_me()
def __len__(self):
return self.size()
obj = MyClass()
# len() should be migrated through __len__
result = len(obj)
"#;
let collector = RuffDeprecatedFunctionCollector::new("test_module".to_string(), None);
let result = collector.collect_from_source(source.to_string()).unwrap();
println!(
"test_len_builtin_magic_method - Collected replacements: {:?}",
result.replacements
);
let test_ctx = crate::tests::test_utils::TestContext::new(source);
let mut type_context = test_ctx.create_type_context(TypeIntrospectionMethod::PyrightLsp);
let migrated = migrate_file(
source,
"test_module",
Path::new(&test_ctx.file_path),
&mut type_context,
result.replacements,
HashMap::new(),
)
.unwrap();
type_context.shutdown().unwrap();
println!(
"test_len_builtin_magic_method - Migrated output:\n{}",
migrated
);
assert!(migrated.contains("result = obj.size()"));
}
#[test]
fn test_magic_method_type_introspection_failure() {
let source = r#"
from dissolve import replace_me
class MyClass:
@replace_me()
def __str__(self):
return self.display()
# Variable with unknown type
unknown_obj = get_unknown_object()
result = str(unknown_obj)
"#;
let collector = RuffDeprecatedFunctionCollector::new("test_module".to_string(), None);
let result = collector.collect_from_source(source.to_string()).unwrap();
let test_ctx = crate::tests::test_utils::TestContext::new(source);
let mut type_context = test_ctx.create_type_context(TypeIntrospectionMethod::PyrightLsp);
let migrated = migrate_file(
source,
"test_module",
Path::new(&test_ctx.file_path),
&mut type_context,
result.replacements,
HashMap::new(),
)
.unwrap();
type_context.shutdown().unwrap();
assert!(migrated.contains("str(unknown_obj)"));
}
#[test]
fn test_magic_method_without_self_prefix_in_replacement() {
let source = r#"
from dissolve import replace_me
class MyClass:
@replace_me()
def __repr__(self):
return format_repr(self)
obj = MyClass()
result = repr(obj)
"#;
let collector = RuffDeprecatedFunctionCollector::new("test_module".to_string(), None);
let result = collector.collect_from_source(source.to_string()).unwrap();
let test_ctx = crate::tests::test_utils::TestContext::new(source);
let mut type_context = test_ctx.create_type_context(TypeIntrospectionMethod::PyrightLsp);
let migrated = migrate_file(
source,
"test_module",
Path::new(&test_ctx.file_path),
&mut type_context,
result.replacements,
HashMap::new(),
)
.unwrap();
type_context.shutdown().unwrap();
let expected = r#"
from dissolve import replace_me
class MyClass:
@replace_me()
def __repr__(self):
return format_repr(self)
obj = MyClass()
result = format_repr(obj)
"#;
assert_eq!(migrated, expected);
}
#[test]
fn test_builtin_wrapper_not_at_start() {
let source = r#"
from dissolve import replace_me
class MyClass:
@replace_me()
def __int__(self):
return max(0, int(self.value))
obj = MyClass()
result = int(obj)
"#;
let collector = RuffDeprecatedFunctionCollector::new("test_module".to_string(), None);
let result = collector.collect_from_source(source.to_string()).unwrap();
for (key, info) in &result.replacements {
println!("Key: {}, Replacement: {}", key, info.replacement_expr);
}
let test_ctx = crate::tests::test_utils::TestContext::new(source);
let mut type_context = test_ctx.create_type_context(TypeIntrospectionMethod::PyrightLsp);
let migrated = migrate_file(
source,
"test_module",
Path::new(&test_ctx.file_path),
&mut type_context,
result.replacements,
HashMap::new(),
)
.unwrap();
type_context.shutdown().unwrap();
println!("Migrated output:\n{}", migrated);
assert!(migrated.contains("result = max(0, int(obj.value))"));
}
#[test]
fn test_magic_method_with_empty_replacement() {
let source = r#"
from dissolve import replace_me
class MyClass:
@replace_me()
def __bool__(self):
return True
obj = MyClass()
result = bool(obj)
"#;
let collector = RuffDeprecatedFunctionCollector::new("test_module".to_string(), None);
let result = collector.collect_from_source(source.to_string()).unwrap();
let test_ctx = crate::tests::test_utils::TestContext::new(source);
let mut type_context = test_ctx.create_type_context(TypeIntrospectionMethod::PyrightLsp);
let migrated = migrate_file(
source,
"test_module",
Path::new(&test_ctx.file_path),
&mut type_context,
result.replacements,
HashMap::new(),
)
.unwrap();
type_context.shutdown().unwrap();
assert!(migrated.contains("result = True"));
}
#[test]
fn test_module_prefix_already_present() {
let source = r#"
from dissolve import replace_me
class MyClass:
@replace_me()
def __hash__(self):
return self.get_id()
# Assuming type introspection returns full module path
obj = MyClass()
result = hash(obj)
"#;
let collector = RuffDeprecatedFunctionCollector::new("test_module".to_string(), None);
let result = collector.collect_from_source(source.to_string()).unwrap();
let test_ctx = crate::tests::test_utils::TestContext::new(source);
let mut type_context = test_ctx.create_type_context(TypeIntrospectionMethod::PyrightLsp);
let migrated = migrate_file(
source,
"test_module",
Path::new(&test_ctx.file_path),
&mut type_context,
result.replacements,
HashMap::new(),
)
.unwrap();
type_context.shutdown().unwrap();
assert!(migrated.contains("result = obj.get_id()"));
}
#[test]
fn test_len_with_complex_expressions() {
let source = r#"
from dissolve import replace_me
class Container:
@replace_me()
def __len__(self):
return self.count()
container1 = Container()
container2 = Container()
# Test len() in various contexts
size1 = len(container1)
size2 = len(container2)
total = len(container1) + len(container2)
"#;
let collector = RuffDeprecatedFunctionCollector::new("test_module".to_string(), None);
let result = collector.collect_from_source(source.to_string()).unwrap();
let test_ctx = crate::tests::test_utils::TestContext::new(source);
let mut type_context = test_ctx.create_type_context(TypeIntrospectionMethod::PyrightLsp);
let migrated = migrate_file(
source,
"test_module",
Path::new(&test_ctx.file_path),
&mut type_context,
result.replacements,
HashMap::new(),
)
.unwrap();
type_context.shutdown().unwrap();
assert!(migrated.contains("size1 = container1.count()"));
assert!(migrated.contains("size2 = container2.count()"));
assert!(migrated.contains("total = container1.count() + container2.count()"));
}