1
0
Fork 0
mirror of https://github.com/yweber/lodel2.git synced 2026-08-06 04:18:37 +02:00

Add test for LeObject with a mock on the datasource + bugfix on _LeObject and LeType

This commit is contained in:
Yann 2015-10-23 12:37:26 +02:00
commit 965560bf6b
4 changed files with 149 additions and 26 deletions

View file

@ -38,7 +38,7 @@ class DummyDatasource(object):
# @param relational_filters list : relationnal filters list (see @ref leobject_filters )
# @return okay bool: True on success, it will raise on failure
def delete(self, letype, leclass, filters, relational_filters):
print("DummyDatasource.delete: ", lodel_id)
print("DummyDatasource.delete: ", filters)
return True
## @brief search for a collection of objects

View file

@ -62,7 +62,6 @@ class _LeObject(object):
for data in datas:
letype.check_datas_or_raise(data, complete = True)
return cls._datasource.insert(letype, leclass, datas)
## @brief Delete LeObjects given filters
@ -72,34 +71,35 @@ class _LeObject(object):
# @param filters list : list of filters (see @ref leobject_filters)
# @return bool
@classmethod
def delete(cls, letype, leclass, filters):
filters,relationnal_filters = leobject.leobject._LeObject._prepare_filters(filters, cls, cls._leclass)
letype, leclass = cls._prepare_targets(letype, leclass)
return cls._datasource(letype, leclass, filters, relationnal_filters)
def delete(cls, letype, filters):
letype, leclass = cls._prepare_targets(letype)
filters,relationnal_filters = leobject.leobject._LeObject._prepare_filters(filters, letype, leclass)
return cls._datasource.delete(letype, leclass, filters, relationnal_filters)
## @brief Update LeObjects given filters and datas
# @param cls
# @param letype LeType|str : LeType child class or name
# @param leclass LeClass|str : LeClass child class or name
# @param filters list : list of filters (see @ref leobject_filters)
@classmethod
def update(cls, letype, leclass, filters, datas):
filters,relationnal_filters = leobject.leobject._LeObject._prepare_filters(filters, cls, cls._leclass)
letype, leclass = cls._prepare_targets(letype, leclass)
def update(cls, letype, filters, datas):
letype, leclass = cls._prepare_targets(letype)
filters,relationnal_filters = leobject.leobject._LeObject._prepare_filters(filters, letype, leclass)
if letype is None:
raise ValueError("Argument letype cannot be None")
letype.check_datas_or_raise(datas, False)
return cls._datasource(letype, leclass, filters, relationnal_filters, datas)
return cls._datasource.update(letype, leclass, filters, relationnal_filters, datas)
## @brief make a search to retrieve a collection of LeObject
# @param query_filters list : list of string of query filters (or tuple (FIELD, OPERATOR, VALUE) ) see @ref leobject_filters
# @param field_list list|None : list of string representing fields see @ref leobject_filters
# @param typename str : The name of the LeType we want
# @param classname str : The name of the LeClass we want
# @param cls
# @return responses ({string:*}): a list of dict with field:value
def get(self, query_filters, field_list = None, typename = None, classname = None):
@classmethod
def get(cls, query_filters, field_list = None, typename = None, classname = None):
letype,leclass = self._prepare_targets(typename, classname)
letype,leclass = cls._prepare_targets(typename, classname)
#Fetching LeType
if typename is None:
@ -107,21 +107,21 @@ class _LeObject(object):
field_list.append('type_id')
#Checking field_list
if field_list is None:
if field_list is None or len(field_list) == 0:
#default field_list
if not (letype is None):
flist = letype._fields
field_list = letype._fields
elif not (leclass is None):
flist = leclass._fieldtypes.keys()
field_list = leclass._fieldtypes.keys()
else:
flist = EditorialModel.classtype.common_fields.keys()
else:
LeFactory._check_fields(letype, leclass, field_list)
field_list = EditorialModel.classtype.common_fields.keys()
field_list = cls._prepare_field_list(field_list, letype, leclass)
#preparing filters
filters, relationnal_filters = self._prepare_filters(query_filters, letype, leclass)
filters, relationnal_filters = cls._prepare_filters(query_filters, letype, leclass)
#Fetching datas from datasource
datas = self._datasource.get(emclass, emtype, field_list, filters, relational_filters)
datas = cls._datasource.get(leclass, letype, field_list, filters, relationnal_filters)
#Instanciating corresponding LeType child classes with datas
result = list()
@ -131,6 +131,14 @@ class _LeObject(object):
return result
@classmethod
def _prepare_field_list(cls, field_list, letype, leclass):
cls._check_fields(letype, leclass, [f for f in field_list if not cls._field_is_relational(f)])
for i, field in enumerate(field_list):
if cls._field_is_relational(field):
field_list[i] = cls._prepare_relational_field(field)
return field_list
## @brief Preparing letype and leclass arguments
#
# This function will do multiple things :
@ -194,7 +202,7 @@ class _LeObject(object):
#Checks that fields are in this type
for field in fields:
if field not in field_l:
raise LeObjectQueryError("No field named '%s' in '%s'"%(field, typename))
raise LeObjectQueryError("No field named '%s' in '%s'"%(field, letype.__name__))
pass
## @brief Prepare filters for datasource

View file

@ -118,15 +118,15 @@ class LeType(leobject.leobject._LeObject):
# @throw AttributeError if datas provides values for fields that doesn't exists
@classmethod
def check_datas_or_raise(cls, datas, complete = False):
autom_fields = [f.name for f in cls._fieldtypes if f.internal]
autom_fields = [f for f, ft in cls._fieldtypes.items() if hasattr(ft,'internal') and ft.internal]
for dname, dval in datas.items():
if dname in autom_fields:
raise AttributeError("The field '%s' is internal"%(dname))
if dname not in cls._fields:
raise AttributeError("No such field '%s' for %s"%(dname, self.__class__.__name__))
cls._fieldtypess[dname].check_or_raise(dval)
cls._fieldtypes[dname].check_or_raise(dval)
fields = [f.name for f in cls._fieldtypes if not f.internal]
fields = [f for f, ft in cls._fieldtypes.items() if not hasattr(ft,'internal') or not ft.internal]
if complete and len(datas) < len(fields):
raise LeObjectError("The argument complete was True but some fields are missing : %s"%(set(fields) - set(datas.keys())))

View file

@ -4,6 +4,7 @@
import unittest
from unittest import TestCase
from unittest.mock import patch
import EditorialModel
import leobject
@ -53,7 +54,6 @@ class _LeObjectTestCase(TestCase):
_LeObject._split_filter(query)
## Testing methods that need the generated code
# @todo mock the datasource to test the get, update, delete and insert methods
class LeObjectTestCase(TestCase):
@classmethod
@ -202,3 +202,118 @@ class LeObjectTestCase(TestCase):
with self.assertRaises(ValueError):
LeObject._prepare_filters(filters, None, None)
class LeObjectMockDatasourceTestCase(TestCase):
""" Testing _LeObject using a mock on the datasource """
@classmethod
def setUpClass(cls):
""" Write the generated code in a temporary directory and import it """
cls.tmpdir = leobject.test.utils.tmp_load_factory_code()
@classmethod
def tearDownClass(cls):
""" Remove the temporary directory created at class setup """
leobject.test.utils.cleanup(cls.tmpdir)
@patch('leobject.datasources.dummy.DummyDatasource.insert')
def test_insert(self, dsmock):
from dyncode import Publication, Numero, LeObject
ndatas = [
[{'titre' : 'FooBar'}],
[{'titre':'hello'},{'titre':'world'}],
]
for ndats in ndatas:
LeObject.insert(Numero,ndats)
dsmock.assert_called_once_with(Numero, Publication, ndats)
dsmock.reset_mock()
LeObject.insert('Numero',ndats)
dsmock.assert_called_once_with(Numero, Publication, ndats)
dsmock.reset_mock()
@patch('leobject.datasources.dummy.DummyDatasource.update')
def test_update(self, dsmock):
from dyncode import Publication, Numero, LeObject
args = [
( ['lodel_id = 1'],
{'titre':'foobar'},
[('lodel_id','=','1')],
[]
),
( ['superior.parent in [1,2,3,4,5,6]', 'titre != "FooBar"'],
{'titre':'FooBar'},
[( 'titre','!=','"FooBar"')],
[( (leobject.leobject.REL_SUP, 'parent') ,' in ', '[1,2,3,4,5,6]')]
),
]
for filters, datas, ds_filters, ds_relfilters in args:
LeObject.update(Numero, filters, datas)
dsmock.assert_called_once_with(Numero, Publication, ds_filters, ds_relfilters, datas)
dsmock.reset_mock()
LeObject.update('Numero', filters, datas)
dsmock.assert_called_once_with(Numero, Publication, ds_filters, ds_relfilters, datas)
dsmock.reset_mock()
@patch('leobject.datasources.dummy.DummyDatasource.delete')
def test_delete(self, dsmock):
from dyncode import Publication, Numero, LeObject
args = [
(
['lodel_id=1'],
[('lodel_id', '=', '1')],
[]
),
(
['subordinate.parent not in [1,2,3]', 'titre = "titre nul"'],
[('titre','=', '"titre nul"')],
[( (leobject.leobject.REL_SUB, 'parent'), ' not in ', '[1,2,3]')]
),
]
for filters, ds_filters, ds_relfilters in args:
LeObject.delete(Numero, filters)
dsmock.assert_called_once_with(Numero, Publication, ds_filters, ds_relfilters)
dsmock.reset_mock()
LeObject.delete('Numero', filters)
dsmock.assert_called_once_with(Numero, Publication, ds_filters, ds_relfilters)
dsmock.reset_mock()
@patch('leobject.datasources.dummy.DummyDatasource.get')
def test_get(self, dsmock):
from dyncode import Publication, Numero, LeObject
args = [
(
['lodel_id', 'superior.parent'],
['titre != "foobar"'],
['lodel_id', (leobject.leobject.REL_SUP, 'parent')],
[('titre','!=', '"foobar"')],
[]
),
(
['lodel_id', 'titre', 'superior.parent', 'subordinate.translation'],
['superior.parent in [1,2,3,4,5]'],
['lodel_id', 'titre', (leobject.leobject.REL_SUP,'parent'), (leobject.leobject.REL_SUB, 'translation')],
[],
[( (leobject.leobject.REL_SUP, 'parent'), ' in ', '[1,2,3,4,5]')]
),
(
[],
[],
Numero._fields,
[],
[]
),
]
for field_list, filters, fl_ds, filters_ds, rfilters_ds in args:
LeObject.get(filters, field_list, Numero)
dsmock.assert_called_with(Publication, Numero, fl_ds, filters_ds, rfilters_ds)
dsmock.reset_mock()