mirror of
https://github.com/trycua/computer.git
synced 2025-12-18 02:54:36 -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