Sébastien BEAU
5 years ago
committed by
David Beal
11 changed files with 20 additions and 445 deletions
-
3attachment_synchronize/__manifest__.py
-
95attachment_synchronize/demo/task_demo.xml
-
4attachment_synchronize/models/attachment.py
-
20attachment_synchronize/models/task.py
-
4attachment_synchronize/tests/__init__.py
-
32attachment_synchronize/tests/common.py
-
74attachment_synchronize/tests/mock_server.py
-
50attachment_synchronize/tests/test_filestore.py
-
86attachment_synchronize/tests/test_ftp.py
-
85attachment_synchronize/tests/test_sftp.py
-
12attachment_synchronize/views/attachment_view.xml
@ -1,95 +0,0 @@ |
|||||
<?xml version="1.0" encoding="UTF-8"?> |
|
||||
<odoo noupdate="1"> |
|
||||
|
|
||||
<record id="location_ftp" model="external.file.location"> |
|
||||
<field name="name">TEST FTP</field> |
|
||||
<field name="protocol">ftp</field> |
|
||||
<field name="address">my-ftp-address</field> |
|
||||
<field name="login">my-ftp-user</field> |
|
||||
<field name="password">my-ftp-password</field> |
|
||||
<field name="port">21</field> |
|
||||
</record> |
|
||||
<record id="location_sftp" model="external.file.location"> |
|
||||
<field name="name">TEST SFTP</field> |
|
||||
<field name="protocol">sftp</field> |
|
||||
<field name="address">my-sftp-address</field> |
|
||||
<field name="login">my-sftp-user</field> |
|
||||
<field name="password">my-sftp-password</field> |
|
||||
<field name="port">22</field> |
|
||||
</record> |
|
||||
<record id="location_filestore" model="external.file.location"> |
|
||||
<field name="name">TEST File Store</field> |
|
||||
<field name="protocol">file_store</field> |
|
||||
<field name="filestore_rootpath">/</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="ftp_import_task" model="external.file.task"> |
|
||||
<field name="method_type">import</field> |
|
||||
<field name="location_id" eval="ref('external_file_location.location_ftp')"/> |
|
||||
<field name="filename">test-import-ftp.txt</field> |
|
||||
<field name="filepath">/home/user/test</field> |
|
||||
<field name="name">Import FTP Task</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="ftp_export_task" model="external.file.task"> |
|
||||
<field name="method_type">export</field> |
|
||||
<field name="location_id" eval="ref('external_file_location.location_ftp')"/> |
|
||||
<field name="filepath">/home/user/test</field> |
|
||||
<field name="name">Export FTP Task</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="sftp_import_task" model="external.file.task"> |
|
||||
<field name="method_type">import</field> |
|
||||
<field name="location_id" eval="ref('external_file_location.location_sftp')"/> |
|
||||
<field name="filename">test-import-sftp.txt</field> |
|
||||
<field name="filepath">/home/user/test</field> |
|
||||
<field name="name">Import SFTP Task</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="sftp_export_task" model="external.file.task"> |
|
||||
<field name="method_type">export</field> |
|
||||
<field name="location_id" eval="ref('external_file_location.location_sftp')"/> |
|
||||
<field name="filepath">/home/user/test</field> |
|
||||
<field name="name">Export SFTP Task</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="filestore_import_task" model="external.file.task"> |
|
||||
<field name="method_type">import</field> |
|
||||
<field name="location_id" eval="ref('external_file_location.location_filestore')"/> |
|
||||
<field name="filename">test-import-filestore.txt</field> |
|
||||
<field name="filepath">/home/user/test</field> |
|
||||
<field name="name">Import filestore Task</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="filestore_export_task" model="external.file.task"> |
|
||||
<field name="method_type">export</field> |
|
||||
<field name="location_id" eval="ref('external_file_location.location_filestore')"/> |
|
||||
<field name="filepath">/home/user/test</field> |
|
||||
<field name="name">Export filestore Task</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="ir_attachment_export_file_sftp" model="ir.attachment.metadata"> |
|
||||
<field name="name">Sftp text export file</field> |
|
||||
<field name="datas">dGVzdCBzZnRwIGZpbGUgZXhwb3J0</field> |
|
||||
<field name="datas_fname">sftp_test_export.txt</field> |
|
||||
<field name="task_id" eval="ref('external_file_location.sftp_export_task')"/> |
|
||||
<field name="file_type">export_external_location</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="ir_attachment_export_file_ftp" model="ir.attachment.metadata"> |
|
||||
<field name="name">ftp text export file</field> |
|
||||
<field name="datas">dGVzdCBmdHAgZmlsZSBleHBvcnQ=</field> |
|
||||
<field name="datas_fname">ftp_test_export.txt</field> |
|
||||
<field name="task_id" eval="ref('external_file_location.ftp_export_task')"/> |
|
||||
<field name="file_type">export_external_location</field> |
|
||||
</record> |
|
||||
|
|
||||
<record id="ir_attachment_export_file_filestore" model="ir.attachment.metadata"> |
|
||||
<field name="name">filestore text export file</field> |
|
||||
<field name="datas">dGVzdCBmaWxlc3RvcmUgZmlsZSBleHBvcnQ=</field> |
|
||||
<field name="datas_fname">filestore_test_export.txt</field> |
|
||||
<field name="task_id" eval="ref('external_file_location.filestore_export_task')"/> |
|
||||
<field name="file_type">export_external_location</field> |
|
||||
</record> |
|
||||
|
|
||||
</odoo> |
|
@ -1,4 +0,0 @@ |
|||||
from . import mock_server |
|
||||
from . import test_ftp |
|
||||
from . import test_sftp |
|
||||
from . import test_filestore |
|
@ -1,32 +0,0 @@ |
|||||
# coding: utf-8 |
|
||||
# @ 2016 Florian da Costa @ Akretion |
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|
||||
import openerp.tests.common as common |
|
||||
from openerp import api |
|
||||
from StringIO import StringIO |
|
||||
|
|
||||
|
|
||||
class ContextualStringIO(StringIO): |
|
||||
""" |
|
||||
snippet from http://bit.ly/1HfH6uW (stackoverflow) |
|
||||
""" |
|
||||
|
|
||||
def __enter__(self): |
|
||||
return self |
|
||||
|
|
||||
def __exit__(self, *args): |
|
||||
self.close() |
|
||||
return False |
|
||||
|
|
||||
|
|
||||
class TestConnection(common.TransactionCase): |
|
||||
|
|
||||
def setUp(self): |
|
||||
super(TestConnection, self).setUp() |
|
||||
self.registry.enter_test_mode() |
|
||||
self.env = api.Environment(self.registry.test_cr, self.env.uid, |
|
||||
self.env.context) |
|
||||
|
|
||||
def tearDown(self): |
|
||||
self.registry.leave_test_mode() |
|
||||
super(TestConnection, self).tearDown() |
|
@ -1,74 +0,0 @@ |
|||||
# coding: utf-8 |
|
||||
# Copyright (C) 2014 initOS GmbH & Co. KG (<http://www.initos.com>). |
|
||||
# @ 2015 Valentin CHEMIERE @ Akretion |
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|
||||
|
|
||||
import mock |
|
||||
from contextlib import contextmanager |
|
||||
from collections import defaultdict |
|
||||
|
|
||||
|
|
||||
class MultiResponse(dict): |
|
||||
pass |
|
||||
|
|
||||
|
|
||||
class ConnMock(object): |
|
||||
|
|
||||
def __init__(self, response): |
|
||||
self.response = response |
|
||||
self._calls = [] |
|
||||
self.call_count = defaultdict(int) |
|
||||
|
|
||||
def __getattribute__(self, method): |
|
||||
if method not in ('_calls', 'response', 'call_count'): |
|
||||
def callable(*args, **kwargs): |
|
||||
self._calls.append({ |
|
||||
'method': method, |
|
||||
'args': args, |
|
||||
'kwargs': kwargs, |
|
||||
}) |
|
||||
call = self.response[method] |
|
||||
if isinstance(call, MultiResponse): |
|
||||
call = call[self.call_count[method]] |
|
||||
self.call_count[method] += 1 |
|
||||
return call |
|
||||
|
|
||||
return callable |
|
||||
else: |
|
||||
return super(ConnMock, self).__getattribute__(method) |
|
||||
|
|
||||
def __call__(self, *args, **kwargs): |
|
||||
return self |
|
||||
|
|
||||
def __enter__(self, *args, **kwargs): |
|
||||
return self |
|
||||
|
|
||||
def __exit__(self, *args, **kwargs): |
|
||||
pass |
|
||||
|
|
||||
def __repr__(self, *args, **kwargs): |
|
||||
return self |
|
||||
|
|
||||
def __getitem__(self, key): |
|
||||
return |
|
||||
|
|
||||
|
|
||||
@contextmanager |
|
||||
def server_mock_sftp(response): |
|
||||
with mock.patch('openerp.addons.external_file_location.tasks.sftp.' |
|
||||
'SftpTask', ConnMock(response)) as SFTPFS: |
|
||||
yield SFTPFS._calls |
|
||||
|
|
||||
|
|
||||
@contextmanager |
|
||||
def server_mock_ftp(response): |
|
||||
with mock.patch('openerp.addons.external_file_location.tasks.ftp.' |
|
||||
'FtpTask', ConnMock(response)) as FTPFS: |
|
||||
yield FTPFS._calls |
|
||||
|
|
||||
|
|
||||
@contextmanager |
|
||||
def server_mock_filestore(response): |
|
||||
with mock.patch('openerp.addons.external_file_location.tasks.filestore.' |
|
||||
'FileStoreTask', ConnMock(response)) as FTPFS: |
|
||||
yield FTPFS._calls |
|
@ -1,50 +0,0 @@ |
|||||
# coding: utf-8 |
|
||||
# @ 2015 Valentin CHEMIERE @ Akretion |
|
||||
# ©2016 @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com> |
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|
||||
import logging |
|
||||
from base64 import b64decode |
|
||||
from .common import TestConnection, ContextualStringIO |
|
||||
from .mock_server import server_mock_filestore |
|
||||
|
|
||||
|
|
||||
_logger = logging.getLogger(__name__) |
|
||||
|
|
||||
|
|
||||
class TestfilestoreConnection(TestConnection): |
|
||||
|
|
||||
def setUp(self): |
|
||||
super(TestfilestoreConnection, self).setUp() |
|
||||
self.test_file_filestore = ContextualStringIO() |
|
||||
self.test_file_filestore.write('import filestore') |
|
||||
self.test_file_filestore.seek(0) |
|
||||
|
|
||||
def test_00_filestore_import(self): |
|
||||
self.task = self.env.ref( |
|
||||
'external_file_location.filestore_import_task') |
|
||||
with server_mock_filestore( |
|
||||
{'open': self.test_file_filestore, |
|
||||
'listdir': ['test-import-filestore.txt']}): |
|
||||
self.task.run_import() |
|
||||
search_file = self.env['ir.attachment.metadata'].search( |
|
||||
[('name', '=', 'test-import-filestore.txt')]) |
|
||||
self.assertEqual(len(search_file), 1) |
|
||||
self.assertEqual(b64decode(search_file[0].datas), 'import filestore') |
|
||||
|
|
||||
def test_01_filestore_export(self): |
|
||||
self.task = self.env.ref( |
|
||||
'external_file_location.filestore_export_task') |
|
||||
self.filestore_attachment = self.env.ref( |
|
||||
'external_file_location.ir_attachment_export_file_filestore') |
|
||||
with server_mock_filestore( |
|
||||
{'setcontents': ''}) as Fakefilestore: |
|
||||
self.task.run_export() |
|
||||
if Fakefilestore: |
|
||||
self.assertEqual('setcontents', Fakefilestore[-1]['method']) |
|
||||
self.assertEqual('done', self.filestore_attachment.state) |
|
||||
self.assertEqual( |
|
||||
'/home/user/test/filestore_test_export.txt', |
|
||||
Fakefilestore[-1]['args'][0]) |
|
||||
self.assertEqual( |
|
||||
'test filestore file export', |
|
||||
Fakefilestore[-1]['kwargs']['data']) |
|
@ -1,86 +0,0 @@ |
|||||
# coding: utf-8 |
|
||||
# @ 2015 Valentin CHEMIERE @ Akretion |
|
||||
# ©2016 @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com> |
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|
||||
import logging |
|
||||
from base64 import b64decode |
|
||||
import hashlib |
|
||||
from .common import TestConnection, ContextualStringIO |
|
||||
from .mock_server import server_mock_ftp |
|
||||
from .mock_server import MultiResponse |
|
||||
from openerp.exceptions import UserError |
|
||||
|
|
||||
|
|
||||
_logger = logging.getLogger(__name__) |
|
||||
|
|
||||
|
|
||||
class TestFtpConnection(TestConnection): |
|
||||
|
|
||||
def setUp(self): |
|
||||
super(TestFtpConnection, self).setUp() |
|
||||
self.test_file_ftp = ContextualStringIO() |
|
||||
self.test_file_ftp.write('import ftp') |
|
||||
self.test_file_ftp.seek(0) |
|
||||
|
|
||||
def test_00_ftp_import(self): |
|
||||
self.task = self.env.ref('external_file_location.ftp_import_task') |
|
||||
with server_mock_ftp( |
|
||||
{'open': self.test_file_ftp, |
|
||||
'listdir': ['test-import-ftp.txt']}): |
|
||||
self.task.run_import() |
|
||||
search_file = self.env['ir.attachment.metadata'].search( |
|
||||
[('name', '=', 'test-import-ftp.txt')]) |
|
||||
self.assertEqual(len(search_file), 1) |
|
||||
self.assertEqual(b64decode(search_file[0].datas), 'import ftp') |
|
||||
|
|
||||
def test_01_ftp_export(self): |
|
||||
self.task = self.env.ref('external_file_location.ftp_export_task') |
|
||||
self.ftp_attachment = self.env.ref( |
|
||||
'external_file_location.ir_attachment_export_file_ftp') |
|
||||
with server_mock_ftp( |
|
||||
{'setcontents': ''}) as FakeFTP: |
|
||||
self.task.run_export() |
|
||||
if FakeFTP: |
|
||||
self.assertEqual('setcontents', FakeFTP[-1]['method']) |
|
||||
self.assertEqual('done', self.ftp_attachment.state) |
|
||||
self.assertEqual( |
|
||||
'/home/user/test/ftp_test_export.txt', |
|
||||
FakeFTP[-1]['args'][0]) |
|
||||
self.assertEqual( |
|
||||
'test ftp file export', |
|
||||
FakeFTP[-1]['kwargs']['data']) |
|
||||
|
|
||||
def test_02_ftp_import_md5(self): |
|
||||
md5_file = ContextualStringIO() |
|
||||
md5_file.write(hashlib.md5('import ftp').hexdigest()) |
|
||||
md5_file.seek(0) |
|
||||
task = self.env.ref('external_file_location.ftp_import_task') |
|
||||
task.md5_check = True |
|
||||
with server_mock_ftp( |
|
||||
{'open': MultiResponse({ |
|
||||
1: md5_file, |
|
||||
0: self.test_file_ftp}), |
|
||||
'listdir': [task.filename]}) as Fakeftp: |
|
||||
task.run_import() |
|
||||
search_file = self.env['ir.attachment.metadata'].search( |
|
||||
(('name', '=', task.filename),)) |
|
||||
self.assertEqual(len(search_file), 1) |
|
||||
self.assertEqual(b64decode(search_file[0].datas), |
|
||||
'import ftp') |
|
||||
self.assertEqual('open', Fakeftp[-1]['method']) |
|
||||
self.assertEqual(hashlib.md5('import ftp').hexdigest(), |
|
||||
search_file.external_hash) |
|
||||
|
|
||||
def test_03_ftp_import_md5_corrupt_file(self): |
|
||||
md5_file = ContextualStringIO() |
|
||||
md5_file.write(hashlib.md5('import test ftp corrupted').hexdigest()) |
|
||||
md5_file.seek(0) |
|
||||
task = self.env.ref('external_file_location.ftp_import_task') |
|
||||
task.md5_check = True |
|
||||
with server_mock_ftp( |
|
||||
{'open': MultiResponse({ |
|
||||
1: md5_file, |
|
||||
0: self.test_file_ftp}), |
|
||||
'listdir': [task.filename]}): |
|
||||
with self.assertRaises(UserError): |
|
||||
task.run_import() |
|
@ -1,85 +0,0 @@ |
|||||
# coding: utf-8 |
|
||||
# @ 2015 Valentin CHEMIERE @ Akretion |
|
||||
# ©2016 @author Mourad EL HADJ MIMOUNE <mourad.elhadj.mimoune@akretion.com> |
|
||||
# License AGPL-3.0 or later (http://www.gnu.org/licenses/agpl.html). |
|
||||
import logging |
|
||||
from base64 import b64decode |
|
||||
import hashlib |
|
||||
from .common import TestConnection, ContextualStringIO |
|
||||
from .mock_server import server_mock_sftp |
|
||||
from .mock_server import MultiResponse |
|
||||
from openerp.exceptions import UserError |
|
||||
|
|
||||
|
|
||||
_logger = logging.getLogger(__name__) |
|
||||
|
|
||||
|
|
||||
class TestSftpConnection(TestConnection): |
|
||||
|
|
||||
def setUp(self): |
|
||||
super(TestSftpConnection, self).setUp() |
|
||||
self.test_file_sftp = ContextualStringIO() |
|
||||
self.test_file_sftp.write('import sftp') |
|
||||
self.test_file_sftp.seek(0) |
|
||||
|
|
||||
def test_00_sftp_import(self): |
|
||||
task = self.env.ref('external_file_location.sftp_import_task') |
|
||||
with server_mock_sftp( |
|
||||
{'open': self.test_file_sftp, |
|
||||
'listdir': [task.filename]}): |
|
||||
task.run_import() |
|
||||
search_file = self.env['ir.attachment.metadata'].search( |
|
||||
[('name', '=', task.filename)]) |
|
||||
self.assertEqual(len(search_file), 1) |
|
||||
self.assertEqual(b64decode(search_file[0].datas), 'import sftp') |
|
||||
|
|
||||
def test_01_sftp_export(self): |
|
||||
self.task = self.env.ref('external_file_location.sftp_export_task') |
|
||||
self.sftp_attachment = self.env.ref( |
|
||||
'external_file_location.ir_attachment_export_file_sftp') |
|
||||
with server_mock_sftp( |
|
||||
{'setcontents': ''}) as FakeSFTP: |
|
||||
self.task.run_export() |
|
||||
if FakeSFTP: |
|
||||
self.assertEqual('setcontents', FakeSFTP[-1]['method']) |
|
||||
self.assertEqual( |
|
||||
'/home/user/test/sftp_test_export.txt', |
|
||||
FakeSFTP[-1]['args'][0]) |
|
||||
self.assertEqual( |
|
||||
'test sftp file export', |
|
||||
FakeSFTP[-1]['kwargs']['data']) |
|
||||
|
|
||||
def test_02_sftp_import_md5(self): |
|
||||
md5_file = ContextualStringIO() |
|
||||
md5_file.write(hashlib.md5('import sftp').hexdigest()) |
|
||||
md5_file.seek(0) |
|
||||
task = self.env.ref('external_file_location.sftp_import_task') |
|
||||
task.md5_check = True |
|
||||
with server_mock_sftp( |
|
||||
{'open': MultiResponse({ |
|
||||
1: md5_file, |
|
||||
0: self.test_file_sftp}), |
|
||||
'listdir': [task.filename]}) as FakeSFTP: |
|
||||
task.run_import() |
|
||||
search_file = self.env['ir.attachment.metadata'].search( |
|
||||
(('name', '=', task.filename),)) |
|
||||
self.assertEqual(len(search_file), 1) |
|
||||
self.assertEqual(b64decode(search_file[0].datas), |
|
||||
'import sftp') |
|
||||
self.assertEqual('open', FakeSFTP[-1]['method']) |
|
||||
self.assertEqual(hashlib.md5('import sftp').hexdigest(), |
|
||||
search_file.external_hash) |
|
||||
|
|
||||
def test_03_sftp_import_md5_corrupt_file(self): |
|
||||
md5_file = ContextualStringIO() |
|
||||
md5_file.write(hashlib.md5('import test sftp corrupted').hexdigest()) |
|
||||
md5_file.seek(0) |
|
||||
task = self.env.ref('external_file_location.sftp_import_task') |
|
||||
task.md5_check = True |
|
||||
with server_mock_sftp( |
|
||||
{'open': MultiResponse({ |
|
||||
1: md5_file, |
|
||||
0: self.test_file_sftp}), |
|
||||
'listdir': [task.filename]}): |
|
||||
with self.assertRaises(UserError): |
|
||||
task.run_import() |
|
Write
Preview
Loading…
Cancel
Save
Reference in new issue