mirror of
https://github.com/bringout/oca-technical.git
synced 2026-04-20 05:31:59 +02:00
Initial commit: OCA Technical packages (595 packages)
This commit is contained in:
commit
2cc02aac6e
24950 changed files with 2318079 additions and 0 deletions
|
|
@ -0,0 +1,4 @@
|
|||
from . import test_advisory_lock
|
||||
from . import test_listener
|
||||
from . import test_locker
|
||||
from . import test_mapper
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
# Copyright 2017 Camptocamp SA
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from odoo import api
|
||||
from odoo.modules.registry import Registry
|
||||
from odoo.tests import common
|
||||
|
||||
from odoo.addons.component.core import WorkContext
|
||||
from odoo.addons.component.tests.common import TransactionComponentCase
|
||||
from odoo.addons.connector.database import pg_try_advisory_lock
|
||||
from odoo.addons.queue_job.exception import RetryableJobError
|
||||
|
||||
|
||||
class TestAdvisoryLock(TransactionComponentCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.registry2 = Registry(common.get_db_name())
|
||||
self.cr2 = self.registry2.cursor()
|
||||
self.env2 = api.Environment(self.cr2, self.env.uid, {})
|
||||
|
||||
@self.addCleanup
|
||||
def reset_cr2():
|
||||
# rollback and close the cursor, and reset the environments
|
||||
self.env2.reset()
|
||||
self.cr2.rollback()
|
||||
self.cr2.close()
|
||||
|
||||
def test_concurrent_lock(self):
|
||||
"""2 concurrent transactions cannot acquire the same lock"""
|
||||
# the lock is based on a string, a second transaction trying
|
||||
# to acquire the same lock won't be able to acquire it
|
||||
lock = "import_record({}, {}, {}, {})".format(
|
||||
"backend.name", 1, "res.partner", "999999"
|
||||
)
|
||||
acquired = pg_try_advisory_lock(self.env, lock)
|
||||
self.assertTrue(acquired)
|
||||
# we test the base function
|
||||
inner_acquired = pg_try_advisory_lock(self.env2, lock)
|
||||
self.assertFalse(inner_acquired)
|
||||
|
||||
def test_concurrent_import_lock(self):
|
||||
"""A 2nd concurrent transaction must retry"""
|
||||
# the lock is based on a string, a second transaction trying
|
||||
# to acquire the same lock won't be able to acquire it
|
||||
lock = "import_record({}, {}, {}, {})".format(
|
||||
"backend.name", 1, "res.partner", "999999"
|
||||
)
|
||||
|
||||
backend = mock.MagicMock()
|
||||
backend.env = self.env
|
||||
work = WorkContext(model_name="res.partner", collection=backend)
|
||||
# we test the function through a Component instance
|
||||
component = work.component_by_name("base.connector")
|
||||
# acquire the lock
|
||||
component.advisory_lock_or_retry(lock)
|
||||
|
||||
# instanciate another component using a different odoo env
|
||||
# hence another PG transaction
|
||||
backend2 = mock.MagicMock()
|
||||
backend2.env = self.env2
|
||||
work2 = WorkContext(model_name="res.partner", collection=backend2)
|
||||
component2 = work2.component_by_name("base.connector")
|
||||
with self.assertRaises(RetryableJobError) as cm:
|
||||
component2.advisory_lock_or_retry(lock, retry_seconds=3)
|
||||
self.assertEqual(cm.exception.seconds, 3)
|
||||
|
|
@ -0,0 +1,59 @@
|
|||
# Copyright 2017 Camptocamp SA
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from odoo.tools import frozendict
|
||||
|
||||
from odoo.addons.component.core import Component
|
||||
from odoo.addons.component.tests.common import TransactionComponentRegistryCase
|
||||
from odoo.addons.component_event.components.event import skip_if
|
||||
from odoo.addons.component_event.core import EventWorkContext
|
||||
from odoo.addons.connector import components
|
||||
|
||||
|
||||
class TestEventListener(TransactionComponentRegistryCase):
|
||||
"""Test Connecter Listener"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self._setup_registry(self)
|
||||
|
||||
def test_skip_if_no_connector_export(self):
|
||||
class MyEventListener(Component):
|
||||
_name = "my.event.listener"
|
||||
_inherit = "base.event.listener"
|
||||
|
||||
def on_record_create(self, record, fields=None):
|
||||
assert True
|
||||
|
||||
class MyOtherEventListener(Component):
|
||||
_name = "my.other.event.listener"
|
||||
_inherit = "base.connector.listener"
|
||||
|
||||
@skip_if(lambda self, record, fields=None: self.no_connector_export(record))
|
||||
def on_record_create(self, record, fields=None):
|
||||
raise AssertionError()
|
||||
|
||||
self.env.context = frozendict(self.env.context, no_connector_export=True)
|
||||
work = EventWorkContext(
|
||||
model_name="res.users", env=self.env, components_registry=self.comp_registry
|
||||
)
|
||||
|
||||
# get the collecter to notify the event
|
||||
# we don't mind about the collection and the model here,
|
||||
# the events we test are global
|
||||
self.collecter = self.comp_registry["base.event.collecter"](work)
|
||||
|
||||
self._build_components(
|
||||
components.core.BaseConnectorComponent,
|
||||
components.listener.ConnectorListener,
|
||||
MyEventListener,
|
||||
MyOtherEventListener,
|
||||
)
|
||||
|
||||
# collect the event and notify it
|
||||
record = mock.Mock(name="record")
|
||||
collected = self.collecter.collect_events("on_record_create")
|
||||
self.assertEqual(2, len(collected.events))
|
||||
collected.notify(record)
|
||||
|
|
@ -0,0 +1,44 @@
|
|||
# Copyright 2018 Camptocamp SA
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl).
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from odoo import api
|
||||
from odoo.modules.registry import Registry
|
||||
from odoo.tests import common
|
||||
|
||||
from odoo.addons.component.core import WorkContext
|
||||
from odoo.addons.component.tests.common import TransactionComponentRegistryCase
|
||||
from odoo.addons.queue_job.exception import RetryableJobError
|
||||
|
||||
|
||||
class TestLocker(TransactionComponentRegistryCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self.backend = mock.MagicMock(name="backend")
|
||||
self.backend.env = self.env
|
||||
|
||||
self.registry2 = Registry(common.get_db_name())
|
||||
self.cr2 = self.registry2.cursor()
|
||||
self.env2 = api.Environment(self.cr2, self.env.uid, {})
|
||||
self.backend2 = mock.MagicMock(name="backend2")
|
||||
self.backend2.env = self.env2
|
||||
|
||||
@self.addCleanup
|
||||
def reset_cr2():
|
||||
# rollback and close the cursor, and reset the environments
|
||||
self.env2.reset()
|
||||
self.cr2.rollback()
|
||||
self.cr2.close()
|
||||
|
||||
def test_lock(self):
|
||||
"""Lock a record"""
|
||||
main_partner = self.env.ref("base.main_partner")
|
||||
work = WorkContext(model_name="res.partner", collection=self.backend)
|
||||
work.component("record.locker").lock(main_partner)
|
||||
|
||||
main_partner2 = self.env2.ref("base.main_partner")
|
||||
work2 = WorkContext(model_name="res.partner", collection=self.backend2)
|
||||
locker2 = work2.component("record.locker")
|
||||
with self.assertRaises(RetryableJobError):
|
||||
locker2.lock(main_partner2)
|
||||
|
|
@ -0,0 +1,935 @@
|
|||
# Copyright 2013 Camptocamp SA
|
||||
# License LGPL-3.0 or later (http://www.gnu.org/licenses/lgpl.html)
|
||||
|
||||
from unittest import mock
|
||||
|
||||
from odoo.addons.component.core import Component, WorkContext
|
||||
from odoo.addons.component.tests.common import TransactionComponentRegistryCase
|
||||
from odoo.addons.connector.components.mapper import (
|
||||
MapOptions,
|
||||
MappingDefinition,
|
||||
changed_by,
|
||||
convert,
|
||||
external_to_m2o,
|
||||
follow_m2o_relations,
|
||||
m2o_to_external,
|
||||
mapping,
|
||||
none,
|
||||
only_create,
|
||||
)
|
||||
|
||||
|
||||
class TestMapper(TransactionComponentRegistryCase):
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self._setup_registry(self)
|
||||
self.comp_registry.load_components("connector")
|
||||
|
||||
def test_mapping_decorator(self):
|
||||
class KifKrokerMapper(Component):
|
||||
_name = "kif.kroker.mapper"
|
||||
_inherit = "base.mapper"
|
||||
|
||||
@changed_by("name", "city")
|
||||
@mapping
|
||||
@only_create
|
||||
def name(self):
|
||||
pass
|
||||
|
||||
@changed_by("email")
|
||||
@mapping
|
||||
def email(self):
|
||||
pass
|
||||
|
||||
@changed_by("street")
|
||||
@mapping
|
||||
def street(self):
|
||||
pass
|
||||
|
||||
def no_decorator(self):
|
||||
pass
|
||||
|
||||
# build our mapper component
|
||||
KifKrokerMapper._build_component(self.comp_registry)
|
||||
|
||||
# what mappings we expect
|
||||
name_def = MappingDefinition(changed_by={"name", "city"}, only_create=True)
|
||||
email_def = MappingDefinition(changed_by={"email"}, only_create=False)
|
||||
street_def = MappingDefinition(changed_by={"street"}, only_create=False)
|
||||
|
||||
# get our component by name in the components registry
|
||||
comp = self.comp_registry["kif.kroker.mapper"]
|
||||
# _map_methods contains the aggregated mapping methods for a Mapper
|
||||
self.assertEqual(
|
||||
comp._map_methods,
|
||||
{"name": name_def, "email": email_def, "street": street_def},
|
||||
)
|
||||
|
||||
def test_mapping_decorator_cross_classes(self):
|
||||
"""Mappings should not propagate to other classes"""
|
||||
|
||||
class MomMapper(Component):
|
||||
_name = "mom.mapper"
|
||||
_inherit = "base.mapper"
|
||||
_apply_on = "res.users"
|
||||
|
||||
@changed_by("name", "city")
|
||||
@mapping
|
||||
def name(self):
|
||||
pass
|
||||
|
||||
class ZappMapper(Component):
|
||||
_name = "zapp.mapper"
|
||||
_inherit = "base.mapper"
|
||||
_apply_on = "res.users"
|
||||
|
||||
@changed_by("email")
|
||||
@only_create
|
||||
@mapping
|
||||
def email(self):
|
||||
pass
|
||||
|
||||
self._build_components(MomMapper, ZappMapper)
|
||||
|
||||
mom_def = MappingDefinition(changed_by={"name", "city"}, only_create=False)
|
||||
zapp_def = MappingDefinition(changed_by={"email"}, only_create=True)
|
||||
|
||||
comp = self.comp_registry["mom.mapper"]
|
||||
self.assertEqual(comp._map_methods, {"name": mom_def})
|
||||
comp = self.comp_registry["zapp.mapper"]
|
||||
self.assertEqual(comp._map_methods, {"email": zapp_def})
|
||||
|
||||
def test_mapping_decorator_cumul(self):
|
||||
"""Mappings should cumulate the ``super`` mappings
|
||||
and the local mappings."""
|
||||
|
||||
class FryMapper(Component):
|
||||
_name = "fry.mapper"
|
||||
_inherit = "base.mapper"
|
||||
_apply_on = "res.users"
|
||||
|
||||
@changed_by("name", "city")
|
||||
@mapping
|
||||
def name(self):
|
||||
pass
|
||||
|
||||
# pylint: disable=R7980
|
||||
class FryMapperInherit(Component):
|
||||
_inherit = "fry.mapper"
|
||||
|
||||
@changed_by("email")
|
||||
@mapping
|
||||
def email(self):
|
||||
pass
|
||||
|
||||
self._build_components(FryMapper, FryMapperInherit)
|
||||
|
||||
name_def = MappingDefinition(changed_by={"name", "city"}, only_create=False)
|
||||
email_def = MappingDefinition(changed_by={"email"}, only_create=False)
|
||||
|
||||
comp = self.comp_registry["fry.mapper"]
|
||||
self.assertEqual(comp._map_methods, {"name": name_def, "email": email_def})
|
||||
|
||||
def test_mapping_decorator_cumul_changed_by(self):
|
||||
"""Mappings should cumulate the changed_by fields of the
|
||||
``super`` mappings and the local mappings"""
|
||||
|
||||
class FryMapper(Component):
|
||||
_name = "fry.mapper"
|
||||
_inherit = "base.mapper"
|
||||
_apply_on = "res.users"
|
||||
|
||||
@changed_by("name", "city")
|
||||
@mapping
|
||||
def name(self):
|
||||
pass
|
||||
|
||||
class FryMapperInherit(Component):
|
||||
_inherit = ( # pylint: disable=consider-merging-classes-inherited
|
||||
"fry.mapper"
|
||||
)
|
||||
_apply_on = "res.users"
|
||||
|
||||
@changed_by("email")
|
||||
@mapping
|
||||
def name(self):
|
||||
pass
|
||||
|
||||
class ThirdMapper(Component):
|
||||
_name = "third.mapper"
|
||||
_inherit = "fry.mapper"
|
||||
_apply_on = "res.users"
|
||||
|
||||
@changed_by("email", "street")
|
||||
@mapping
|
||||
def name(self):
|
||||
pass
|
||||
|
||||
self._build_components(FryMapper, FryMapperInherit, ThirdMapper)
|
||||
|
||||
name_def = MappingDefinition(
|
||||
changed_by={"name", "city", "email"}, only_create=False
|
||||
)
|
||||
|
||||
comp = self.comp_registry["fry.mapper"]
|
||||
self.assertEqual(comp._map_methods, {"name": name_def})
|
||||
|
||||
name_def = MappingDefinition(
|
||||
changed_by={"name", "city", "email", "street"}, only_create=False
|
||||
)
|
||||
comp = self.comp_registry["third.mapper"]
|
||||
self.assertEqual(comp._map_methods, {"name": name_def})
|
||||
|
||||
def test_several_bases_cumul(self):
|
||||
class FryMapper(Component):
|
||||
_name = "fry.mapper"
|
||||
_inherit = "base.mapper"
|
||||
_apply_on = "res.users"
|
||||
|
||||
@changed_by("name", "city")
|
||||
@mapping
|
||||
def name(self):
|
||||
pass
|
||||
|
||||
@only_create
|
||||
@mapping
|
||||
def street(self):
|
||||
pass
|
||||
|
||||
@only_create
|
||||
@mapping
|
||||
def zip(self):
|
||||
pass
|
||||
|
||||
class FarnsworthMapper(Component):
|
||||
_name = "farnsworth.mapper"
|
||||
_inherit = "base.mapper"
|
||||
_apply_on = "res.users"
|
||||
|
||||
@changed_by("email")
|
||||
@mapping
|
||||
def name(self):
|
||||
pass
|
||||
|
||||
@changed_by("street")
|
||||
@mapping
|
||||
def city(self):
|
||||
pass
|
||||
|
||||
@mapping
|
||||
def zip(self):
|
||||
pass
|
||||
|
||||
class ThirdMapper(Component):
|
||||
_name = "third.mapper"
|
||||
_inherit = ["fry.mapper", "farnsworth.mapper"]
|
||||
_apply_on = "res.users"
|
||||
|
||||
@changed_by("email", "street")
|
||||
@mapping
|
||||
def name(self):
|
||||
pass
|
||||
|
||||
@mapping
|
||||
def email(self):
|
||||
pass
|
||||
|
||||
self._build_components(FryMapper, FarnsworthMapper, ThirdMapper)
|
||||
|
||||
name_def = MappingDefinition(
|
||||
changed_by={"name", "city", "email", "street"}, only_create=False
|
||||
)
|
||||
street_def = MappingDefinition(changed_by=set(), only_create=True)
|
||||
city_def = MappingDefinition(changed_by={"street"}, only_create=False)
|
||||
email_def = MappingDefinition(changed_by=set(), only_create=False)
|
||||
zip_def = MappingDefinition(changed_by=set(), only_create=True)
|
||||
|
||||
comp = self.comp_registry["third.mapper"]
|
||||
self.assertEqual(comp._map_methods["name"], name_def)
|
||||
self.assertEqual(comp._map_methods["street"], street_def)
|
||||
self.assertEqual(comp._map_methods["city"], city_def)
|
||||
self.assertEqual(comp._map_methods["email"], email_def)
|
||||
self.assertEqual(comp._map_methods["zip"], zip_def)
|
||||
|
||||
def test_mapping_record(self):
|
||||
"""Map a record and check the result"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
_apply_on = "res.users"
|
||||
|
||||
direct = [("name", "out_name")]
|
||||
|
||||
@mapping
|
||||
def street(self, record):
|
||||
return {"out_street": record["street"].upper()}
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {"name": "Guewen", "street": "street"}
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {"out_name": "Guewen", "out_street": "STREET"}
|
||||
self.assertEqual(map_record.values(), expected)
|
||||
self.assertEqual(map_record.values(for_create=True), expected)
|
||||
|
||||
def test_mapping_record_on_create(self):
|
||||
"""Map a record and check the result for creation of record"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
_apply_on = "res.users"
|
||||
|
||||
direct = [("name", "out_name")]
|
||||
|
||||
@mapping
|
||||
def street(self, record):
|
||||
return {"out_street": record["street"].upper()}
|
||||
|
||||
@only_create
|
||||
@mapping
|
||||
def city(self, record):
|
||||
return {"out_city": "city"}
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {"name": "Guewen", "street": "street"}
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {"out_name": "Guewen", "out_street": "STREET"}
|
||||
self.assertEqual(map_record.values(), expected)
|
||||
expected = {"out_name": "Guewen", "out_street": "STREET", "out_city": "city"}
|
||||
self.assertEqual(map_record.values(for_create=True), expected)
|
||||
|
||||
def test_mapping_update(self):
|
||||
"""Force values on a map record"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
|
||||
direct = [("name", "out_name")]
|
||||
|
||||
@mapping
|
||||
def street(self, record):
|
||||
return {"out_street": record["street"].upper()}
|
||||
|
||||
@only_create
|
||||
@mapping
|
||||
def city(self, record):
|
||||
return {"out_city": "city"}
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {"name": "Guewen", "street": "street"}
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
map_record = mapper.map_record(record)
|
||||
map_record.update({"test": 1}, out_city="forced")
|
||||
expected = {
|
||||
"out_name": "Guewen",
|
||||
"out_street": "STREET",
|
||||
"out_city": "forced",
|
||||
"test": 1,
|
||||
}
|
||||
self.assertEqual(map_record.values(), expected)
|
||||
expected = {
|
||||
"out_name": "Guewen",
|
||||
"out_street": "STREET",
|
||||
"out_city": "forced",
|
||||
"test": 1,
|
||||
}
|
||||
self.assertEqual(map_record.values(for_create=True), expected)
|
||||
|
||||
# pylint: disable=W8110
|
||||
def test_finalize(self):
|
||||
"""Inherit finalize to modify values"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
|
||||
direct = [("name", "out_name")]
|
||||
|
||||
def finalize(self, record, values):
|
||||
result = super().finalize(record, values)
|
||||
result["test"] = "abc"
|
||||
return result
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {"name": "Guewen", "street": "street"}
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {"out_name": "Guewen", "test": "abc"}
|
||||
self.assertEqual(map_record.values(), expected)
|
||||
expected = {"out_name": "Guewen", "test": "abc"}
|
||||
self.assertEqual(map_record.values(for_create=True), expected)
|
||||
|
||||
def test_some_fields(self):
|
||||
"""Map only a selection of fields"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
|
||||
direct = [("name", "out_name"), ("street", "out_street")]
|
||||
|
||||
@changed_by("country")
|
||||
@mapping
|
||||
def country(self, record):
|
||||
return {"country": "country"}
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {"name": "Guewen", "street": "street", "country": "country"}
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {"out_name": "Guewen", "country": "country"}
|
||||
self.assertEqual(map_record.values(fields=["name", "country"]), expected)
|
||||
expected = {"out_name": "Guewen", "country": "country"}
|
||||
self.assertEqual(
|
||||
map_record.values(for_create=True, fields=["name", "country"]), expected
|
||||
)
|
||||
|
||||
def test_mapping_modifier(self):
|
||||
"""Map a direct record with a modifier function"""
|
||||
|
||||
def do_nothing(field):
|
||||
def transform(self, record, to_attr):
|
||||
return record[field]
|
||||
|
||||
return transform
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
|
||||
direct = [(do_nothing("name"), "out_name")]
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {"name": "Guewen"}
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {"out_name": "Guewen"}
|
||||
self.assertEqual(map_record.values(), expected)
|
||||
self.assertEqual(map_record.values(for_create=True), expected)
|
||||
|
||||
def test_mapping_direct_property(self):
|
||||
"""Map a direct record with 'direct' being a property"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
|
||||
@property
|
||||
def direct(self):
|
||||
return [("name", "out_name")]
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {"name": "Foo"}
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {"out_name": "Foo"}
|
||||
self.assertEqual(map_record.values(), expected)
|
||||
self.assertEqual(map_record.values(for_create=True), expected)
|
||||
|
||||
def test_mapping_convert(self):
|
||||
"""Map a direct record with the convert modifier function"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
|
||||
direct = [(convert("name", int), "out_name")]
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {"name": "300"}
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {"out_name": 300}
|
||||
self.assertEqual(map_record.values(), expected)
|
||||
self.assertEqual(map_record.values(for_create=True), expected)
|
||||
|
||||
def test_mapping_modifier_none(self):
|
||||
"""Pipeline of modifiers"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
|
||||
direct = [(none("in_f"), "out_f"), (none("in_t"), "out_t")]
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {"in_f": False, "in_t": True}
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {"out_f": None, "out_t": True}
|
||||
self.assertEqual(map_record.values(), expected)
|
||||
self.assertEqual(map_record.values(for_create=True), expected)
|
||||
|
||||
def test_mapping_modifier_pipeline(self):
|
||||
"""Pipeline of modifiers"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
|
||||
direct = [
|
||||
(none(convert("in_f", bool)), "out_f"),
|
||||
(none(convert("in_t", bool)), "out_t"),
|
||||
]
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {"in_f": 0, "in_t": 1}
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {"out_f": None, "out_t": True}
|
||||
self.assertEqual(map_record.values(), expected)
|
||||
self.assertEqual(map_record.values(for_create=True), expected)
|
||||
|
||||
def test_modifier_import_filter_field(self):
|
||||
"""A direct mapping with a modifier must still be considered
|
||||
from the list of fields
|
||||
"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
|
||||
direct = [
|
||||
("field", "field2"),
|
||||
("no_field", "no_field2"),
|
||||
(convert("name", int), "out_name"),
|
||||
]
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {"name": "300", "field": "value", "no_field": "no_value"}
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {"out_name": 300, "field2": "value"}
|
||||
self.assertEqual(map_record.values(fields=["field", "name"]), expected)
|
||||
self.assertEqual(
|
||||
map_record.values(for_create=True, fields=["field", "name"]), expected
|
||||
)
|
||||
|
||||
def test_modifier_export_filter_field(self):
|
||||
"""A direct mapping with a modifier on an export mapping"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.export.mapper"
|
||||
|
||||
direct = [
|
||||
("field", "field2"),
|
||||
("no_field", "no_field2"),
|
||||
(convert("name", int), "out_name"),
|
||||
]
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {"name": "300", "field": "value", "no_field": "no_value"}
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {"out_name": 300, "field2": "value"}
|
||||
self.assertEqual(map_record.values(fields=["field", "name"]), expected)
|
||||
self.assertEqual(
|
||||
map_record.values(for_create=True, fields=["field", "name"]), expected
|
||||
)
|
||||
|
||||
def test_mapping_custom_option(self):
|
||||
"""Usage of custom options in mappings"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
|
||||
@mapping
|
||||
def any(self, record):
|
||||
if self.options.custom:
|
||||
res = True
|
||||
else:
|
||||
res = False
|
||||
return {"res": res}
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {}
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {"res": True}
|
||||
self.assertEqual(map_record.values(custom=True), expected)
|
||||
|
||||
def test_mapping_custom_option_not_defined(self):
|
||||
"""Usage of custom options not defined raise AttributeError"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
|
||||
@mapping
|
||||
def any(self, record):
|
||||
if self.options.custom is None:
|
||||
res = True
|
||||
else:
|
||||
res = False
|
||||
return {"res": res}
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {}
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {"res": True}
|
||||
self.assertEqual(map_record.values(), expected)
|
||||
|
||||
def test_map_options(self):
|
||||
"""Test MapOptions"""
|
||||
options = MapOptions({"xyz": "abc"}, k=1)
|
||||
options.l = 2 # noqa: E741
|
||||
self.assertEqual(options["xyz"], "abc")
|
||||
self.assertEqual(options["k"], 1)
|
||||
self.assertEqual(options["l"], 2)
|
||||
self.assertEqual(options.xyz, "abc")
|
||||
self.assertEqual(options.k, 1)
|
||||
self.assertEqual(options.l, 2)
|
||||
self.assertEqual(options["undefined"], None)
|
||||
self.assertEqual(options.undefined, None)
|
||||
|
||||
def test_changed_by_fields(self):
|
||||
"""Test attribute ``_changed_by_fields`` on Mapper."""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.export.mapper"
|
||||
|
||||
direct = [
|
||||
("street", "out_street"),
|
||||
(none("in_t"), "out_t"),
|
||||
(none(convert("in_f", bool)), "out_f"),
|
||||
]
|
||||
|
||||
@changed_by("name", "city")
|
||||
@mapping
|
||||
def name(self):
|
||||
pass
|
||||
|
||||
@changed_by("email")
|
||||
@mapping
|
||||
def email(self):
|
||||
pass
|
||||
|
||||
def no_decorator(self):
|
||||
pass
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
work = mock.MagicMock(name="WorkContext()")
|
||||
mapper = self.comp_registry["my.mapper"](work)
|
||||
|
||||
self.assertEqual(
|
||||
mapper.changed_by_fields(),
|
||||
{"street", "in_t", "in_f", "name", "city", "email"},
|
||||
)
|
||||
|
||||
|
||||
class TestMapperRecordsets(TransactionComponentRegistryCase):
|
||||
"""Test mapper with "real" records instead of mocks"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self._setup_registry(self)
|
||||
self.comp_registry.load_components("connector")
|
||||
|
||||
backend_record = mock.Mock()
|
||||
backend_record.env = self.env
|
||||
self.work = WorkContext(
|
||||
model_name="res.partner",
|
||||
collection=backend_record,
|
||||
components_registry=self.comp_registry,
|
||||
)
|
||||
|
||||
def test_mapping_modifier_follow_m2o_relations(self):
|
||||
"""Map with the follow_m2o_relations modifier"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
|
||||
direct = [(follow_m2o_relations("parent_id.name"), "parent_name")]
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
partner = self.env.ref("base.res_partner_address_4")
|
||||
mapper = self.comp_registry["my.mapper"](self.work)
|
||||
map_record = mapper.map_record(partner)
|
||||
expected = {"parent_name": "Deco Addict"}
|
||||
self.assertEqual(map_record.values(), expected)
|
||||
self.assertEqual(map_record.values(for_create=True), expected)
|
||||
|
||||
|
||||
class TestMapperBinding(TransactionComponentRegistryCase):
|
||||
"""Test Mapper with Bindings"""
|
||||
|
||||
def setUp(self):
|
||||
super().setUp()
|
||||
self._setup_registry(self)
|
||||
self.comp_registry.load_components("connector")
|
||||
|
||||
backend_record = mock.Mock()
|
||||
backend_record.env = self.env
|
||||
backend_record._name = "my.collection"
|
||||
self.work = WorkContext(
|
||||
model_name="res.partner",
|
||||
collection=backend_record,
|
||||
components_registry=self.comp_registry,
|
||||
)
|
||||
|
||||
self.country_binder = mock.MagicMock(name="country_binder")
|
||||
self.country_binder.return_value = self.country_binder
|
||||
self.country_binder._name = "test.binder"
|
||||
self.country_binder._inherit = "base.binder"
|
||||
self.country_binder.apply_on_models = ["res.country"]
|
||||
self.country_binder._usage = "binder"
|
||||
self.country_binder._collection = "my.collection"
|
||||
self.country_binder._abstract = False
|
||||
self.comp_registry["test.binder"] = self.country_binder
|
||||
|
||||
def test_mapping_m2o_to_external(self):
|
||||
"""Map a direct record with the m2o_to_external modifier function"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
_apply_on = "res.partner"
|
||||
|
||||
direct = [(m2o_to_external("country_id"), "country")]
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
partner = self.env.ref("base.main_partner")
|
||||
partner.write({"country_id": self.env.ref("base.ch").id})
|
||||
self.country_binder.to_external.return_value = 10
|
||||
|
||||
mapper = self.comp_registry["my.mapper"](self.work)
|
||||
map_record = mapper.map_record(partner)
|
||||
self.assertEqual(map_record.values(), {"country": 10})
|
||||
self.country_binder.to_external.assert_called_once_with(
|
||||
partner.country_id.id, wrap=False
|
||||
)
|
||||
|
||||
def test_mapping_backend_to_m2o(self):
|
||||
"""Map a direct record with the backend_to_m2o modifier function"""
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
_apply_on = "res.partner"
|
||||
|
||||
direct = [(external_to_m2o("country"), "country_id")]
|
||||
|
||||
self._build_components(MyMapper)
|
||||
|
||||
record = {"country": 10}
|
||||
ch = self.env.ref("base.ch")
|
||||
self.country_binder.to_internal.return_value = ch
|
||||
mapper = self.comp_registry["my.mapper"](self.work)
|
||||
map_record = mapper.map_record(record)
|
||||
self.assertEqual(map_record.values(), {"country_id": ch.id})
|
||||
self.country_binder.to_internal.assert_called_once_with(10, unwrap=False)
|
||||
|
||||
def test_mapping_record_children_no_map_child(self):
|
||||
"""Map a record with children, using default MapChild"""
|
||||
# we need these components which make the 'link' between
|
||||
# the main mapper and the line mapper
|
||||
|
||||
class LineMapper(Component):
|
||||
_name = "line.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
_apply_on = "res.currency.rate"
|
||||
|
||||
direct = [("name", "name")]
|
||||
|
||||
@mapping
|
||||
def price(self, record):
|
||||
return {"rate": record["rate"] * 2}
|
||||
|
||||
@only_create
|
||||
@mapping
|
||||
def discount(self, record):
|
||||
return {"test": 0.5}
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
_apply_on = "res.currency"
|
||||
|
||||
direct = [("name", "name")]
|
||||
|
||||
children = [("lines", "line_ids", "res.currency.rate")]
|
||||
|
||||
self._build_components(LineMapper, MyMapper)
|
||||
|
||||
record = {
|
||||
"name": "SO1",
|
||||
"lines": [
|
||||
{"name": "2013-11-07", "rate": 10},
|
||||
{"name": "2013-11-08", "rate": 20},
|
||||
],
|
||||
}
|
||||
mapper = self.comp_registry["my.mapper"](self.work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {
|
||||
"name": "SO1",
|
||||
"line_ids": [
|
||||
(0, 0, {"name": "2013-11-07", "rate": 20}),
|
||||
(0, 0, {"name": "2013-11-08", "rate": 40}),
|
||||
],
|
||||
}
|
||||
self.assertEqual(map_record.values(), expected)
|
||||
expected = {
|
||||
"name": "SO1",
|
||||
"line_ids": [
|
||||
(0, 0, {"name": "2013-11-07", "rate": 20, "test": 0.5}),
|
||||
(0, 0, {"name": "2013-11-08", "rate": 40, "test": 0.5}),
|
||||
],
|
||||
}
|
||||
self.assertEqual(map_record.values(for_create=True), expected)
|
||||
|
||||
def test_mapping_record_children(self):
|
||||
"""Map a record with children, using defined MapChild"""
|
||||
# we need these components which make the 'link' between
|
||||
# the main mapper and the line mapper
|
||||
|
||||
class LineMapper(Component):
|
||||
_name = "line.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
_apply_on = "res.currency.rate"
|
||||
|
||||
direct = [("name", "name")]
|
||||
|
||||
@mapping
|
||||
def price(self, record):
|
||||
return {"rate": record["rate"] * 2}
|
||||
|
||||
@only_create
|
||||
@mapping
|
||||
def discount(self, record):
|
||||
return {"test": 0.5}
|
||||
|
||||
class LineImportMapChild(Component):
|
||||
_name = "line.map.child.import"
|
||||
_inherit = "base.map.child.import"
|
||||
_apply_on = "res.currency.rate"
|
||||
|
||||
def format_items(self, items_values):
|
||||
return [("ABC", values) for values in items_values]
|
||||
|
||||
class MyMapper(Component):
|
||||
_name = "my.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
_apply_on = "res.currency"
|
||||
|
||||
direct = [("name", "name")]
|
||||
|
||||
children = [("lines", "line_ids", "res.currency.rate")]
|
||||
|
||||
self._build_components(LineMapper, LineImportMapChild, MyMapper)
|
||||
|
||||
record = {
|
||||
"name": "SO1",
|
||||
"lines": [
|
||||
{"name": "2013-11-07", "rate": 10},
|
||||
{"name": "2013-11-08", "rate": 20},
|
||||
],
|
||||
}
|
||||
mapper = self.comp_registry["my.mapper"](self.work)
|
||||
map_record = mapper.map_record(record)
|
||||
expected = {
|
||||
"name": "SO1",
|
||||
"line_ids": [
|
||||
("ABC", {"name": "2013-11-07", "rate": 20}),
|
||||
("ABC", {"name": "2013-11-08", "rate": 40}),
|
||||
],
|
||||
}
|
||||
self.assertEqual(map_record.values(), expected)
|
||||
expected = {
|
||||
"name": "SO1",
|
||||
"line_ids": [
|
||||
("ABC", {"name": "2013-11-07", "rate": 20, "test": 0.5}),
|
||||
("ABC", {"name": "2013-11-08", "rate": 40, "test": 0.5}),
|
||||
],
|
||||
}
|
||||
self.assertEqual(map_record.values(for_create=True), expected)
|
||||
|
||||
def test_mapping_record_children_void(self):
|
||||
"""Map a record with children, using defined MapChild"""
|
||||
|
||||
class LineMapper(Component):
|
||||
_name = "line.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
_apply_on = "res.currency.rate"
|
||||
|
||||
@mapping
|
||||
def price(self, record):
|
||||
rate = record.get("rate")
|
||||
if rate and rate < 40:
|
||||
return {"rate": record["rate"] * 2}
|
||||
|
||||
class SaleLineImportMapChild(Component):
|
||||
_name = "sale.line.mapper"
|
||||
_inherit = "base.map.child.import"
|
||||
_apply_on = "res.currency.rate"
|
||||
|
||||
def format_items(self, items_values):
|
||||
return [("ABC", values) for values in items_values]
|
||||
|
||||
class ObjectMapper(Component):
|
||||
_name = "currency.mapper"
|
||||
_inherit = "base.import.mapper"
|
||||
_apply_on = "res.currency"
|
||||
|
||||
direct = [("name", "name")]
|
||||
|
||||
children = [("lines", "line_ids", "res.currency.rate")]
|
||||
|
||||
self._build_components(ObjectMapper, SaleLineImportMapChild, LineMapper)
|
||||
|
||||
# Test with an excluded child record
|
||||
record = {
|
||||
"name": "SO1",
|
||||
"lines": [{"rate": 10}, {"rate": 20}, {"rate": 30}, {"rate": 40}],
|
||||
}
|
||||
mapper = self.comp_registry["currency.mapper"](self.work)
|
||||
map_record = mapper.map_record(record)
|
||||
|
||||
expected = {
|
||||
"name": "SO1",
|
||||
"line_ids": [
|
||||
("ABC", {"rate": 20}),
|
||||
("ABC", {"rate": 40}),
|
||||
("ABC", {"rate": 60}),
|
||||
],
|
||||
}
|
||||
self.assertEqual(map_record.values(), expected)
|
||||
Loading…
Add table
Add a link
Reference in a new issue