use crate::migrate_ruff::migrate_file;
use crate::{RuffDeprecatedFunctionCollector, TypeIntrospectionMethod};
use std::collections::HashMap;
use std::path::Path;
#[test]
fn test_keyword_arg_same_as_param_name() {
let source = r#"
from dissolve import replace_me
@replace_me()
def process(message):
return send(message=message)
result = process("hello")
"#;
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(r#"send(message="hello")"#));
assert!(!migrated.contains(r#"send("hello"="hello")"#));
}
#[test]
fn test_multiple_keyword_args_with_param_names() {
let source = r#"
from dissolve import replace_me
@replace_me()
def configure(name, value, mode):
return setup(name=name, value=value, mode=mode)
result = configure("test", 42, "debug")
"#;
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(r#"setup(name="test", value=42, mode="debug")"#));
}
#[test]
fn test_mixed_keyword_and_positional() {
let source = r#"
from dissolve import replace_me
@replace_me()
def old_api(x, y, z):
return new_api(x, y, mode=z)
result = old_api(1, 2, "fast")
"#;
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(r#"new_api(1, 2, mode="fast")"#));
}
#[test]
fn test_local_class_type_annotation() {
let source = r#"
from dissolve import replace_me
class MyClass:
@replace_me()
def old_method(self):
return self.new_method()
def new_method(self):
return "new"
def process(obj: MyClass):
return obj.old_method()
"#;
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("obj.old_method()"));
assert!(migrated.contains("obj.new_method()"));
}
#[test]
fn test_imported_class_type_annotation() {
let source = r#"
from typing import List
from dissolve import replace_me
class Container:
@replace_me()
def get_items(self):
return self.list_items()
def list_items(self):
return []
def process_container(c: Container) -> List:
return c.get_items()
"#;
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("c.get_items()"));
assert!(migrated.contains("c.list_items()"));
}
#[test]
fn test_simple_context_manager_tracking() {
let source = r#"
from dissolve import replace_me
class Resource:
@replace_me()
def old_close(self):
return self.close()
def close(self):
pass
def __enter__(self):
return self
def __exit__(self, *args):
pass
def open_resource():
return Resource()
with open_resource() as r:
r.old_close()
"#;
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("r.old_close()"));
assert!(migrated.contains("r.close()"));
}
#[test]
fn test_nested_with_statements() {
let source = r#"
from dissolve import replace_me
class FileHandler:
@replace_me()
def old_read(self):
return self.read_data()
def read_data(self):
return "data"
def __enter__(self):
return self
def __exit__(self, *args):
pass
class DBHandler:
@replace_me()
def old_query(self):
return self.execute_query()
def execute_query(self):
return []
def __enter__(self):
return self
def __exit__(self, *args):
pass
def get_file():
return FileHandler()
def get_db():
return DBHandler()
with get_file() as f:
with get_db() as db:
data = f.old_read()
results = db.old_query()
"#;
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("f.old_read()"));
assert!(migrated.contains("f.read_data()"));
assert!(!migrated.contains("db.old_query()"));
assert!(migrated.contains("db.execute_query()"));
}
#[test]
fn test_three_level_inheritance() {
let source = r#"
from dissolve import replace_me
class Base:
@replace_me()
def old_base_method(self):
return self.new_base_method()
def new_base_method(self):
return "base"
class Middle(Base):
pass
class Derived(Middle):
pass
d = Derived()
result = d.old_base_method()
# Test with direct Base instance
b = Base()
result2 = b.old_base_method()
"#;
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("b.old_base_method()"),
"Direct Base instance should be migrated"
);
assert!(
migrated.contains("b.new_base_method()"),
"Direct Base instance should call new_base_method"
);
}
#[test]
fn test_diamond_inheritance() {
let source = r#"
from dissolve import replace_me
class A:
@replace_me()
def old_method(self):
return self.new_method()
def new_method(self):
return "A"
class B(A):
pass
class C(A):
pass
class D(B, C):
pass
d = D()
result = d.old_method()
# Also test with explicit type annotation
d2: D = D()
result2 = d2.old_method()
# And test direct on class A
a = A()
result3 = a.old_method()
"#;
let collector = RuffDeprecatedFunctionCollector::new("test_module".to_string(), None);
let result = collector.collect_from_source(source.to_string()).unwrap();
println!("Collected replacements: {:?}", result.replacements);
println!("Inheritance map: {:?}", result.inheritance_map);
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 diamond inheritance:\n{}", migrated);
if migrated.contains("a.new_method()") {
println!("Direct A instance works - issue is with diamond inheritance type inference");
}
assert!(
!migrated.contains("a.old_method()"),
"Direct A instance should be migrated"
);
assert!(
migrated.contains("a.new_method()"),
"Direct A instance should call new_method"
);
}