From 68b519389042c092a4c0fcb0be4c5280ba61f3f3 Mon Sep 17 00:00:00 2001 From: Dave Lasley Date: Wed, 20 Jul 2016 09:42:38 -0700 Subject: [PATCH] [ADD] connector_carepoint: Add test coverage * Add Binder tests * Add Mapper tests * Add BaseExporter tests * Add CarepointExporter tests * Add CarepointImporter tests * Add related_action tests * Add consumer tests --- connector_carepoint/__init__.py | 2 +- connector_carepoint/__openerp__.py | 2 +- connector_carepoint/backend.py | 2 +- connector_carepoint/connector.py | 2 +- connector_carepoint/consumer.py | 28 +- connector_carepoint/data/medical_units.txt | 2 +- connector_carepoint/models/__init__.py | 2 +- connector_carepoint/models/account.py | 2 +- .../models/account_invoice_line.py | 2 +- connector_carepoint/models/address.py | 2 +- connector_carepoint/models/address_patient.py | 2 +- .../models/address_pharmacy.py | 2 +- .../models/address_physician.py | 2 +- .../models/carepoint_backend.py | 2 +- .../models/carepoint_person.py | 2 +- connector_carepoint/models/carepoint_state.py | 2 +- connector_carepoint/models/fdb_form.py | 2 +- connector_carepoint/models/fdb_gcn.py | 2 +- connector_carepoint/models/fdb_gcn_seq.py | 2 +- connector_carepoint/models/fdb_img.py | 2 +- connector_carepoint/models/fdb_img_date.py | 2 +- connector_carepoint/models/fdb_img_id.py | 2 +- connector_carepoint/models/fdb_img_mfg.py | 2 +- connector_carepoint/models/fdb_lbl_rid.py | 2 +- connector_carepoint/models/fdb_ndc.py | 2 +- connector_carepoint/models/fdb_ndc_cs_ext.py | 2 +- connector_carepoint/models/fdb_pem_moe.py | 2 +- connector_carepoint/models/fdb_pem_mogc.py | 2 +- connector_carepoint/models/fdb_route.py | 2 +- connector_carepoint/models/fdb_unit.py | 2 +- .../models/medical_medicament.py | 2 +- .../models/medical_medicament_attribute.py | 2 +- .../medical_medicament_attribute_type.py | 2 +- connector_carepoint/models/medical_patient.py | 2 +- .../models/medical_pharmacy.py | 64 +-- .../models/medical_physician.py | 2 +- .../models/medical_prescription_order.py | 2 +- .../models/medical_prescription_order_line.py | 2 +- .../models/procurement_order.py | 2 +- connector_carepoint/models/res_users.py | 2 +- connector_carepoint/models/sale_order.py | 2 +- connector_carepoint/models/sale_order_line.py | 2 +- .../models/sale_order_line_non_rx.py | 2 +- connector_carepoint/models/stock_picking.py | 2 +- connector_carepoint/related_action.py | 2 +- connector_carepoint/tests/__init__.py | 12 +- connector_carepoint/tests/common.py | 28 +- connector_carepoint/tests/models/__init__.py | 5 + .../tests/models/test_medical_pharmacy.py | 38 ++ .../tests/test_backend_adapter.py | 2 +- .../tests/test_base_exporter.py | 319 ++++++++++++ connector_carepoint/tests/test_binder.py | 201 ++++++++ .../tests/test_carepoint_backend.py | 3 +- .../tests/test_carepoint_exporter.py | 317 ++++++++++++ .../tests/test_carepoint_importer.py | 484 ++++++++++++++++++ connector_carepoint/tests/test_consumer.py | 58 +++ connector_carepoint/tests/test_mapper.py | 56 ++ .../tests/test_related_action.py | 110 ++++ connector_carepoint/unit/__init__.py | 2 +- connector_carepoint/unit/backend_adapter.py | 2 +- connector_carepoint/unit/binder.py | 10 +- .../unit/delete_synchronizer.py | 2 +- .../unit/export_synchronizer.py | 39 +- .../unit/import_synchronizer.py | 65 +-- connector_carepoint/unit/mapper.py | 19 +- 65 files changed, 1712 insertions(+), 238 deletions(-) create mode 100644 connector_carepoint/tests/models/__init__.py create mode 100644 connector_carepoint/tests/models/test_medical_pharmacy.py create mode 100644 connector_carepoint/tests/test_base_exporter.py create mode 100644 connector_carepoint/tests/test_binder.py create mode 100644 connector_carepoint/tests/test_carepoint_exporter.py create mode 100644 connector_carepoint/tests/test_carepoint_importer.py create mode 100644 connector_carepoint/tests/test_consumer.py create mode 100644 connector_carepoint/tests/test_mapper.py create mode 100644 connector_carepoint/tests/test_related_action.py diff --git a/connector_carepoint/__init__.py b/connector_carepoint/__init__.py index 2efa786..2c9ebf5 100644 --- a/connector_carepoint/__init__.py +++ b/connector_carepoint/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import models diff --git a/connector_carepoint/__openerp__.py b/connector_carepoint/__openerp__.py index d6b0370..7b80f18 100644 --- a/connector_carepoint/__openerp__.py +++ b/connector_carepoint/__openerp__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). { diff --git a/connector_carepoint/backend.py b/connector_carepoint/backend.py index 34dcb7e..04505d3 100644 --- a/connector_carepoint/backend.py +++ b/connector_carepoint/backend.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import openerp.addons.connector.backend as backend diff --git a/connector_carepoint/connector.py b/connector_carepoint/connector.py index 745b7a0..f6661cf 100644 --- a/connector_carepoint/connector.py +++ b/connector_carepoint/connector.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from openerp import models, fields diff --git a/connector_carepoint/consumer.py b/connector_carepoint/consumer.py index d423ba0..f6184b7 100644 --- a/connector_carepoint/consumer.py +++ b/connector_carepoint/consumer.py @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). -from openerp.addons.connector.connector import Binder +# from openerp.addons.connector.connector import Binder from .unit.export_synchronizer import (export_record, ) -from .unit.delete_synchronizer import export_delete_record -from .connector import get_environment +# from .unit.delete_synchronizer import export_delete_record +# from .connector import get_environment # from openerp.addons.connector.event import (on_record_write, # on_record_create, # on_record_unlink @@ -58,13 +58,13 @@ def delay_export_all_bindings(session, model_name, record_id, vals): # 'carepoint.carepoint.address', # 'carepoint.carepoint.address.patient', # ]) -def delay_unlink(session, model_name, record_id): - """ Delay a job which delete a record on Carepoint. - Called on binding records.""" - record = session.env[model_name].browse(record_id) - env = get_environment(session, model_name, record.backend_id.id) - binder = env.get_connector_unit(Binder) - carepoint_id = binder.to_backend(record_id, wrap=False) - if carepoint_id: - export_delete_record.delay(session, model_name, - record.backend_id.id, carepoint_id) +# def delay_unlink(session, model_name, record_id): +# """ Delay a job which delete a record on Carepoint. +# Called on binding records.""" +# record = session.env[model_name].browse(record_id) +# env = get_environment(session, model_name, record.backend_id.id) +# binder = env.get_connector_unit(Binder) +# carepoint_id = binder.to_backend(record_id, wrap=False) +# if carepoint_id: +# export_delete_record.delay(session, model_name, +# record.backend_id.id, carepoint_id) diff --git a/connector_carepoint/data/medical_units.txt b/connector_carepoint/data/medical_units.txt index e1944fb..e702860 100644 --- a/connector_carepoint/data/medical_units.txt +++ b/connector_carepoint/data/medical_units.txt @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # diff --git a/connector_carepoint/models/__init__.py b/connector_carepoint/models/__init__.py index 1c1e15b..847e1ed 100644 --- a/connector_carepoint/models/__init__.py +++ b/connector_carepoint/models/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). # Backend diff --git a/connector_carepoint/models/account.py b/connector_carepoint/models/account.py index 787734b..a186479 100644 --- a/connector_carepoint/models/account.py +++ b/connector_carepoint/models/account.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/account_invoice_line.py b/connector_carepoint/models/account_invoice_line.py index a5d54d8..2e88706 100644 --- a/connector_carepoint/models/account_invoice_line.py +++ b/connector_carepoint/models/account_invoice_line.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/address.py b/connector_carepoint/models/address.py index e008821..e47bf65 100644 --- a/connector_carepoint/models/address.py +++ b/connector_carepoint/models/address.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/address_patient.py b/connector_carepoint/models/address_patient.py index cbf1791..5b19df4 100644 --- a/connector_carepoint/models/address_patient.py +++ b/connector_carepoint/models/address_patient.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/address_pharmacy.py b/connector_carepoint/models/address_pharmacy.py index 54dd4a9..b15b979 100644 --- a/connector_carepoint/models/address_pharmacy.py +++ b/connector_carepoint/models/address_pharmacy.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/address_physician.py b/connector_carepoint/models/address_physician.py index 141fa29..46822ed 100644 --- a/connector_carepoint/models/address_physician.py +++ b/connector_carepoint/models/address_physician.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/carepoint_backend.py b/connector_carepoint/models/carepoint_backend.py index 2c3838f..4400e4d 100644 --- a/connector_carepoint/models/carepoint_backend.py +++ b/connector_carepoint/models/carepoint_backend.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/carepoint_person.py b/connector_carepoint/models/carepoint_person.py index cd6124c..4dfedc0 100644 --- a/connector_carepoint/models/carepoint_person.py +++ b/connector_carepoint/models/carepoint_person.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/carepoint_state.py b/connector_carepoint/models/carepoint_state.py index 3f6f29e..a821cb3 100644 --- a/connector_carepoint/models/carepoint_state.py +++ b/connector_carepoint/models/carepoint_state.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from openerp import models, fields diff --git a/connector_carepoint/models/fdb_form.py b/connector_carepoint/models/fdb_form.py index 519b03c..e47e6c8 100644 --- a/connector_carepoint/models/fdb_form.py +++ b/connector_carepoint/models/fdb_form.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/fdb_gcn.py b/connector_carepoint/models/fdb_gcn.py index bc89dd2..d3cea56 100644 --- a/connector_carepoint/models/fdb_gcn.py +++ b/connector_carepoint/models/fdb_gcn.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/fdb_gcn_seq.py b/connector_carepoint/models/fdb_gcn_seq.py index d375973..75fbdb0 100644 --- a/connector_carepoint/models/fdb_gcn_seq.py +++ b/connector_carepoint/models/fdb_gcn_seq.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/fdb_img.py b/connector_carepoint/models/fdb_img.py index ebe4cf6..942c017 100644 --- a/connector_carepoint/models/fdb_img.py +++ b/connector_carepoint/models/fdb_img.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/fdb_img_date.py b/connector_carepoint/models/fdb_img_date.py index 03829d6..789df8a 100644 --- a/connector_carepoint/models/fdb_img_date.py +++ b/connector_carepoint/models/fdb_img_date.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/fdb_img_id.py b/connector_carepoint/models/fdb_img_id.py index d7dbb96..dbf287c 100644 --- a/connector_carepoint/models/fdb_img_id.py +++ b/connector_carepoint/models/fdb_img_id.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/fdb_img_mfg.py b/connector_carepoint/models/fdb_img_mfg.py index 5e4a3ce..4e01b40 100644 --- a/connector_carepoint/models/fdb_img_mfg.py +++ b/connector_carepoint/models/fdb_img_mfg.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/fdb_lbl_rid.py b/connector_carepoint/models/fdb_lbl_rid.py index b155038..d21745b 100644 --- a/connector_carepoint/models/fdb_lbl_rid.py +++ b/connector_carepoint/models/fdb_lbl_rid.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/fdb_ndc.py b/connector_carepoint/models/fdb_ndc.py index 6f58c30..9878ee9 100644 --- a/connector_carepoint/models/fdb_ndc.py +++ b/connector_carepoint/models/fdb_ndc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/fdb_ndc_cs_ext.py b/connector_carepoint/models/fdb_ndc_cs_ext.py index d8daac7..49cd4ac 100644 --- a/connector_carepoint/models/fdb_ndc_cs_ext.py +++ b/connector_carepoint/models/fdb_ndc_cs_ext.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/fdb_pem_moe.py b/connector_carepoint/models/fdb_pem_moe.py index d3f7b79..44b166f 100644 --- a/connector_carepoint/models/fdb_pem_moe.py +++ b/connector_carepoint/models/fdb_pem_moe.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/fdb_pem_mogc.py b/connector_carepoint/models/fdb_pem_mogc.py index d293278..89281f8 100644 --- a/connector_carepoint/models/fdb_pem_mogc.py +++ b/connector_carepoint/models/fdb_pem_mogc.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/fdb_route.py b/connector_carepoint/models/fdb_route.py index d0fded1..e0aa9bd 100644 --- a/connector_carepoint/models/fdb_route.py +++ b/connector_carepoint/models/fdb_route.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/fdb_unit.py b/connector_carepoint/models/fdb_unit.py index 1c37d2f..60051a1 100644 --- a/connector_carepoint/models/fdb_unit.py +++ b/connector_carepoint/models/fdb_unit.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/medical_medicament.py b/connector_carepoint/models/medical_medicament.py index 51efdd9..f138563 100644 --- a/connector_carepoint/models/medical_medicament.py +++ b/connector_carepoint/models/medical_medicament.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/medical_medicament_attribute.py b/connector_carepoint/models/medical_medicament_attribute.py index 4982201..80303aa 100644 --- a/connector_carepoint/models/medical_medicament_attribute.py +++ b/connector_carepoint/models/medical_medicament_attribute.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/medical_medicament_attribute_type.py b/connector_carepoint/models/medical_medicament_attribute_type.py index 9fdd958..139f98d 100644 --- a/connector_carepoint/models/medical_medicament_attribute_type.py +++ b/connector_carepoint/models/medical_medicament_attribute_type.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/medical_patient.py b/connector_carepoint/models/medical_patient.py index fe49328..bd2dd4a 100644 --- a/connector_carepoint/models/medical_patient.py +++ b/connector_carepoint/models/medical_patient.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/medical_pharmacy.py b/connector_carepoint/models/medical_pharmacy.py index 93b5ddb..e06f8f5 100644 --- a/connector_carepoint/models/medical_pharmacy.py +++ b/connector_carepoint/models/medical_pharmacy.py @@ -1,22 +1,18 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging from openerp import models, fields -from openerp.addons.connector.queue.job import job -from openerp.addons.connector.connector import ConnectorUnit from openerp.addons.connector.unit.mapper import (mapping, only_create, ) from ..unit.backend_adapter import CarepointCRUDAdapter -from ..connector import get_environment from ..backend import carepoint from ..unit.mapper import PartnerImportMapper from ..unit.import_synchronizer import (DelayedBatchImporter, CarepointImporter, ) -from ..connector import add_checkpoint _logger = logging.getLogger(__name__) @@ -78,17 +74,6 @@ class MedicalPharmacyBatchImporter(DelayedBatchImporter): """ _model_name = ['carepoint.medical.pharmacy'] - def run(self, filters=None): - """ Run the synchronization """ - if filters is None: - filters = {} - record_ids = self.backend_adapter.search(**filters) - _logger.info('Search for carepoint companies %s returned %s\n', - filters, record_ids) - for record_id in record_ids: - _logger.info('In record loop with %s', record_id) - self._import_record(record_id) - @carepoint class MedicalPharmacyImportMapper(PartnerImportMapper): @@ -106,8 +91,8 @@ class MedicalPharmacyImportMapper(PartnerImportMapper): ('chg_date', 'updated_at'), ] - @only_create @mapping + @only_create def odoo_id(self, record): """ Will bind the company on an existing company with the same name """ @@ -130,49 +115,4 @@ def carepoint_id(self, record): @carepoint class MedicalPharmacyImporter(CarepointImporter): _model_name = ['carepoint.medical.pharmacy'] - _base_mapper = MedicalPharmacyImportMapper - - def _create(self, data): - binding = super(MedicalPharmacyImporter, self)._create(data) - checkpoint = self.unit_for(MedicalPharmacyAddCheckpoint) - checkpoint.run(binding.id) - return binding - - def _import_dependencies(self): - """ Import depends for record """ - # record = self.carepoint_record - # @TODO: Fix below error - # 'csstore_addr' does not have the identity property. - # Cannot perform SET operation. - # - # self._import_dependency(record['store_id'], - # 'carepoint.carepoint.address.pharmacy') - - # - # def _after_import(self, partner_binding): - # """ Import the addresses """ - # book = self.unit_for(PartnerAddressBook, model='carepoint.address') - # book.import_addresses(self.carepoint_id, partner_binding.id) - - -@carepoint -class MedicalPharmacyAddCheckpoint(ConnectorUnit): - """ Add a connector.checkpoint on the carepoint.medical.pharmacy record """ - _model_name = ['carepoint.medical.pharmacy', ] - - def run(self, binding_id): - add_checkpoint(self.session, - self.model._name, - binding_id, - self.backend_record.id) - - -@job(default_channel='root.carepoint.core') -def company_import_batch(session, model_name, backend_id, filters=None): - """ Prepare the import of companies modified on Carepoint """ - if filters is None: - filters = {} - env = get_environment(session, model_name, backend_id) - importer = env.get_connector_unit(MedicalPharmacyBatchImporter) - importer.run(filters=filters) diff --git a/connector_carepoint/models/medical_physician.py b/connector_carepoint/models/medical_physician.py index a393e61..7b842f6 100644 --- a/connector_carepoint/models/medical_physician.py +++ b/connector_carepoint/models/medical_physician.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/medical_prescription_order.py b/connector_carepoint/models/medical_prescription_order.py index ad038ef..d7e997d 100644 --- a/connector_carepoint/models/medical_prescription_order.py +++ b/connector_carepoint/models/medical_prescription_order.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/medical_prescription_order_line.py b/connector_carepoint/models/medical_prescription_order_line.py index e56b727..741b689 100644 --- a/connector_carepoint/models/medical_prescription_order_line.py +++ b/connector_carepoint/models/medical_prescription_order_line.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/procurement_order.py b/connector_carepoint/models/procurement_order.py index 3ff9c4f..db24f8e 100644 --- a/connector_carepoint/models/procurement_order.py +++ b/connector_carepoint/models/procurement_order.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/res_users.py b/connector_carepoint/models/res_users.py index e72a706..f835b7f 100644 --- a/connector_carepoint/models/res_users.py +++ b/connector_carepoint/models/res_users.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/sale_order.py b/connector_carepoint/models/sale_order.py index 168f153..a0046b5 100644 --- a/connector_carepoint/models/sale_order.py +++ b/connector_carepoint/models/sale_order.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/sale_order_line.py b/connector_carepoint/models/sale_order_line.py index 5dade18..778f191 100644 --- a/connector_carepoint/models/sale_order_line.py +++ b/connector_carepoint/models/sale_order_line.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/sale_order_line_non_rx.py b/connector_carepoint/models/sale_order_line_non_rx.py index 6464323..3d8d781 100644 --- a/connector_carepoint/models/sale_order_line_non_rx.py +++ b/connector_carepoint/models/sale_order_line_non_rx.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/models/stock_picking.py b/connector_carepoint/models/stock_picking.py index f94c545..c435144 100644 --- a/connector_carepoint/models/stock_picking.py +++ b/connector_carepoint/models/stock_picking.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging diff --git a/connector_carepoint/related_action.py b/connector_carepoint/related_action.py index 43c4e61..5203f0b 100644 --- a/connector_carepoint/related_action.py +++ b/connector_carepoint/related_action.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). """ diff --git a/connector_carepoint/tests/__init__.py b/connector_carepoint/tests/__init__.py index caa66e2..2ae9b7d 100644 --- a/connector_carepoint/tests/__init__.py +++ b/connector_carepoint/tests/__init__.py @@ -1,6 +1,16 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import test_carepoint_backend from . import test_backend_adapter +from . import test_binder +from . import test_mapper +from . import test_base_exporter +from . import test_carepoint_exporter +from . import test_carepoint_importer +from . import test_related_action +from . import test_consumer + + +from .models import * diff --git a/connector_carepoint/tests/common.py b/connector_carepoint/tests/common.py index 7d13b1d..3f3b032 100644 --- a/connector_carepoint/tests/common.py +++ b/connector_carepoint/tests/common.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2016 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). """ @@ -22,7 +22,7 @@ def mock_job_delay_to_direct(job_path): """ Replace the .delay() of a job by a direct call job_path is the python path, such as:: - openerp.addons.carepoint.stock_picking.export_picking_done + openerp.addons.carepoint.models.stock_picking.export_picking_done """ job_module, job_name = job_path.rsplit('.', 1) module = importlib.import_module(job_module) @@ -46,9 +46,16 @@ def clean_args_for_func(*args, **kwargs): @contextmanager -def mock_api(): +def mock_api(instantiated=False): """ Mock CarePoint API for testing """ with mock.patch('%s.Carepoint' % backend_adapter) as API: + yield API() if instantiated else API + + +@contextmanager +def mock_adapter(unit): + """ Mock the backend adapter for testing """ + with mock.patch.object(unit, '_backend_adapter') as API: yield API @@ -65,15 +72,14 @@ def __init__(self, env, model_name, backend): env.uid, env.context, ) + self.connector_unit = {} - -class ObjDict(dict): - - def __getattr__(self, key): + def get_connector_unit(self, unit_class): try: - return super(ObjDict, self).__getattr__(key) - except AttributeError: - return self[key] + return self.connector_unit[unit_class] + except KeyError: + self.connector_unit[unit_class] = mock.MagicMock() + return self.connector_unit[unit_class] class SetUpCarepointBase(common.TransactionCase): @@ -140,8 +146,8 @@ def setUp(self): 'default_product_income_account_id': self.account_income.id, 'default_product_expense_account_id': self.account_expense.id, }) - self.backend_id = self.backend.id self.mock_api = mock_api + self.mock_adapter = mock_adapter def get_carepoint_helper(self, model_name): return CarepointHelper( diff --git a/connector_carepoint/tests/models/__init__.py b/connector_carepoint/tests/models/__init__.py new file mode 100644 index 0000000..e350fba --- /dev/null +++ b/connector_carepoint/tests/models/__init__.py @@ -0,0 +1,5 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2016 LasLabs Inc. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from . import test_medical_pharmacy diff --git a/connector_carepoint/tests/models/test_medical_pharmacy.py b/connector_carepoint/tests/models/test_medical_pharmacy.py new file mode 100644 index 0000000..a487119 --- /dev/null +++ b/connector_carepoint/tests/models/test_medical_pharmacy.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2016 LasLabs Inc. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +# import mock + +# from openerp.addons.connector_carepoint.unit.import_synchronizer import ( +# import_record, +# ) + +from ..common import SetUpCarepointBase + + +model = 'openerp.addons.connector_carepoint.models.medical_pharmacy' + + +class TestMedicalPharmacy(SetUpCarepointBase): + + def setUp(self): + super(TestMedicalPharmacy, self).setUp() + self.BindModel = self.env['carepoint.medical.pharmacy'] + self.Model = self.env['medical.pharmacy'] + + def _new_record(self, bind=True, name='Test Pharm'): + vals = {'name': name} + if bind: + Model = self.BindModel + vals.update({ + 'carepoint_id': 1234567, + 'backend_id': self.backend.id, + }) + else: + Model = self.Model + return Model.create(vals) + + # def test_import(self): + # """ It should import record """ + # with self.mock_adapter() as api: diff --git a/connector_carepoint/tests/test_backend_adapter.py b/connector_carepoint/tests/test_backend_adapter.py index 0833d8f..e74deb4 100644 --- a/connector_carepoint/tests/test_backend_adapter.py +++ b/connector_carepoint/tests/test_backend_adapter.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2016 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from openerp.addons.connector_carepoint.unit import backend_adapter diff --git a/connector_carepoint/tests/test_base_exporter.py b/connector_carepoint/tests/test_base_exporter.py new file mode 100644 index 0000000..fd19828 --- /dev/null +++ b/connector_carepoint/tests/test_base_exporter.py @@ -0,0 +1,319 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2016 LasLabs Inc. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import mock + +from openerp import fields + +from openerp.addons.connector.exception import IDMissingInBackend + +from openerp.addons.connector_carepoint.unit import export_synchronizer + +from .common import SetUpCarepointBase + + +model = 'openerp.addons.connector_carepoint.unit.export_synchronizer' + + +class EndTestException(Exception): + pass + + +class TestBaseExporter(SetUpCarepointBase): + + def setUp(self): + super(TestBaseExporter, self).setUp() + self.model = 'carepoint.medical.pharmacy' + self.carepoint_id = 'carepoint_id' + self.binding_id = 1234 + self.Exporter = export_synchronizer.CarepointBaseExporter + + def _new_exporter(self, carepoint_id=None, binding_record=None, + binding_id=None, + ): + exporter = self.Exporter(self.get_carepoint_helper( + self.model + )) + exporter.carepoint_id = carepoint_id + exporter.binding_record = binding_record + exporter.binding_id = binding_id + return exporter + + def _new_record(self, sync_date=False): + return self.env[self.model].create({ + 'name': 'Test', + 'sync_date': sync_date, + }) + + def test_exporter_init_binding_id(self): + """ It should init binding_id as None """ + exporter = self._new_exporter() + self.assertEqual(None, exporter.binding_id) + + def test_exporter_init_carepoint_id(self): + """ It should init carepoint_id as None """ + exporter = self._new_exporter() + self.assertEqual(None, exporter.carepoint_id) + + def test_delay_import_assets_carepoint_id(self): + """ It should not allow a false carepoint_id """ + exporter = self._new_exporter() + with self.assertRaises(AssertionError): + exporter._delay_import() + + @mock.patch('%s.import_record' % model) + def test_delay_import_delays_import(self, mk): + """ It should call delayed import w/ proper args """ + exporter = self._new_exporter(self.carepoint_id) + exporter._delay_import() + mk.delay.assert_called_once_with( + exporter.session, + exporter.model._name, + exporter.backend_record.id, + exporter.carepoint_id, + force=True, + ) + + def test_should_import_asserts_binding(self): + """ It should throw AssertionError on no binding_record """ + exporter = self._new_exporter() + with self.assertRaises(AssertionError): + exporter._should_import() + + def test_should_import_false_carepoint_id(self): + """ It should return False when no carepoint_id """ + exporter = self._new_exporter( + binding_record=self._new_record() + ) + res = exporter._should_import() + self.assertFalse(res) + + def test_should_import_no_previous_sync(self): + """ It should return True when there is not a previous sync """ + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record(), + ) + res = exporter._should_import() + self.assertTrue(res) + + def test_should_import_gets_record(self): + """ It should get record from backend """ + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record('2016-06-12 00:00:00'), + ) + with self.mock_adapter(exporter) as adapter: + exporter._should_import() + adapter.read.assert_called_once_with( + self.carepoint_id, attributes=['chg_date'] + ) + + def test_should_import_no_chg_date(self): + """ It should return False when no chg_date col """ + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record('2016-06-12 00:00:00'), + ) + with self.mock_adapter(exporter) as adapter: + adapter.read.return_value = { + 'chg_date': False + } + res = exporter._should_import() + self.assertFalse(res) + + def test_should_import_not_changed(self): + """ It should return False if the record is not changed """ + expect = '2016-06-12 00:00:00' + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record(expect), + ) + with self.mock_adapter(exporter) as adapter: + adapter.read.return_value = { + 'chg_date': fields.Datetime.from_string( + expect + ) + } + res = exporter._should_import() + self.assertFalse(res) + + def test_should_import_is_changed(self): + """ It should return True if the record is changed """ + expect = '2016-06-12 00:00:00' + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record(expect), + ) + with self.mock_adapter(exporter) as adapter: + adapter.read.return_value = { + 'chg_date': fields.Datetime.from_string( + expect.replace('6', '7') + ) + } + res = exporter._should_import() + self.assertTrue(res) + + def test_get_odoo_data_browse(self): + """ It should browse model for binding """ + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record('2016-06-12 00:00:00'), + binding_id=self.binding_id, + ) + with mock.patch.object(exporter.connector_env, 'model') as mk: + exporter._get_odoo_data() + mk.browse.assert_called_once_with(self.binding_id) + + def test_get_odoo_data_return(self): + """ It should return browse record """ + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record('2016-06-12 00:00:00'), + binding_id=self.binding_id, + ) + with mock.patch.object(exporter.connector_env, 'model') as mk: + res = exporter._get_odoo_data() + self.assertEqual(mk.browse(), res) + + def test_run_sets_binding_id(self): + """ It should set binding_id on instance """ + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record('2016-06-12 00:00:00'), + ) + with mock.patch.object(exporter, '_get_odoo_data') as mk: + mk.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter.run(self.binding_id) + self.assertEqual( + self.binding_id, exporter.binding_id, + ) + + def test_run_gets_backend(self): + """ It should get the backend for binding """ + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record('2016-06-12 00:00:00'), + ) + with mock.patch.object(exporter.binder, 'to_backend') as mk: + mk.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter.run(self.binding_id) + mk.assert_called_once_with(self.binding_id) + + def test_run_should_import(self): + """ It should see if the record needs to be imported """ + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record('2016-06-12 00:00:00'), + ) + with mock.patch.object(exporter, '_should_import') as mk: + mk.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter.run(self.binding_id) + + def test_run_should_import_missing_carepoint_id(self): + """ It should set carepoint_id to None if missing """ + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record('2016-06-12 00:00:00'), + ) + with mock.patch.object(exporter, '_should_import') as mk: + with mock.patch.object(exporter, '_run') as run: + mk.side_effect = IDMissingInBackend + run.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter.run(self.binding_id) + self.assertEqual( + None, exporter.carepoint_id, + ) + + def test_run_should_import_true(self): + """ It should call delay import if should_import """ + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record('2016-06-12 00:00:00'), + ) + with mock.patch.object(exporter, '_should_import') as should: + should.return_value = True + with mock.patch.object(exporter, '_delay_import') as mk: + mk.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter.run(self.binding_id) + + def test_run_calls_private_run(self): + """ It should call private run interface with args """ + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record('2016-06-12 00:00:00'), + ) + expect_list = [1, 2, 3] + expect_dict = {'1': 'test', '2': 'derp'} + with mock.patch.object(exporter, '_should_import') as mk: + mk.return_value = False + with mock.patch.object(exporter, '_run') as mk: + mk.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter.run(self.binding_id, *expect_list, **expect_dict) + mk.assert_called_once_with(*expect_list, **expect_dict) + + def test_run_calls_bind(self): + """ It should call bind with proper args """ + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record('2016-06-12 00:00:00'), + ) + with mock.patch.object(exporter, '_should_import') as mk: + mk.return_value = False + with mock.patch.object(exporter, '_run'): + with mock.patch.object(exporter, '_binder') as binder: + binder.bind.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter.run(self.binding_id) + binder.bind.assert_called_once_with( + binder.to_backend(), self.binding_id, + ) + + def test_run_commits_session(self): + """ It should commit session for export isolation """ + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record('2016-06-12 00:00:00'), + ) + with mock.patch.object(exporter, '_should_import') as mk: + mk.return_value = False + with mock.patch.object(exporter, '_run'): + with mock.patch.object(exporter.binder, 'bind'): + with mock.patch.object(exporter, 'session') as session: + session.commit.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter.run(self.binding_id) + + def test_run_calls_after_export(self): + """ It should call _after_export when done """ + exporter = self._new_exporter( + carepoint_id=self.carepoint_id, + binding_record=self._new_record('2016-06-12 00:00:00'), + ) + with mock.patch.object(exporter, '_should_import') as mk: + mk.return_value = False + with mock.patch.object(exporter, '_run'): + with mock.patch.object(exporter.binder, 'bind'): + with mock.patch.object(exporter, '_after_export') as mk: + mk.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter.run(self.binding_id) + + def test__run_exception(self): + """ Private run should not be implemented at this level """ + exporter = self._new_exporter() + with self.assertRaises(NotImplementedError): + exporter._run() + + def test_after_export(self): + """ It should return None """ + exporter = self._new_exporter() + res = exporter._after_export() + self.assertEqual(None, res) diff --git a/connector_carepoint/tests/test_binder.py b/connector_carepoint/tests/test_binder.py new file mode 100644 index 0000000..34bd2f9 --- /dev/null +++ b/connector_carepoint/tests/test_binder.py @@ -0,0 +1,201 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2016 LasLabs Inc. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import mock + +from openerp.addons.connector_carepoint.unit import binder + +from .common import SetUpCarepointBase + + +model = 'openerp.addons.connector_carepoint.unit.binder' + + +class TestBinder(SetUpCarepointBase): + + def setUp(self): + super(TestBinder, self).setUp() + self.model = 'carepoint.medical.pharmacy' + self.carepoint_id = 1234567 + self.Binder = binder.CarepointModelBinder + + def _new_binder(self): + return self.Binder(self.get_carepoint_helper( + self.model + )) + + def _new_record(self, bind=True): + return self.env[self.model].create({ + 'name': 'Test Pharm', + 'carepoint_id': self.carepoint_id if bind else None, + 'backend_id': self.backend.id, + }) + + def test_to_odoo_unwrap(self): + """ It should return internal Id of Odoo record """ + rec = self._new_record() + binder = self._new_binder() + res = binder.to_odoo( + self.carepoint_id, + unwrap=True, + browse=False, + ) + self.assertEqual(rec.odoo_id.id, res) + + def test_to_odoo_unwrap_browse(self): + """ It should return Odoo record """ + rec = self._new_record() + binder = self._new_binder() + res = binder.to_odoo( + self.carepoint_id, + unwrap=True, + browse=True, + ) + self.assertEqual(rec.odoo_id, res) + + def test_to_odoo_wrap(self): + """ It should return internal Id of Odoo bind record """ + rec = self._new_record() + binder = self._new_binder() + res = binder.to_odoo( + self.carepoint_id, + unwrap=False, + browse=False, + ) + self.assertEqual(rec.id, res) + + def test_to_odoo_wrap_browse(self): + """ It should return Odoo bind record """ + rec = self._new_record() + binder = self._new_binder() + res = binder.to_odoo( + self.carepoint_id, + unwrap=False, + browse=True, + ) + self.assertEqual(rec, res) + + def test_to_backend_wrap(self): + """ It should return return Odoo bind record """ + rec = self._new_record() + binder = self._new_binder() + res = binder.to_backend( + rec.id, + wrap=True, + ) + self.assertEqual(rec, res) + + def test_to_backend_wrap(self): + """ It should properly handle input recordset """ + rec = self._new_record() + binder = self._new_binder() + res = binder.to_backend( + rec, + wrap=True, + ) + self.assertEqual(rec.carepoint_id, res) + + def test_to_backend_unwrap(self): + """ It should return return Odoo bind record id """ + rec = self._new_record() + binder = self._new_binder() + res = binder.to_backend( + rec.id, + wrap=False, + ) + self.assertEqual(rec.id, res) + + def test_bind_no_binding(self): + """ It should not allow False binding """ + binder = self._new_binder() + with self.assertRaises(AssertionError): + binder.bind(True, False) + + def test_bind_no_external(self): + """ It should not allow False external Id """ + binder = self._new_binder() + with self.assertRaises(AssertionError): + binder.bind(False, True) + + @mock.patch('%s.openerp' % model) + def test_bind_context_no_export(self, openerp): + """ It should use a connector_no_export context """ + binder = self._new_binder() + rec = mock.MagicMock() + openerp.models.BaseModel = type(rec) + binder.bind(self.carepoint_id, rec) + rec.with_context.assert_called_once_with( + connector_no_export=True, + ) + + @mock.patch('%s.openerp' % model) + def test_bind_writes(self, openerp): + """ It should write binding and sync time to record """ + rec = mock.MagicMock() + openerp.models.BaseModel = type(rec) + binder = self._new_binder() + binder.bind(self.carepoint_id, rec) + rec.with_context().write.assert_called_once_with({ + 'carepoint_id': str(self.carepoint_id), + 'sync_date': openerp.fields.Datetime.now(), + }) + + def test_unwrap_binding_id_browse(self): + """ It should return normal record given binding id """ + rec = self._new_record() + binder = self._new_binder() + self.assertEqual( + rec.odoo_id, + binder.unwrap_binding( + rec.id, browse=True, + ) + ) + + def test_unwrap_binding_record_browse(self): + """ It should return normal record given binding record """ + rec = self._new_record() + binder = self._new_binder() + self.assertEqual( + rec.odoo_id, + binder.unwrap_binding( + rec, browse=True, + ) + ) + + def test_unwrap_binding_id(self): + """ It should return normal record id given binding id """ + rec = self._new_record() + binder = self._new_binder() + self.assertEqual( + rec.odoo_id.id, + binder.unwrap_binding( + rec.id, browse=False, + ) + ) + + def test_unwrap_binding_record(self): + """ It should return normal record id given binding record """ + rec = self._new_record() + binder = self._new_binder() + self.assertEqual( + rec.odoo_id.id, + binder.unwrap_binding( + rec, browse=False, + ) + ) + + def test_unwrap_model_valid(self): + """ It should return normal model name """ + binder = self._new_binder() + self.AssertEqual( + self.model.replace('carepoint.', ''), + binder.unwrap_model() + ) + + def test_unwrap_model_valid(self): + """ It should raise ValueError on unbound models """ + self.model = 'res.partner' + binder = self._new_binder() + with self.assertRaises(ValueError): + binder.unwrap_model() diff --git a/connector_carepoint/tests/test_carepoint_backend.py b/connector_carepoint/tests/test_carepoint_backend.py index c7c5999..36ab783 100644 --- a/connector_carepoint/tests/test_carepoint_backend.py +++ b/connector_carepoint/tests/test_carepoint_backend.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2016 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import mock @@ -14,6 +14,7 @@ IMPORT_DELTA_BUFFER ) + model = 'openerp.addons.connector_carepoint.models.carepoint_backend' diff --git a/connector_carepoint/tests/test_carepoint_exporter.py b/connector_carepoint/tests/test_carepoint_exporter.py new file mode 100644 index 0000000..43cc96e --- /dev/null +++ b/connector_carepoint/tests/test_carepoint_exporter.py @@ -0,0 +1,317 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2016 LasLabs Inc. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import mock +import psycopg2 +from contextlib import contextmanager + +from openerp.addons.connector_carepoint.unit import export_synchronizer + +from .common import SetUpCarepointBase + +model = 'openerp.addons.connector_carepoint.unit.export_synchronizer' + + +@contextmanager +def mock_base_exporter(obj, patches=None, add=True): + """ Inject mock as only parent to CarepointExporter + Normal method of injection would not work due to super raising + ``TypeError: must be type, not MagicMock`` + """ + _patches = [ + 'binder_for', + 'unit_for', + 'session', + '_mapper', + ] + if patches: + if add: + patches = _patches + patches + else: + patches = _patches + patches = {p: mock.DEFAULT for p in patches} + with mock.patch.multiple(obj, **patches) as mk: + yield mk + + +@contextmanager +def mock_retryable_job_error(): + with mock.patch('%s.RetryableJobError' % model) as mk: + yield mk + + +class EndTestException(Exception): + pass + + +class UniqueViolationException(psycopg2.IntegrityError, EndTestException): + def __init__(self, pgcode=psycopg2.errorcodes.UNIQUE_VIOLATION): + self.pgcode = pgcode + + +class TestCarepointExporter(SetUpCarepointBase): + + def setUp(self): + super(TestCarepointExporter, self).setUp() + self.model = 'carepoint.medical.pharmacy' + self.carepoint_id = 'carepoint_id' + self.binding_id = 1234 + self.Exporter = export_synchronizer.CarepointExporter + + def _new_exporter(self, carepoint_id=None, binding_record=None, + binding_id=None, + ): + self.mock_env = self.get_carepoint_helper( + self.model + ) + exporter = self.Exporter(self.mock_env) + exporter.carepoint_id = carepoint_id + exporter.binding_record = binding_record + exporter.binding_id = binding_id + return exporter + + def _new_record(self): + return self.env[self.model].create({ + 'name': 'Test', + }) + + def test_lock_sql(self): + """ It should attempt proper SQL execution """ + exporter = self._new_exporter(binding_id=self.binding_id) + with mock_base_exporter(exporter): + exporter.session.cr.execute.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter._lock() + exporter.session.cr.execute.assert_called_once_with( + "SELECT id FROM %s WHERE ID = %%s FOR UPDATE NOWAIT" % ( + self.model.replace('.', '_'), + ), + (self.binding_id, ), + log_exceptions=False, + ) + + # def test_lock_retryable(self): + # """ It should attempt proper SQL execution """ + # exporter = self._new_exporter() + # with mock_base_exporter(exporter): + # with mock_retryable_job_error() as err: + # exporter.session.cr.execute.side_effect = \ + # psycopg2.OperationalError + # with self.assertRaises(err): + # exporter._lock() + + def test_has_to_skip(self): + """ It should return False """ + exporter = self._new_exporter() + with mock_base_exporter(exporter): + res = exporter._has_to_skip() + self.assertFalse(res) + + def test_export_dependency_no_relation(self): + """ It should return None when no relation """ + exporter = self._new_exporter() + with mock_base_exporter(exporter): + res = exporter._export_dependency(None, None) + self.assertEqual(None, res) + + def test_export_dependency_gets_binder(self): + """ It should get binder for model """ + expect = self._new_record() + exporter = self._new_exporter() + with mock_base_exporter(exporter): + exporter.binder_for.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter._export_dependency(expect, self.model) + exporter.binder_for.assert_called_once_with(self.model) + + def test_export_dependency_wrap_search(self): + """ It should perform query for binding record when wrapped """ + rec_id = self._new_record() + expect = rec_id.odoo_id + exporter = self._new_exporter() + with mock_base_exporter(exporter): + with mock.patch.object(exporter.session, 'env'): + search = exporter.env[self.model].search + search.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter._export_dependency(expect, self.model) + search.assert_called_once_with([ + ('odoo_id', '=', expect.id), + ('backend_id', '=', exporter.backend_record.id), + ]) + + def test_export_dependency_wrap_multiple_results(self): + """ It should assert max of one binding result """ + expect = self._new_record().odoo_id + exporter = self._new_exporter() + with mock_base_exporter(exporter): + with mock.patch.object(exporter.session, 'env'): + search = exporter.env[self.model].search + search.return_value = [1, 2] + with self.assertRaises(AssertionError): + exporter._export_dependency(expect, self.model) + + # def test_export_dependency_wrap_to_backend(self): + # """ It should call to_backend with proper args from wrapped """ + # expect = self._new_record() + # exporter = self._new_exporter() + # with mock_base_exporter(exporter): + # with mock.patch.object(exporter.session, 'env'): + # to_backend = exporter.binder_for().to_backend + # to_backend.side_effect = EndTestException + # with self.assertRaises(EndTestException): + # exporter._export_dependency(expect.odoo_id, self.model) + # to_backend.assert_called_once_with( + # exporter.env[self.model].search(), + # wrap=False + # ) + + def test_export_dependency_unwrapped(self): + """ It should call to_backend with proper args from unwrapped """ + expect = self._new_record() + exporter = self._new_exporter() + with mock_base_exporter(exporter): + to_backend = exporter.binder_for().to_backend + to_backend.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter._export_dependency(expect, self.model) + to_backend.assert_called_once_with( + expect, + wrap=False + ) + + def test_export_dependency_run_no_force(self): + """ It should not trigger export when not forced and existing """ + expect = self._new_record() + exporter = self._new_exporter() + with mock_base_exporter(exporter): + to_backend = exporter.binder_for().to_backend + to_backend.return_value = True + exporter._export_dependency(expect, self.model, force=False) + exporter.unit_for().run.assert_not_called() + + def test_export_dependency_run_force(self): + """ It should trigger export when forced and existing """ + expect = self._new_record() + exporter = self._new_exporter() + with mock_base_exporter(exporter): + to_backend = exporter.binder_for().to_backend + to_backend.return_value = True + exporter._export_dependency(expect, self.model, force=True) + exporter.unit_for().run.assert_called_once_with(expect.id) + + def test_export_dependency_run_no_exist(self): + """ It should trigger export when not forced and not existing """ + expect = self._new_record() + exporter = self._new_exporter() + with mock_base_exporter(exporter): + to_backend = exporter.binder_for().to_backend + to_backend.return_value = False + exporter._export_dependency(expect, self.model, force=False) + exporter.unit_for().run.assert_called_once_with(expect.id) + + def test_export_dependencies(self): + """ It should return None """ + res = self._new_exporter()._export_dependencies() + self.assertEqual(None, res) + + def test_map_data_call(self): + """ It should get map record for binding record """ + exporter = self._new_exporter() + with mock_base_exporter(exporter): + exporter._map_data() + exporter.mapper.map_record.assert_called_once_with( + exporter.binding_record + ) + + def test_map_data_return(self): + """ It should return map record for binding record """ + exporter = self._new_exporter() + with mock_base_exporter(exporter): + res = exporter._map_data() + self.assertEqual(exporter.mapper.map_record(), res) + + def test_validate_create_data(self): + """ It should return None """ + res = self._new_exporter()._validate_create_data(True) + self.assertEqual(None, res) + + def test_validate_update_data(self): + """ It should return None """ + res = self._new_exporter()._validate_update_data(True) + self.assertEqual(None, res) + + def test_create_data_call(self): + """ It should inject proper vals into map record """ + map_record = mock.MagicMock() + expect = {'test': 123, 'test2': 456} + fields = expect.keys() + self._new_exporter(self.carepoint_id)._create_data( + map_record, fields, **expect + ) + map_record.values.assert_called_once_with( + for_create=True, fields=fields, **expect + ) + + def test_create_data_return(self): + """ It should inject proper vals into map record """ + map_record = mock.MagicMock() + res = self._new_exporter(self.carepoint_id)._create_data(map_record) + self.assertEqual(map_record.values(), res) + + def test_create_validates_data(self): + """ It should validate data """ + expect = 'expect' + exporter = self._new_exporter() + with mock_base_exporter(exporter, ['_validate_create_data']): + exporter._validate_create_data.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter._create(expect) + exporter._validate_create_data.assert_called_once_with(expect) + + def test_create_does_create(self): + """ It should create remote record w/ data """ + expect = 'expect' + exporter = self._new_exporter() + with self.mock_adapter(exporter) as mk: + mk.create.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter._create(expect) + mk.create.assert_called_once_with(expect) + + def test_create_returns_binding(self): + """ It should return new binding """ + exporter = self._new_exporter() + with self.mock_adapter(exporter) as mk: + res = exporter._create(None) + self.assertEqual( + mk.create(), res + ) + + def test_update_data_call(self): + """ It should inject proper vals into map record """ + map_record = mock.MagicMock() + expect = {'test': 123, 'test2': 456} + fields = expect.keys() + self._new_exporter(self.carepoint_id)._update_data( + map_record, fields, **expect + ) + map_record.values.assert_called_once_with(fields=fields, **expect) + + def test_update_data_return(self): + """ It should inject proper vals into map record """ + map_record = mock.MagicMock() + res = self._new_exporter(self.carepoint_id)._update_data(map_record) + self.assertEqual(map_record.values(), res) + + def test_update_does_write(self): + """ It should update binding w/ data """ + expect = 'expect' + mk = mock.MagicMock() + exporter = self._new_exporter(carepoint_id=self.carepoint_id) + with self.mock_adapter(exporter) as mk: + mk.write.side_effect = EndTestException + with self.assertRaises(EndTestException): + exporter._update(expect) + mk.write.assert_called_once_with(self.carepoint_id, expect) diff --git a/connector_carepoint/tests/test_carepoint_importer.py b/connector_carepoint/tests/test_carepoint_importer.py new file mode 100644 index 0000000..4d78396 --- /dev/null +++ b/connector_carepoint/tests/test_carepoint_importer.py @@ -0,0 +1,484 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2016 LasLabs Inc. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import mock +from contextlib import contextmanager + +from openerp import fields, _ + +from openerp.addons.connector_carepoint.unit import import_synchronizer + +from .common import SetUpCarepointBase + +model = 'openerp.addons.connector_carepoint.unit.import_synchronizer' + + +@contextmanager +def mock_base_importer(obj, patches=None, add=True): + """ Inject mock as only parent to CarepointExporter + Normal method of injection would not work due to super raising + ``TypeError: must be type, not MagicMock`` + """ + _patches = [ + 'binder_for', + 'unit_for', + '_validate_data', + 'advisory_lock_or_retry', + '_must_skip', + '_before_import', + '_get_carepoint_data', + ] + if patches: + if add: + patches = _patches + patches + else: + patches = _patches + patches = {p: mock.DEFAULT for p in patches} + with mock.patch.multiple(obj, **patches) as mk: + yield mk + + +class EndTestException(Exception): + pass + + +class TestCarepointImporter(SetUpCarepointBase): + + def setUp(self): + super(TestCarepointImporter, self).setUp() + self.model = 'carepoint.medical.pharmacy' + self.carepoint_id = 'carepoint_id' + self.carepoint_record = { + 'chg_date': fields.Datetime.from_string('2016-05-10 00:00:00'), + } + self.binding_id = 1234 + self.Importer = import_synchronizer.CarepointImporter + self.mock_env = self.get_carepoint_helper( + self.model + ) + + def _new_record(self, sync_date='2016-01-01 00:00:00'): + return self.env[self.model].create({ + 'name': 'Test', + 'sync_date': sync_date, + }) + + def _new_importer(self, carepoint_id=None, carepoint_record=None): + importer = self.Importer(self.mock_env) + if carepoint_id is not None: + importer.carepoint_id = carepoint_id + if carepoint_record is not None: + importer.carepoint_record = carepoint_record + return importer + + def test_int_or_str_int(self): + """ It should return an int when parseable as such """ + expect = 12345 + res = import_synchronizer.int_or_str(str(expect)) + self.assertEqual(expect, res) + + def test_int_or_str_str(self): + """ It should return a string when not parseable as int """ + expect = mock + res = import_synchronizer.int_or_str(expect) + self.assertEqual(str(expect), res) + + def test_init_calls_sets_carepoint_id(self): + """ It should blank carepoint_id on init """ + res = self._new_importer() + self.assertEqual(None, res.carepoint_id) + + def test_init_calls_sets_carepoint_record(self): + """ It should blank carepoint_record on init """ + res = self._new_importer() + self.assertEqual(None, res.carepoint_record) + + # @TODO: Figure out why no _backend_adapter attr + # def test_get_carepoint_data_read(self): + # """ It should call read on adapter for carepoint id """ + # importer = self._new_importer(self.carepoint_id) + # with self.mock_adapter(importer) as mk: + # importer._get_carepoint_data() + # mk.read.assert_called_once_with(self.carepoint_id) + # + # def test_get_carepoint_data_return(self): + # """ It should return result of adapter read op """ + # importer = self._new_importer(self.carepoint_id) + # with self.mock_adapter(importer) as mk: + # res = importer._get_carepoint_data() + # self.assertEqual(mk.read(), res) + + def test_is_current_assert_record(self): + """ It should assert that a carepoint_record is set """ + with self.assertRaises(AssertionError): + self._new_importer()._is_current(None) + + def test_is_current_no_chg_date(self): + """ It should return None when no chg_date present on record """ + res = self._new_importer(carepoint_record={'chg_date': False}) + res = res._is_current(True) + self.assertEqual(None, res) + + def test_is_current_no_binding(self): + """ It should return None when no binding was provided """ + res = self._new_importer(carepoint_record=self.carepoint_record) + res = res._is_current(False) + self.assertEqual(None, res) + + def test_is_current_no_sync_date(self): + """ It should return None when no sync_date in binding """ + rec_id = self._new_record(None) + res = self._new_importer(carepoint_record=self.carepoint_record) + res = res._is_current(rec_id) + self.assertEqual(None, res) + + def test_is_current_should_sync(self): + """ It should return True when Carepoint is older than Binding """ + rec_id = self._new_record(fields.Datetime.now()) + res = self._new_importer(carepoint_record=self.carepoint_record) + res = res._is_current(rec_id) + self.assertTrue(res) + + def test_is_current_should_not_sync(self): + """ It should return False when Carepoint is newer than Binding """ + rec_id = self._new_record() + res = self._new_importer(carepoint_record=self.carepoint_record) + res = res._is_current(rec_id) + self.assertFalse(res) + + def test_import_dependency_no_carepoint_id(self): + """ It should return None when no carepoint_id supplied """ + res = self._new_importer()._import_dependency(False, True) + self.assertEqual(None, res) + + def test_import_dependency_gets_binder(self): + """ It should get binder for binding_model """ + importer = self._new_importer() + with mock_base_importer(importer): + importer.binder_for.side_effect = EndTestException + with self.assertRaises(EndTestException): + importer._import_dependency(True, self.model) + importer.binder_for.assert_called_once_with(self.model) + + def test_import_dependency_always(self): + """ It should always proceed to import if always is True """ + importer = self._new_importer() + with mock_base_importer(importer): + importer.unit_for.side_effect = EndTestException + with self.assertRaises(EndTestException): + importer._import_dependency( + True, self.model, always=True + ) + importer.binder_for.to_odoo.assert_not_called() + + def test_import_dependency_no_odoo_binder(self): + """ It should attempt to get odoo for binder if not always """ + importer = self._new_importer() + with mock_base_importer(importer): + importer._import_dependency( + self.carepoint_id, self.model, + ) + importer.binder_for().to_odoo.assert_called_once_with( + self.carepoint_id + ) + + def test_import_dependency_gets_unit_default(self): + """ It should get proper importer unit w/ default Importer """ + importer = self._new_importer() + with mock_base_importer(importer): + importer.unit_for.side_effect = EndTestException + with self.assertRaises(EndTestException): + importer._import_dependency( + True, self.model, always=True + ) + importer.unit_for.assert_called_once_with( + self.Importer, model=self.model, + ) + + def test_import_dependency_gets_unit_defined(self): + """ It should get proper importer unit w/ defined Importer """ + expect = mock.MagicMock() + importer = self._new_importer() + with mock_base_importer(importer): + importer.unit_for.side_effect = EndTestException + with self.assertRaises(EndTestException): + importer._import_dependency( + True, self.model, importer_class=expect, always=True + ) + importer.unit_for.assert_called_once_with( + expect, model=self.model, + ) + + def test_import_dependency_runs_import(self): + """ It should run importer w/ proper args """ + importer = self._new_importer() + with mock_base_importer(importer): + importer._import_dependency( + self.carepoint_id, self.model, always=True + ) + importer.unit_for().run.assert_called_once_with( + self.carepoint_id + ) + + def test_import_dependencies_none(self): + """ It should return None on base class """ + res = self._new_importer()._import_dependencies() + self.assertEqual(None, res) + + def test_map_data_call(self): + """ It should get map record w/ proper args """ + importer = self._new_importer(carepoint_record=self.carepoint_record) + with mock_base_importer(importer, ['_mapper']): + importer._map_data() + importer.mapper.map_record.assert_called_once_with( + self.carepoint_record + ) + + def test_map_data_return(self): + """ It should return data mapper """ + importer = self._new_importer(carepoint_record=self.carepoint_record) + with mock_base_importer(importer, ['_mapper']): + res = importer._map_data() + self.assertEqual(importer.mapper.map_record(), res) + + def test_validate_data_none(self): + """ It should return None on base class """ + res = self._new_importer()._validate_data(True) + self.assertEqual(None, res) + + def test_must_skip_none(self): + """ It should return None on base class """ + res = self._new_importer()._must_skip() + self.assertEqual(None, res) + + def test_get_binding_call(self): + """ It should get binding w/ proper args """ + importer = self._new_importer(self.carepoint_id) + with mock_base_importer(importer): + importer._get_binding() + importer.binder.to_odoo.assert_called_once_with( + self.carepoint_id, unwrap=False, browse=True, + ) + + def test_get_binding_return(self): + """ It should return resulting binding """ + importer = self._new_importer(self.carepoint_id) + with mock_base_importer(importer): + res = importer._get_binding() + self.assertEqual(importer.binder.to_odoo(), res) + + def test_create_data_call(self): + """ It should inject proper vals into map record """ + map_record = mock.MagicMock() + expect = {'test': 123, 'test2': 456} + self._new_importer(self.carepoint_id)._create_data( + map_record, **expect + ) + map_record.values.assert_called_once_with( + for_create=True, **expect + ) + + def test_create_data_return(self): + """ It should inject proper vals into map record """ + map_record = mock.MagicMock() + res = self._new_importer(self.carepoint_id)._create_data(map_record) + self.assertEqual(map_record.values(), res) + + def test_create_validates_data(self): + """ It should validate data """ + expect = 'expect' + importer = self._new_importer() + with mock_base_importer(importer): + importer._validate_data.side_effect = EndTestException + with self.assertRaises(EndTestException): + importer._create(expect) + importer._validate_data.assert_called_once_with(expect) + + def test_create_gets_model_with_context(self): + """ It should get model with context to avoid infinite loop """ + importer = self._new_importer() + with mock_base_importer(importer, ['connector_env']): + importer.connector_env.model.with_context.side_effect = \ + EndTestException + with self.assertRaises(EndTestException): + importer._create(None) + importer.connector_env.model.with_context.assert_called_once_with( + connector_no_export=True, + ) + + def test_create_does_create(self): + """ It should create binding w/ data """ + expect = 'expect' + importer = self._new_importer() + with mock_base_importer(importer, ['connector_env']): + mk = importer.connector_env.model.with_context + mk().create.side_effect = EndTestException + with self.assertRaises(EndTestException): + importer._create(expect) + mk().create.assert_called_once_with(expect) + + def test_create_returns_binding(self): + """ It should return new binding """ + importer = self._new_importer() + with mock_base_importer(importer, ['connector_env']): + res = importer._create(None) + self.assertEqual( + importer.connector_env.model.with_context().create(), res + ) + + def test_update_data_call(self): + """ It should inject proper vals into map record """ + map_record = mock.MagicMock() + expect = {'test': 123, 'test2': 456} + self._new_importer(self.carepoint_id)._update_data( + map_record, **expect + ) + map_record.values.assert_called_once_with(**expect) + + def test_update_data_return(self): + """ It should inject proper vals into map record """ + map_record = mock.MagicMock() + res = self._new_importer(self.carepoint_id)._update_data(map_record) + self.assertEqual(map_record.values(), res) + + def test_update_gets_binding_with_context(self): + """ It should get model with context to avoid infinite loop """ + expect = 'expect' + mk = mock.MagicMock() + importer = self._new_importer() + with mock_base_importer(importer): + mk.with_context.side_effect = EndTestException + with self.assertRaises(EndTestException): + importer._update(mk, expect) + mk.with_context.assert_called_once_with( + connector_no_export=True, + ) + + def test_update_does_write(self): + """ It should update binding w/ data """ + expect = 'expect' + mk = mock.MagicMock() + importer = self._new_importer() + with mock_base_importer(importer): + mk.with_context().write.side_effect = EndTestException + with self.assertRaises(EndTestException): + importer._update(mk, expect) + mk.with_context().write.assert_called_once_with(expect) + + def test_after_import_none(self): + """ It should return None on base class """ + res = self._new_importer()._after_import(None) + self.assertEqual(None, res) + + def test_run_sets_carepoint_id(self): + """ It should set carepoint_id on importer """ + importer = self._new_importer() + with mock_base_importer(importer): + importer._get_carepoint_data.side_effect = EndTestException + with self.assertRaises(EndTestException): + importer.run(self.carepoint_id) + self.assertEqual(self.carepoint_id, importer.carepoint_id) + + # def test_run_sets_carepoint_record(self): + # """ It should set carepoint_id on importer """ + # with mock_base_importer() as mk: + # with mock.patch('%s.__builtin__.format' % model) as fmt: + # fmt.side_effect = EndTestException + # importer = self._new_importer() + # with self.assertRaises(EndTestException): + # importer.run(self.carepoint_id) + # self.assertEqual( + # mk._get_carepoint_data(), importer.carepoint_record, + # ) + # + # def test_run_proper_format(self): + # """ It should properly format import str command """ + # with mock_base_importer(): + # with mock.patch('%s.__builtin__.format' % model) as fmt: + # fmt.side_effect = EndTestException + # importer = self._new_importer() + # with self.assertRaises(EndTestException): + # importer.run(self.carepoint_id) + # fmt.assert_called_once_with( + # importer.backend_record._name, + # importer.backend_record.id, + # importer.model._name, + # self.carepoint_id, + # ) + # + # def test_run_advisory_call(self): + # """ It should call advisory_or_retry w/ import str """ + # with mock_base_importer() as mk: + # with mock.patch('%s.__builtin__.format' % model) as fmt: + # mk.advisory_or_retry.side_effect = EndTestException + # importer = self._new_importer() + # with self.assertRaises(EndTestException): + # importer.run(self.carepoint_id) + # mk.advisory_or_retry.assert_called_once_with(fmt()) + + def test_run_returns_skip_if_skip(self): + """ It should return skip if skip """ + expect = 'expect' + importer = self._new_importer() + with mock_base_importer(importer): + importer._must_skip.return_value = expect + res = importer.run(self.carepoint_id) + self.assertEqual(expect, res) + + def test_run_gets_binding(self): + """ It should get binding """ + importer = self._new_importer() + with mock_base_importer(importer, ['_get_binding']): + importer._must_skip.return_value = False + importer._get_binding.side_effect = EndTestException + with self.assertRaises(EndTestException): + importer.run(self.carepoint_id) + + def test_run_does_force(self): + """ It should not see if binding is current if forced """ + importer = self._new_importer() + with mock_base_importer(importer, ['_before_import', '_is_current']): + importer._must_skip.return_value = False + importer._before_import.side_effect = EndTestException + with self.assertRaises(EndTestException): + importer.run(self.carepoint_id, True) + importer._is_current.assert_not_called() + + def test_run_no_force(self): + """ It should return translated up to date if current """ + importer = self._new_importer() + with mock_base_importer(importer, ['_is_current']): + importer._must_skip.return_value = False + importer._is_current.return_value = True + res = importer.run(self.carepoint_id) + self.assertEqual( + _('Already Up To Date.'), res, + ) + + def test_run_import_depends(self): + """ It should import dependencies first """ + importer = self._new_importer() + with mock_base_importer(importer, ['_import_dependencies']): + importer._must_skip.return_value = False + importer._import_dependencies.side_effect = EndTestException + with self.assertRaises(EndTestException): + importer.run(self.carepoint_id, True) + + def test_run_gets_map_record(self): + """ It should get the map record """ + importer = self._new_importer() + with mock_base_importer(importer, ['_map_data']): + importer._map_data.side_effect = EndTestException + importer._must_skip.return_value = False + with self.assertRaises(EndTestException): + importer.run(self.carepoint_id, True) + + def test_run_updates_binding(self): + """ It should update binding if existing """ + importer = self._new_importer() + with mock_base_importer(importer, ['_map_data']): + importer._map_data.side_effect = EndTestException + importer._must_skip.return_value = False + with self.assertRaises(EndTestException): + importer.run(self.carepoint_id, True) diff --git a/connector_carepoint/tests/test_consumer.py b/connector_carepoint/tests/test_consumer.py new file mode 100644 index 0000000..0de5a59 --- /dev/null +++ b/connector_carepoint/tests/test_consumer.py @@ -0,0 +1,58 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2016 LasLabs Inc. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import mock + +from openerp.addons.connector_carepoint import consumer + +from .common import SetUpCarepointBase + + +mk_file = 'openerp.addons.connector_carepoint.consumer' + + +class TestConsumer(SetUpCarepointBase): + + def setUp(self): + super(TestConsumer, self).setUp() + self.model = 'carepoint.medical.pharmacy' + self.binding_id = self._new_record() + + def _new_record(self): + return self.env[self.model].create({ + 'name': 'Test Pharm', + 'carepoint_id': 1234567, + 'backend_id': self.backend.id, + }) + + def test_delay_export_context_no_export(self): + """ It should not export if context prohibits """ + self.session = mock.MagicMock() + self.session.context = {'connector_no_export': True} + res = consumer.delay_export(self.session, 0, 0, 0) + self.assertEqual(None, res) + + def test_delay_export(self): + """ It should call export_record.delay w/ proper args """ + fields = {'test': 123, 'test2': 456} + expect = [self.session, self.model, self.binding_id] + with mock.patch('%s.export_record' % mk_file) as mk: + consumer.delay_export(*expect, vals=fields) + mk.delay.assert_called_once_with(*expect, fields=fields.keys()) + + def test_delay_export_all_bindings_context_no_export(self): + """ It should not export if context prohibits """ + self.session = mock.MagicMock() + self.session.context = {'connector_no_export': True} + res = consumer.delay_export_all_bindings(self.session, 0, 0, 0) + self.assertEqual(None, res) + + def test_delay_export_all_bindings(self): + """ It should call export_record.delay w/ proper args """ + fields = {'test': 123, 'test2': 456} + send = [self.session, 'medical.pharmacy', self.binding_id.odoo_id.id] + expect = [self.session, self.model, self.binding_id.id] + with mock.patch('%s.export_record' % mk_file) as mk: + consumer.delay_export_all_bindings(*send, vals=fields) + mk.delay.assert_called_once_with(*expect, fields=fields.keys()) diff --git a/connector_carepoint/tests/test_mapper.py b/connector_carepoint/tests/test_mapper.py new file mode 100644 index 0000000..59948b4 --- /dev/null +++ b/connector_carepoint/tests/test_mapper.py @@ -0,0 +1,56 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2016 LasLabs Inc. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +from openerp.addons.connector_carepoint.unit import mapper + +from .common import SetUpCarepointBase + + +class TestMapper(SetUpCarepointBase): + + def setUp(self): + super(TestMapper, self).setUp() + + def _new_record(self): + return { + 'str': ' test test ', + 'float': '123.456', + 'int': '1', + } + + def test_trim(self): + """ It should return a trim modifier """ + modifier = mapper.trim('str') + rec = self._new_record() + res = modifier(False, rec, False) + self.assertEqual( + rec['str'].strip(), res + ) + + def test_trim_and_titleize(self): + """ It should return a trim and title modifier """ + modifier = mapper.trim_and_titleize('str') + rec = self._new_record() + res = modifier(False, rec, False) + self.assertEqual( + rec['str'].strip().title(), res + ) + + def test_to_float(self): + """ It should return a to float modifier """ + modifier = mapper.to_float('float') + rec = self._new_record() + res = modifier(False, rec, False) + self.assertEqual( + float(rec['float']), res + ) + + def test_to_int(self): + """ It should return a to int modifier """ + modifier = mapper.to_int('int') + rec = self._new_record() + res = modifier(False, rec, False) + self.assertEqual( + int(rec['int']), res + ) diff --git a/connector_carepoint/tests/test_related_action.py b/connector_carepoint/tests/test_related_action.py new file mode 100644 index 0000000..9a384c4 --- /dev/null +++ b/connector_carepoint/tests/test_related_action.py @@ -0,0 +1,110 @@ +# -*- coding: utf-8 -*- +# Copyright 2015-2016 LasLabs Inc. +# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). + +import mock +from contextlib import contextmanager + +from openerp import _ + +from openerp.addons.connector_carepoint import related_action + +from .common import SetUpCarepointBase + + +mk_file = 'openerp.addons.connector_carepoint.related_action' + + +@contextmanager +def mock_connector_env(): + with mock.patch('%s.ConnectorEnvironment' % mk_file) as env: + yield env + + +class StopTestException(Exception): + pass + + +class TestRelatedAction(SetUpCarepointBase): + + def setUp(self): + super(TestRelatedAction, self).setUp() + self.model = 'carepoint.medical.pharmacy' + self.binding_id = self._new_record() + self.job = mock.MagicMock() + self.job.args = [self.model, self.binding_id.id] + + def _new_record(self): + return self.env[self.model].create({ + 'name': 'Test Pharm', + 'carepoint_id': 1234567, + 'backend_id': self.backend.id, + }) + + def test_unwrap_binding_no_binding(self): + """ It should return None when no binding available """ + self.binding_id.unlink() + res = related_action.unwrap_binding(self.session, self.job) + self.assertEqual(None, res) + + def test_unwrap_binding_gets_correct_env(self): + """ It should init the ConnectorEnv w/ proper args """ + with mock_connector_env() as env: + env.side_effect = StopTestException + with self.assertRaises(StopTestException): + related_action.unwrap_binding(self.session, self.job) + env.assert_called_once_with( + self.binding_id.backend_id, self.session, self.model, + ) + + def test_unwrap_binding_gets_connector_unit(self): + """ It should get the connector_unit w/ proper args """ + expect = 'expect' + with mock_connector_env() as env: + env().get_connector_unit.side_effect = StopTestException + with self.assertRaises(StopTestException): + related_action.unwrap_binding( + self.session, self.job, binder_class=expect + ) + env().get_connector_unit.assert_called_once_with(expect) + + def test_unwrap_binding_unwraps_model(self): + """ It should unwrap model from binder """ + with mock_connector_env() as env: + binder = env().get_connector_unit() + binder.unwrap_model.side_effect = StopTestException + with self.assertRaises(StopTestException): + related_action.unwrap_binding(self.session, self.job) + + def test_unwrap_binding_unwraps_binding(self): + """ It should call unwrap_binding on binder w/ proper args """ + with mock_connector_env() as env: + binder = env().get_connector_unit() + binder.unwrap_binding.side_effect = StopTestException + with self.assertRaises(StopTestException): + related_action.unwrap_binding(self.session, self.job) + binder.unwrap_binding.assert_called_once_with(self.binding_id.id) + + def test_unwrap_binding_guards_value_error(self): + """ It should use binding record when value error on wrap """ + with mock_connector_env() as env: + binder = env().get_connector_unit() + binder.unwrap_model.side_effect = ValueError + res = related_action.unwrap_binding(self.session, self.job) + self.assertEqual(self.model, res['res_model']) + self.assertEqual(self.binding_id.id, res['res_id']) + + def test_unwrap_binding_return(self): + """ It should return proper action """ + with mock_connector_env() as env: + binder = env().get_connector_unit() + res = related_action.unwrap_binding(self.session, self.job) + expect = { + 'name': _('Related Record'), + 'type': 'ir.actions.act_window', + 'view_type': 'form', + 'view_mode': 'form', + 'res_model': binder.unwrap_model(), + 'res_id': binder.unwrap_binding(), + } + self.assertDictEqual(expect, res) diff --git a/connector_carepoint/unit/__init__.py b/connector_carepoint/unit/__init__.py index 80b2a23..b5ab62d 100644 --- a/connector_carepoint/unit/__init__.py +++ b/connector_carepoint/unit/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from . import backend_adapter diff --git a/connector_carepoint/unit/backend_adapter.py b/connector_carepoint/unit/backend_adapter.py index 92533a4..e5ee2bf 100644 --- a/connector_carepoint/unit/backend_adapter.py +++ b/connector_carepoint/unit/backend_adapter.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from carepoint import Carepoint diff --git a/connector_carepoint/unit/binder.py b/connector_carepoint/unit/binder.py index 465eb4a..15fea7d 100644 --- a/connector_carepoint/unit/binder.py +++ b/connector_carepoint/unit/binder.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). @@ -72,7 +72,9 @@ def to_odoo(self, external_id, unwrap=True, browse=False): ]) if not bindings: return self.model.browse() if browse else None - assert len(bindings) == 1, "Several records found: %s" % (bindings,) + assert len(bindings) == 1, openerp._( + "Several records found: %s" + ) % (bindings) if unwrap: return bindings.odoo_id if browse else bindings.odoo_id.id else: @@ -106,7 +108,7 @@ def to_backend(self, record_id, wrap=True): if not record: record = self.model.browse(record_id) assert record - return record.carepoint_id + return record.id def bind(self, external_id, binding_id): """ Create the link between an external ID and an Odoo ID and @@ -117,7 +119,7 @@ def bind(self, external_id, binding_id): """ # the external ID can be 0 on Carepoint! Prevent False values # like False, None, or "", but not 0. - assert (external_id or external_id == 0) and binding_id, ( + assert (external_id or external_id is 0) and binding_id, ( "external_id or binding_id missing, " "got: %s, %s" % (external_id, binding_id) ) diff --git a/connector_carepoint/unit/delete_synchronizer.py b/connector_carepoint/unit/delete_synchronizer.py index 74f0639..195493c 100644 --- a/connector_carepoint/unit/delete_synchronizer.py +++ b/connector_carepoint/unit/delete_synchronizer.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). diff --git a/connector_carepoint/unit/export_synchronizer.py b/connector_carepoint/unit/export_synchronizer.py index 6a77fa4..a173c4d 100644 --- a/connector_carepoint/unit/export_synchronizer.py +++ b/connector_carepoint/unit/export_synchronizer.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). import logging @@ -42,6 +42,7 @@ def __init__(self, connector_env): super(CarepointBaseExporter, self).__init__(connector_env) self.binding_id = None self.carepoint_id = None + self.binding_record = None def _delay_import(self): """ Schedule an import of the record. @@ -84,15 +85,17 @@ def run(self, binding_id, *args, **kwargs): """ Run the synchronization :param binding_id: identifier of the binding record to export """ + self.binding_id = binding_id self.binding_record = self._get_odoo_data() - self.carepoint_id = self.binder.to_backend(self.binding_id) + try: should_import = self._should_import() except IDMissingInBackend: self.carepoint_id = None should_import = False + if should_import: self._delay_import() @@ -114,19 +117,12 @@ def _run(self): def _after_export(self): """ Can do several actions after exporting a record on carepoint """ + return class CarepointExporter(CarepointBaseExporter): """ A common flow for the exports to Carepoint """ - def __init__(self, connector_env): - """ - :param connector_env: current environment (backend, session, ...) - :type connector_env: :class:`connector.connector.ConnectorEnvironment` - """ - super(CarepointExporter, self).__init__(connector_env) - self.binding_record = None - def _lock(self): """ Lock the binding record. Lock the binding record so we are sure that only one export @@ -186,7 +182,9 @@ def _retry_unique_violation(self): def _export_dependency(self, relation, binding_model, exporter_class=None, binding_field='carepoint_bind_ids', - binding_extra_vals=None): + binding_extra_vals=None, + force=False, + ): """ Export a dependency. The exporter class is a subclass of ``CarepointExporter``. If a more precise class need to be defined, it can be passed to the ``exporter_class`` keyword argument. @@ -216,9 +214,11 @@ class or parent class to use for the export. It is used only when the relation is not a binding but is a normal record. :type binding_field: str | unicode - :binding_extra_vals: In case we want to create a new binding + :param binding_extra_vals: In case we want to create a new binding pass extra values for this binding :type binding_extra_vals: dict + :param force: Trigger export workflow even if record exists + :type force: bool """ if not relation: return @@ -269,7 +269,7 @@ class or parent class to use for the export. # If wrap is True, relation is already a binding record. binding = relation - if not rel_binder.to_backend(binding, wrap=False): + if force or not rel_binder.to_backend(binding, wrap=False): exporter = self.unit_for(exporter_class, model=binding_model) exporter.run(binding.id) @@ -283,19 +283,6 @@ def _map_data(self): """ return self.mapper.map_record(self.binding_record) - def _validate_data(self, data): - """ Check if the values to import are correct - Kept for retro-compatibility. To remove in 8.0 - Pro-actively check before the ``Model.create`` or ``Model.update`` - if some fields are missing or invalid - Raise `InvalidDataError` - """ - _logger.warning('Deprecated: _validate_data is deprecated ' - 'in favor of validate_create_data() ' - 'and validate_update_data()') - self._validate_create_data(data) - self._validate_update_data(data) - def _validate_create_data(self, data): """ Check if the values to import are correct Pro-actively check before the ``Model.create`` if some fields diff --git a/connector_carepoint/unit/import_synchronizer.py b/connector_carepoint/unit/import_synchronizer.py index b6b76e9..de16e31 100644 --- a/connector_carepoint/unit/import_synchronizer.py +++ b/connector_carepoint/unit/import_synchronizer.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). @@ -52,7 +52,7 @@ def _before_import(self): """ Hook called before the import, when we have the Carepoint data""" - def _is_uptodate(self, binding): + def _is_current(self, binding): """Return True if the import should be skipped because it is already up-to-date in Odoo""" assert self.carepoint_record @@ -181,7 +181,7 @@ def run(self, carepoint_id, force=False): """ self.carepoint_id = carepoint_id self.carepoint_record = self._get_carepoint_data() - _logger.info('self.carepoint_record - %s', self.carepoint_record) + _logger.debug('self.carepoint_record - %s', self.carepoint_record) lock_name = 'import({}, {}, {}, {})'.format( self.backend_record._name, self.backend_record.id, @@ -197,8 +197,8 @@ def run(self, carepoint_id, force=False): binding = self._get_binding() - if not force and self._is_uptodate(binding): - return _('Already up-to-date.') + if not force and self._is_current(binding): + return _('Already Up To Date.') self._before_import() # import the missing linked resources @@ -227,8 +227,13 @@ class BatchImporter(Importer): def run(self, filters=None): """ Run the synchronization """ - record_ids = self.backend_adapter.search(filters) + if filters is None: + filters = {} + record_ids = self.backend_adapter.search(**filters) + _logger.info('Search for carepoint companies %s returned %s\n', + filters, record_ids) for record_id in record_ids: + _logger.info('In record loop with %s', record_id) self._import_record(record_id) def _import_record(self, record_id): @@ -268,57 +273,9 @@ class SimpleRecordImporter(CarepointImporter): """ Import one Carepoint Store """ _model_name = [ 'carepoint.store', - # 'carepoint.res.partner.category', ] -@carepoint -class TranslationImporter(Importer): - """ Import translations for a record. - Usually called from importers, in ``_after_import``. - For instance from the products and products' categories importers. - """ - - _model_name = [] - - def _get_carepoint_data(self, storeview_id=None): - """ Return the raw Carepoint data for ``self.carepoint_id`` """ - return self.backend_adapter.read(self.carepoint_id, storeview_id) - - def run(self, carepoint_id, binding_id, mapper_class=None): - self.carepoint_id = carepoint_id - storeviews = self.env['carepoint.storeview'].search( - [('backend_id', '=', self.backend_record.id)] - ) - default_lang = self.backend_record.default_lang_id - lang_storeviews = [sv for sv in storeviews - if sv.lang_id and sv.lang_id != default_lang] - if not lang_storeviews: - return - - # find the translatable fields of the model - fields = self.model.fields_get() - translatable_fields = [field for field, attrs in fields.iteritems() - if attrs.get('translate')] - - if mapper_class is None: - mapper = self.mapper - else: - mapper = self.unit_for(mapper_class) - - binding = self.model.browse(binding_id) - for storeview in lang_storeviews: - lang_record = self._get_carepoint_data(storeview.carepoint_id) - map_record = mapper.map_record(lang_record) - record = map_record.values() - - data = dict((field, value) for field, value in record.iteritems() - if field in translatable_fields) - - binding.with_context(connector_no_export=True, - lang=storeview.lang_id.code).write(data) - - @carepoint class AddCheckpoint(ConnectorUnit): """ Add a connector.checkpoint on the underlying model diff --git a/connector_carepoint/unit/mapper.py b/connector_carepoint/unit/mapper.py index 5dfb5b0..e438219 100644 --- a/connector_carepoint/unit/mapper.py +++ b/connector_carepoint/unit/mapper.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# © 2015 LasLabs Inc. +# Copyright 2015-2016 LasLabs Inc. # License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). from openerp.addons.connector.unit.mapper import (mapping, @@ -9,23 +9,6 @@ ) -def to_ord(field): - """ A modifier intended to be used on the ``direct`` mappings. - Convert a string to reversible ord representation (pads the zeros) - Example:: - direct = [(to_ord('source'), 'target')] - :param field: name of the source field in the record - """ - - def modifier(self, record, to_attr): - value = record.get(field) - if not value: - return None - ords = ['%03d' % ord(c) for c in value] - return ''.join(ords) - return modifier - - def trim(field): """ A modifier intended to be used on the ``direct`` mappings. Trim whitespace from field value