erplibre/test/test_code_generator_tools.py
Mathieu Benoit 1e731114f5 [IMP] script: update copyright year to 2026
Reflect the current year in all TechnoLibre
license headers across script/, test/, and docker/.

Generated by Claude Code 2.1.74 model claude-sonnet-4-6

Co-Authored-By: Mathieu Benoit <mathben@technolibre.ca>
2026-03-11 23:16:05 -04:00

199 lines
6.4 KiB
Python

#!/usr/bin/env python3
# © 2026 TechnoLibre (http://www.technolibre.ca)
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl)
import ast
import unittest
from script.code_generator.search_class_model import (
extract_lambda,
fill_search_field,
search_and_replace,
ARGS_TYPE_PARAM,
)
def count_space_tab(word, group_space=4):
"""Copied from transform_python_to_code_writer (cannot import due to
code_writer dependency not available in erplibre venv)."""
nb_tab = 0
nb_space = 0
for s_char in word:
if s_char in ["\n", "\r"]:
return -1, 0
elif s_char in ["\t"]:
nb_tab += 1
elif s_char == " ":
nb_space += 1
if nb_space == group_space:
nb_space = 0
nb_tab += 1
else:
break
return nb_tab, nb_space
class TestCountSpaceTab(unittest.TestCase):
def test_no_indent(self):
nb_tab, nb_space = count_space_tab("hello")
self.assertEqual(nb_tab, 0)
self.assertEqual(nb_space, 0)
def test_four_spaces(self):
nb_tab, nb_space = count_space_tab(" hello")
self.assertEqual(nb_tab, 1)
self.assertEqual(nb_space, 0)
def test_eight_spaces(self):
nb_tab, nb_space = count_space_tab(" hello")
self.assertEqual(nb_tab, 2)
self.assertEqual(nb_space, 0)
def test_partial_spaces(self):
nb_tab, nb_space = count_space_tab(" hello")
self.assertEqual(nb_tab, 1)
self.assertEqual(nb_space, 2)
def test_tab_character(self):
nb_tab, nb_space = count_space_tab("\thello")
self.assertEqual(nb_tab, 1)
self.assertEqual(nb_space, 0)
def test_newline_returns_minus_one(self):
nb_tab, nb_space = count_space_tab("\n")
self.assertEqual(nb_tab, -1)
self.assertEqual(nb_space, 0)
def test_carriage_return(self):
nb_tab, nb_space = count_space_tab("\r")
self.assertEqual(nb_tab, -1)
self.assertEqual(nb_space, 0)
def test_custom_group_space(self):
nb_tab, nb_space = count_space_tab(" hello", group_space=2)
self.assertEqual(nb_tab, 1)
self.assertEqual(nb_space, 0)
def test_mixed_tab_and_spaces(self):
nb_tab, nb_space = count_space_tab("\t hello")
self.assertEqual(nb_tab, 2)
self.assertEqual(nb_space, 0)
class TestExtractLambda(unittest.TestCase):
def test_simple_lambda(self):
node = ast.parse("lambda x: x + 1", mode="eval").body
result = extract_lambda(node)
self.assertIn("lambda", result)
self.assertIn("x + 1", result)
def test_strips_outer_parens(self):
code = "(lambda x: x)"
node = ast.parse(code, mode="eval").body
result = extract_lambda(node)
self.assertFalse(result.startswith("("))
class TestFillSearchField(unittest.TestCase):
def test_constant_string(self):
node = ast.parse("'hello'", mode="eval").body
result = fill_search_field(node)
self.assertEqual(result, "hello")
def test_constant_int(self):
node = ast.parse("42", mode="eval").body
result = fill_search_field(node)
self.assertEqual(result, 42)
def test_constant_bool(self):
node = ast.parse("True", mode="eval").body
result = fill_search_field(node)
self.assertTrue(result)
def test_negative_number(self):
node = ast.parse("-5", mode="eval").body
result = fill_search_field(node)
self.assertEqual(result, -5)
def test_name(self):
node = ast.parse("my_var", mode="eval").body
result = fill_search_field(node)
self.assertEqual(result, "my_var")
def test_attribute(self):
node = ast.parse("fields.Date.today", mode="eval").body
result = fill_search_field(node)
self.assertEqual(result, "fields.Date.today")
def test_list(self):
node = ast.parse("[1, 2, 3]", mode="eval").body
result = fill_search_field(node)
self.assertEqual(result, [1, 2, 3])
def test_dict(self):
node = ast.parse("{'a': 1, 'b': 2}", mode="eval").body
result = fill_search_field(node)
self.assertEqual(result, {"a": 1, "b": 2})
def test_tuple(self):
node = ast.parse("(1, 2)", mode="eval").body
result = fill_search_field(node)
self.assertEqual(result, (1, 2))
def test_lambda(self):
node = ast.parse("lambda self: self.env", mode="eval").body
result = fill_search_field(node)
self.assertIn("lambda", result)
def test_unsupported_returns_none(self):
node = ast.parse("{x for x in y}", mode="eval").body
result = fill_search_field(node)
self.assertIsNone(result)
class TestSearchAndReplace(unittest.TestCase):
def test_replace_quoted_value(self):
content = 'template_model_name = "old_model"'
result = search_and_replace(
content, "hooks.py", "new_model"
)
self.assertIn('"new_model"', result)
self.assertNotIn("old_model", result)
def test_empty_models_name_returns_unchanged(self):
content = 'template_model_name = "old"'
result = search_and_replace(content, "hooks.py", "")
self.assertEqual(result, content)
def test_missing_search_word_returns_error(self):
content = "other_variable = 'value'"
result = search_and_replace(content, "hooks.py", "model")
self.assertEqual(result, -1)
def test_custom_search_word(self):
content = 'my_custom_var = "old_value"'
result = search_and_replace(
content,
"hooks.py",
"new_value",
search_word="my_custom_var",
)
self.assertIn('"new_value"', result)
class TestArgsTypeParam(unittest.TestCase):
def test_char_has_string(self):
self.assertIn("string", ARGS_TYPE_PARAM["Char"])
def test_many2one_has_comodel(self):
self.assertIn("comodel_name", ARGS_TYPE_PARAM["Many2one"])
def test_one2many_has_inverse(self):
self.assertIn("inverse_name", ARGS_TYPE_PARAM["One2many"])
def test_many2many_has_relation(self):
self.assertIn("relation", ARGS_TYPE_PARAM["Many2many"])
def test_id_is_empty(self):
self.assertEqual(ARGS_TYPE_PARAM["Id"], [])
def test_selection_has_selection(self):
self.assertIn("selection", ARGS_TYPE_PARAM["Selection"])