Files
enterprise-addons/ai/tests/test_instance_validation.py
T

327 lines
12 KiB
Python

# Part of Odoo. See LICENSE file for full copyright and licensing details.
import re
from odoo.exceptions import ValidationError
from odoo.tests import TransactionCase
from odoo.addons.ai.utils.tools_schema import validators
class TestInstanceValidation(TransactionCase):
def validate_instance_with_schema(self, schema, instance, required_parameters=None, error_message=''):
if required_parameters is None:
required_parameters = list(schema.keys())
if error_message:
with self.assertRaisesRegex(ValidationError, re.escape(error_message)):
validators.validate_params_llm_values_with_schema(
instance=instance,
schema=schema,
required_parameters=required_parameters,
env=self.env,
)
else:
return validators.validate_params_llm_values_with_schema(
instance=instance,
schema=schema,
required_parameters=required_parameters,
env=self.env,
)
def test_missing_optional_parameter(self):
'''
Assert that when some parameters are not required and are missing from the LLM response, the validation will succeed.
'''
schema = {
'user_name': {
'type': 'string',
'description': 'the name of the user',
},
'phone_number': {
'type': 'string',
'description': 'the phone number of the user',
},
}
instance = {'user_name': 'Mohamed'}
required_parameters = ['user_name']
instance = self.validate_instance_with_schema(
instance=instance,
schema=schema,
required_parameters=required_parameters,
)
# non required param should have been added
self.assertEqual(instance['phone_number'], None)
def test_missing_required_parameter(self):
'''
Assert that when some parameters are required but are missing from the LLM response, the validation will fail.
'''
schema = {
'user_name': {
'type': 'string',
'description': 'the name of the user',
},
'phone_number': {
'type': 'string',
'description': 'the phone number of the user',
},
}
instance = {'phone_number': '+32123456789'}
required_parameters = ['user_name', 'phone_number']
self.validate_instance_with_schema(
instance=instance,
schema=schema,
required_parameters=required_parameters,
error_message="Could you please provide info about 'user_name' as it is required to process your request",
)
def test_wrong_type_parameter(self):
param_name = 'user_age'
param_type = 'number'
schema = {
param_name: {
'type': param_type,
'description': 'the age of the user',
}
}
instance = {param_name: 'fifteen'}
self.validate_instance_with_schema(
schema=schema,
instance=instance,
error_message=f"The type of the parameter '{param_name}' is incorrect. It should be '{param_type}'."
)
def test_wrong_pattern_parameter(self):
'''
Assert that when a parameter doesn't follow the expected pattern, the validation will fail.
'''
param_name = 'meeting_date'
# The pattern validates that the date of the form YYYY-mm-dd
param_pattern = r'^20\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])'
schema = {
param_name: {
'type': 'string',
'description': 'the date of the meeting',
'pattern': param_pattern,
}
}
instance = {param_name: '2025-31-01'}
self.validate_instance_with_schema(
schema=schema,
instance=instance,
error_message=f"The value '{instance[param_name]}' of the parameter '{param_name}' doesn't match the expected pattern '{param_pattern}'."
)
def test_wrong_type_array_item(self):
param_name = 'numbers_to_sum'
schema = {
param_name: {
'type': 'array',
'items': {'type': 'number'},
'description': 'the numbers whose sum is computed',
}
}
instance = {param_name: [15, 20, 'hello']}
self.validate_instance_with_schema(
schema=schema,
instance=instance,
error_message="The type of the parameter 'numbers_to_sum's item' is incorrect. It should be 'number'.",
)
def test_correct_pattern_array_item(self):
'''
Assert that when an array item follows the expected pattern, the validation will succeed.
'''
param_name = 'meeting_date'
# The pattern validates that the date of the form YYYY-mm-dd
param_pattern = r'^20\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])'
schema = {
param_name: {
'type': 'array',
'items': {'type': 'string'},
'description': 'the date of the meeting',
'pattern': param_pattern,
}
}
instance = {param_name: ['2025-01-31', '2024-05-29']}
self.validate_instance_with_schema(
schema=schema,
instance=instance,
)
def test_wrong_pattern_array_item(self):
'''
Assert that when an array item doesn't follow the expected pattern, the validation will fail.
'''
param_name = 'meeting_date'
# The pattern validates that the date of the form YYYY-mm-dd
param_pattern = r'^20\d{2}-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[01])'
schema = {
param_name: {
'type': 'array',
'items': {'type': 'string', 'pattern': param_pattern},
'description': 'the date of the meeting',
}
}
invalid_value = '2025-31-01'
instance = {param_name: ['2025-01-31', invalid_value]}
self.validate_instance_with_schema(
schema=schema,
instance=instance,
error_message=f"The value '{invalid_value}' of the parameter 'meeting_date's item' doesn't match the expected pattern '{param_pattern}'."
)
def test_missing_optional_object_property(self):
'''
Assert that when some properties of an object parameter are not required and are missing from the LLM response, the validation will succeed.
'''
param_name = 'object_param'
schema = {
param_name: {
'type': 'object',
'description': 'user contact details',
'properties': {
'email': {
'type': 'string',
'description': 'the email of the user',
},
'phone': {
'type': 'string',
'description': 'the phone number of the user',
},
},
'required': ['email'],
}
}
instance = {param_name: {'email': 'Mohamed@gmail.com'}}
self.validate_instance_with_schema(
schema=schema,
instance=instance,
)
def test_missing_required_object_property(self):
'''
Assert that when some properties of an object parameter are required but are missing from the LLM response, the validation will fail.
'''
param_name = 'object_param'
schema = {
param_name: {
'type': 'object',
'description': 'user contact details',
'properties': {
'email': {
'type': 'str',
'description': 'the email of the user',
},
'phone': {
'type': 'str',
'description': 'the phone number of the user',
},
},
'required': ['email']
}
}
instance = {param_name: {'phone': '+32123456789'}}
self.validate_instance_with_schema(
schema=schema,
instance=instance,
error_message="Could you please provide info about 'email' as it is required to process your request",
)
def test_wrong_type_object_property(self):
param_name = 'object_param'
property_name = 'email'
property_type = 'string'
schema = {
param_name: {
'type': 'object',
'description': 'user contact details',
'properties': {
property_name: {
'type': property_type,
'description': 'the email of the user',
},
},
'required': [property_name]
}
}
instance = {param_name: {'email': 123}}
self.validate_instance_with_schema(
schema=schema,
instance=instance,
error_message=f"The type of the parameter '{property_name}' is incorrect. It should be '{property_type}'.",
)
def test_wrong_format_object_property(self):
'''
Assert that when an object property doesn't follow the expected pattern, the validation will fail.
'''
param_name = 'object_param'
property_name = 'email'
property_type = 'string'
property_pattern = r'\w+@\w+\.\w+'
property_value = 'saif_genidy@odoo'
schema = {
param_name: {
'type': 'object',
'description': 'user contact details',
'properties': {
property_name: {
'type': property_type,
'description': 'the email of the user',
'pattern': property_pattern,
},
},
'required': [property_name]
}
}
instance = {param_name: {property_name: property_value}}
self.validate_instance_with_schema(
schema=schema,
instance=instance,
error_message=f"The value '{property_value}' of the parameter '{property_name}' doesn't match the expected pattern '{property_pattern}'.",
)
def test_truncate(self):
schema = {
'object_param': {
'type': 'object',
'description': 'object root',
'properties': {
'str_value': {
'type': 'string',
'description': 'str value',
'maxLength': 5,
},
'sub_obj': {
'type': 'object',
'description': 'sub object',
'properties': {
'str_value_2': {
'type': 'string',
'description': 'str value',
'maxLength': 3,
},
},
'required': ['str_value_2'],
},
},
'required': ['str_value'],
},
}
instance = {'object_param': {'str_value': '123456789', 'sub_obj': {'str_value_2': 'abcdefg'}}}
ret = self.validate_instance_with_schema(
schema=schema,
instance=instance,
)
self.assertEqual(ret, {'object_param': {'str_value': '12345...', 'sub_obj': {'str_value_2': 'abc...'}}})
def test_enum(self):
schema = {'state': {'type': 'string', 'description': 'the date of the meeting', 'enum': ['done', 'ready']}}
self.validate_instance_with_schema(schema=schema, instance={'state': 'done'})
self.validate_instance_with_schema(
schema=schema,
instance={'state': 'invalid'},
error_message="Wrong value invalid, should be in: done, ready",
)