mirror of
https://github.com/trycua/lume.git
synced 2025-12-21 12:20:01 -06:00
test(helpers): add comprehensive teists for dependency collection
- Test closure variable detection (helpers, constants, imports) - Test nested dependency resolution and ordering - Test class definitions and decorator removal - Test import style preservation - Test caching mechanism
This commit is contained in:
478
libs/python/computer/tests/test_helpers.py
Normal file
478
libs/python/computer/tests/test_helpers.py
Normal file
@@ -0,0 +1,478 @@
|
|||||||
|
from computer.helpers import generate_source_code
|
||||||
|
|
||||||
|
|
||||||
|
class TestSimpleCases:
|
||||||
|
"""Test simple dependency cases"""
|
||||||
|
|
||||||
|
def test_simple_function_no_dependencies(self):
|
||||||
|
"""Test simple function with no dependencies"""
|
||||||
|
|
||||||
|
def simple_func():
|
||||||
|
return 42
|
||||||
|
|
||||||
|
code = generate_source_code(simple_func)
|
||||||
|
|
||||||
|
assert "def simple_func():" in code
|
||||||
|
assert "return 42" in code
|
||||||
|
|
||||||
|
def test_function_with_parameters(self):
|
||||||
|
"""Test function with parameters"""
|
||||||
|
|
||||||
|
def add(a, b):
|
||||||
|
return a + b
|
||||||
|
|
||||||
|
code = generate_source_code(add)
|
||||||
|
|
||||||
|
assert "def add(a, b):" in code
|
||||||
|
assert "return a + b" in code
|
||||||
|
|
||||||
|
def test_function_with_docstring(self):
|
||||||
|
"""Test function with docstring"""
|
||||||
|
|
||||||
|
def documented():
|
||||||
|
"""This is a docstring"""
|
||||||
|
return True
|
||||||
|
|
||||||
|
code = generate_source_code(documented)
|
||||||
|
|
||||||
|
assert "def documented():" in code
|
||||||
|
assert "This is a docstring" in code
|
||||||
|
|
||||||
|
|
||||||
|
class TestImports:
|
||||||
|
"""Test import handling"""
|
||||||
|
|
||||||
|
def test_stdlib_import_inside_function(self):
|
||||||
|
"""Test function with stdlib import inside"""
|
||||||
|
|
||||||
|
def with_stdlib():
|
||||||
|
import math
|
||||||
|
|
||||||
|
return math.sqrt(16)
|
||||||
|
|
||||||
|
code = generate_source_code(with_stdlib)
|
||||||
|
|
||||||
|
assert "import math" in code
|
||||||
|
assert "def with_stdlib():" in code
|
||||||
|
assert "math.sqrt(16)" in code
|
||||||
|
|
||||||
|
def test_multiple_stdlib_imports(self):
|
||||||
|
"""Test function with multiple stdlib imports"""
|
||||||
|
|
||||||
|
def with_multiple():
|
||||||
|
import json
|
||||||
|
import math
|
||||||
|
|
||||||
|
return json.dumps({"value": math.pi})
|
||||||
|
|
||||||
|
code = generate_source_code(with_multiple)
|
||||||
|
|
||||||
|
assert "import json" in code
|
||||||
|
assert "import math" in code
|
||||||
|
|
||||||
|
def test_from_import_stdlib(self):
|
||||||
|
"""Test from X import Y style"""
|
||||||
|
|
||||||
|
def with_from_import():
|
||||||
|
from math import pi, sqrt
|
||||||
|
|
||||||
|
return sqrt(16) + pi
|
||||||
|
|
||||||
|
code = generate_source_code(with_from_import)
|
||||||
|
|
||||||
|
assert "from math import" in code
|
||||||
|
assert "sqrt, pi" in code or ("sqrt" in code and "pi" in code)
|
||||||
|
|
||||||
|
def test_third_party_global_import(self):
|
||||||
|
"""Test globally imported third-party module"""
|
||||||
|
from requests import get
|
||||||
|
|
||||||
|
def with_requests():
|
||||||
|
return get
|
||||||
|
|
||||||
|
code = generate_source_code(with_requests)
|
||||||
|
|
||||||
|
assert "from requests import get" in code
|
||||||
|
assert "def with_requests():" in code
|
||||||
|
|
||||||
|
|
||||||
|
class TestHelperFunctions:
|
||||||
|
"""Test helper function dependencies"""
|
||||||
|
|
||||||
|
def test_single_helper_function(self):
|
||||||
|
"""Test function with one helper"""
|
||||||
|
|
||||||
|
def helper(x):
|
||||||
|
return x * 2
|
||||||
|
|
||||||
|
def main():
|
||||||
|
return helper(21)
|
||||||
|
|
||||||
|
code = generate_source_code(main)
|
||||||
|
|
||||||
|
assert "def helper(x):" in code
|
||||||
|
assert "return x * 2" in code
|
||||||
|
assert "def main():" in code
|
||||||
|
assert "return helper(21)" in code
|
||||||
|
|
||||||
|
def test_nested_helper_functions(self):
|
||||||
|
"""Test function with nested helpers"""
|
||||||
|
|
||||||
|
def helper_a(x):
|
||||||
|
return x + 1
|
||||||
|
|
||||||
|
def helper_b(x):
|
||||||
|
return helper_a(x) * 2
|
||||||
|
|
||||||
|
def main():
|
||||||
|
return helper_b(10)
|
||||||
|
|
||||||
|
code = generate_source_code(main)
|
||||||
|
|
||||||
|
assert "def helper_a(x):" in code
|
||||||
|
assert "def helper_b(x):" in code
|
||||||
|
assert "def main():" in code
|
||||||
|
|
||||||
|
def test_helper_dependency_ordering(self):
|
||||||
|
"""Test that helpers are ordered correctly (dependencies first)"""
|
||||||
|
|
||||||
|
def level_3(x):
|
||||||
|
return x + 1
|
||||||
|
|
||||||
|
def level_2(x):
|
||||||
|
return level_3(x) * 2
|
||||||
|
|
||||||
|
def level_1(x):
|
||||||
|
return level_2(x) + 5
|
||||||
|
|
||||||
|
code = generate_source_code(level_1)
|
||||||
|
|
||||||
|
# Check ordering
|
||||||
|
pos_3 = code.find("def level_3")
|
||||||
|
pos_2 = code.find("def level_2")
|
||||||
|
pos_1 = code.find("def level_1")
|
||||||
|
|
||||||
|
assert pos_3 < pos_2 < pos_1, "Functions not in correct dependency order"
|
||||||
|
|
||||||
|
def test_helper_with_import(self):
|
||||||
|
"""Test helper function that uses imports"""
|
||||||
|
import math
|
||||||
|
|
||||||
|
def helper(x):
|
||||||
|
return math.sqrt(x)
|
||||||
|
|
||||||
|
def main():
|
||||||
|
return helper(16)
|
||||||
|
|
||||||
|
code = generate_source_code(main)
|
||||||
|
|
||||||
|
assert "import math" in code
|
||||||
|
assert "def helper(x):" in code
|
||||||
|
assert "def main():" in code
|
||||||
|
|
||||||
|
|
||||||
|
class TestGlobalConstants:
|
||||||
|
"""Test global constant handling"""
|
||||||
|
|
||||||
|
def test_simple_constant(self):
|
||||||
|
"""Test function using simple constant"""
|
||||||
|
MY_CONSTANT = 42
|
||||||
|
|
||||||
|
def with_constant():
|
||||||
|
return MY_CONSTANT * 2
|
||||||
|
|
||||||
|
code = generate_source_code(with_constant)
|
||||||
|
|
||||||
|
assert "MY_CONSTANT = 42" in code
|
||||||
|
assert "def with_constant():" in code
|
||||||
|
|
||||||
|
def test_string_constant(self):
|
||||||
|
"""Test function using string constant"""
|
||||||
|
API_URL = "https://api.example.com"
|
||||||
|
|
||||||
|
def with_string():
|
||||||
|
return API_URL
|
||||||
|
|
||||||
|
code = generate_source_code(with_string)
|
||||||
|
|
||||||
|
assert "API_URL" in code
|
||||||
|
assert "https://api.example.com" in code
|
||||||
|
|
||||||
|
def test_multiple_constants(self):
|
||||||
|
"""Test function using multiple constants"""
|
||||||
|
BASE_URL = "https://example.com"
|
||||||
|
TIMEOUT = 30
|
||||||
|
|
||||||
|
def with_multiple():
|
||||||
|
return f"{BASE_URL}?timeout={TIMEOUT}"
|
||||||
|
|
||||||
|
code = generate_source_code(with_multiple)
|
||||||
|
|
||||||
|
assert "BASE_URL" in code
|
||||||
|
assert "TIMEOUT" in code
|
||||||
|
|
||||||
|
|
||||||
|
class TestClassDefinitions:
|
||||||
|
"""Test class definition handling"""
|
||||||
|
|
||||||
|
def test_simple_class(self):
|
||||||
|
"""Test function using simple class"""
|
||||||
|
|
||||||
|
class Calculator:
|
||||||
|
def __init__(self, value):
|
||||||
|
self.value = value
|
||||||
|
|
||||||
|
def add(self, x):
|
||||||
|
return self.value + x
|
||||||
|
|
||||||
|
def with_class():
|
||||||
|
calc = Calculator(10)
|
||||||
|
return calc.add(5)
|
||||||
|
|
||||||
|
code = generate_source_code(with_class)
|
||||||
|
|
||||||
|
assert "class Calculator:" in code
|
||||||
|
assert "__init__" in code
|
||||||
|
assert "def add" in code
|
||||||
|
assert "def with_class():" in code
|
||||||
|
|
||||||
|
def test_class_with_methods(self):
|
||||||
|
"""Test class with multiple methods"""
|
||||||
|
|
||||||
|
class Math:
|
||||||
|
def add(self, a, b):
|
||||||
|
return a + b
|
||||||
|
|
||||||
|
def multiply(self, a, b):
|
||||||
|
return a * b
|
||||||
|
|
||||||
|
def use_math():
|
||||||
|
m = Math()
|
||||||
|
return m.add(2, 3) + m.multiply(4, 5)
|
||||||
|
|
||||||
|
code = generate_source_code(use_math)
|
||||||
|
|
||||||
|
assert "class Math:" in code
|
||||||
|
assert "def add" in code
|
||||||
|
assert "def multiply" in code
|
||||||
|
|
||||||
|
|
||||||
|
class TestDecoratorRemoval:
|
||||||
|
"""Test decorator removal"""
|
||||||
|
|
||||||
|
def test_simple_decorator_removed(self):
|
||||||
|
"""Test that decorators are removed"""
|
||||||
|
|
||||||
|
def my_decorator(f):
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
return f(*args, **kwargs)
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
|
||||||
|
@my_decorator
|
||||||
|
def decorated():
|
||||||
|
return 42
|
||||||
|
|
||||||
|
code = generate_source_code(decorated)
|
||||||
|
|
||||||
|
assert "@my_decorator" not in code
|
||||||
|
assert "def decorated():" in code
|
||||||
|
assert "return 42" in code
|
||||||
|
|
||||||
|
def test_multiple_decorators_removed(self):
|
||||||
|
"""Test that multiple decorators are removed"""
|
||||||
|
|
||||||
|
def decorator1(f):
|
||||||
|
return f
|
||||||
|
|
||||||
|
def decorator2(f):
|
||||||
|
return f
|
||||||
|
|
||||||
|
@decorator1
|
||||||
|
@decorator2
|
||||||
|
def multi_decorated():
|
||||||
|
return True
|
||||||
|
|
||||||
|
code = generate_source_code(multi_decorated)
|
||||||
|
|
||||||
|
assert "@decorator1" not in code
|
||||||
|
assert "@decorator2" not in code
|
||||||
|
assert "def multi_decorated():" in code
|
||||||
|
|
||||||
|
|
||||||
|
class TestComplexScenarios:
|
||||||
|
"""Test complex real-world scenarios"""
|
||||||
|
|
||||||
|
def test_api_client_pattern(self):
|
||||||
|
"""Test typical API client pattern"""
|
||||||
|
import json
|
||||||
|
|
||||||
|
BASE_URL = "https://api.example.com"
|
||||||
|
|
||||||
|
def make_request(endpoint):
|
||||||
|
return f"{BASE_URL}/{endpoint}"
|
||||||
|
|
||||||
|
def parse_response(response):
|
||||||
|
return json.loads(response)
|
||||||
|
|
||||||
|
def get_user(user_id):
|
||||||
|
url = make_request(f"users/{user_id}")
|
||||||
|
response = f'{{"id": {user_id}}}'
|
||||||
|
return parse_response(response)
|
||||||
|
|
||||||
|
code = generate_source_code(get_user)
|
||||||
|
|
||||||
|
assert "import json" in code
|
||||||
|
assert "BASE_URL" in code
|
||||||
|
assert "def make_request" in code
|
||||||
|
assert "def parse_response" in code
|
||||||
|
assert "def get_user" in code
|
||||||
|
|
||||||
|
def test_data_processing_pipeline(self):
|
||||||
|
"""Test data processing pipeline"""
|
||||||
|
|
||||||
|
def validate_data(data):
|
||||||
|
return [x for x in data if x > 0]
|
||||||
|
|
||||||
|
def transform_data(data):
|
||||||
|
return [x * 2 for x in data]
|
||||||
|
|
||||||
|
def process_pipeline(raw_data):
|
||||||
|
valid = validate_data(raw_data)
|
||||||
|
return transform_data(valid)
|
||||||
|
|
||||||
|
code = generate_source_code(process_pipeline)
|
||||||
|
|
||||||
|
assert "def validate_data" in code
|
||||||
|
assert "def transform_data" in code
|
||||||
|
assert "def process_pipeline" in code
|
||||||
|
|
||||||
|
# Check ordering
|
||||||
|
validate_pos = code.find("def validate_data")
|
||||||
|
transform_pos = code.find("def transform_data")
|
||||||
|
pipeline_pos = code.find("def process_pipeline")
|
||||||
|
|
||||||
|
assert validate_pos < pipeline_pos
|
||||||
|
assert transform_pos < pipeline_pos
|
||||||
|
|
||||||
|
|
||||||
|
class TestEdgeCases:
|
||||||
|
"""Test edge cases"""
|
||||||
|
|
||||||
|
def test_function_with_default_args(self):
|
||||||
|
"""Test function with default arguments"""
|
||||||
|
|
||||||
|
def with_defaults(a, b=10, c=20):
|
||||||
|
return a + b + c
|
||||||
|
|
||||||
|
code = generate_source_code(with_defaults)
|
||||||
|
|
||||||
|
assert "def with_defaults(a, b=10, c=20):" in code
|
||||||
|
|
||||||
|
def test_function_with_kwargs(self):
|
||||||
|
"""Test function with *args and **kwargs"""
|
||||||
|
|
||||||
|
def with_varargs(*args, **kwargs):
|
||||||
|
return sum(args)
|
||||||
|
|
||||||
|
code = generate_source_code(with_varargs)
|
||||||
|
|
||||||
|
assert "def with_varargs(*args, **kwargs):" in code
|
||||||
|
|
||||||
|
def test_async_function(self):
|
||||||
|
"""Test async function"""
|
||||||
|
|
||||||
|
async def async_func():
|
||||||
|
return 42
|
||||||
|
|
||||||
|
code = generate_source_code(async_func)
|
||||||
|
|
||||||
|
assert "async def async_func():" in code
|
||||||
|
assert "return 42" in code
|
||||||
|
|
||||||
|
def test_lambda_works_in_files(self):
|
||||||
|
"""Test that lambda functions work when defined in files"""
|
||||||
|
lambda_func = lambda x: x * 2
|
||||||
|
|
||||||
|
# Lambda functions defined in files CAN have their source extracted
|
||||||
|
code = generate_source_code(lambda_func)
|
||||||
|
|
||||||
|
# Should include the lambda
|
||||||
|
assert "lambda" in code.lower() or "lambda_func" in code
|
||||||
|
|
||||||
|
|
||||||
|
class TestCaching:
|
||||||
|
"""Test caching mechanism"""
|
||||||
|
|
||||||
|
def test_cache_returns_same_result(self):
|
||||||
|
"""Test that cache returns identical results"""
|
||||||
|
|
||||||
|
def cached_func():
|
||||||
|
return 42
|
||||||
|
|
||||||
|
code1 = generate_source_code(cached_func)
|
||||||
|
code2 = generate_source_code(cached_func)
|
||||||
|
|
||||||
|
assert code1 == code2
|
||||||
|
|
||||||
|
def test_different_functions_different_cache(self):
|
||||||
|
"""Test that different functions have different cache entries"""
|
||||||
|
|
||||||
|
def func1():
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def func2():
|
||||||
|
return 2
|
||||||
|
|
||||||
|
code1 = generate_source_code(func1)
|
||||||
|
code2 = generate_source_code(func2)
|
||||||
|
|
||||||
|
assert code1 != code2
|
||||||
|
assert "return 1" in code1
|
||||||
|
assert "return 2" in code2
|
||||||
|
|
||||||
|
|
||||||
|
class TestBuiltinHandling:
|
||||||
|
"""Test that builtins are handled correctly"""
|
||||||
|
|
||||||
|
def test_builtin_not_included(self):
|
||||||
|
"""Test that builtin functions are not included as dependencies"""
|
||||||
|
|
||||||
|
def uses_builtins():
|
||||||
|
data = list(range(10))
|
||||||
|
return len(data)
|
||||||
|
|
||||||
|
code = generate_source_code(uses_builtins)
|
||||||
|
|
||||||
|
# Should not include definitions for list, range, len
|
||||||
|
assert "def list" not in code
|
||||||
|
assert "def range" not in code
|
||||||
|
assert "def len" not in code
|
||||||
|
assert "def uses_builtins():" in code
|
||||||
|
|
||||||
|
|
||||||
|
class TestImportStylePreservation:
|
||||||
|
"""Test that import styles are preserved"""
|
||||||
|
|
||||||
|
def test_from_import_preserved(self):
|
||||||
|
"""Test from X import Y is preserved"""
|
||||||
|
from math import sqrt
|
||||||
|
|
||||||
|
def use_sqrt():
|
||||||
|
return sqrt(16)
|
||||||
|
|
||||||
|
code = generate_source_code(use_sqrt)
|
||||||
|
|
||||||
|
assert "from math import sqrt" in code
|
||||||
|
|
||||||
|
def test_import_as_preserved(self):
|
||||||
|
"""Test import X as Y is preserved"""
|
||||||
|
import json as j
|
||||||
|
|
||||||
|
def use_json():
|
||||||
|
return j.dumps({"key": "value"})
|
||||||
|
|
||||||
|
code = generate_source_code(use_json)
|
||||||
|
|
||||||
|
# The import style should be preserved
|
||||||
|
assert "import json" in code
|
||||||
Reference in New Issue
Block a user