import os
import shutil
import unittest
import zipfile
from mock import patch
from ddt import ddt, data, unpack
from testfixtures import TempDirectory
import activity.activity_PMCDeposit as activity_module
from activity.activity_PMCDeposit import activity_PMCDeposit
import tests.activity.settings_mock as settings_mock
from tests.classes_mock import FakeSMTPServer
from tests.activity.classes_mock import (
    FakeLogger,
    FakeSession,
    FakeStorageContext,
    FakeFTP,
)
import tests.activity.test_activity_data as activity_test_data
import tests.activity.helpers as helpers


@ddt
class TestPMCDeposit(unittest.TestCase):
    def setUp(self):
        fake_logger = FakeLogger()
        self.activity = activity_PMCDeposit(
            settings_mock, fake_logger, None, None, None
        )
        self.activity.make_activity_directories()
        self.test_data_dir = "tests/test_data/pmc/"

        self.do_activity_passes = []

        self.do_activity_passes.append(
            {
                "scenario": "zip file was not downloaded",
                "input_data": {
                    "data": {"document": "does_not_exist.zip"},
                    "run": "test-uuid",
                },
                "pmc_zip_key_names": [],
                "related_article_json": [],
                "expected_zip_filename": None,
                "expected_result": self.activity.ACTIVITY_TEMPORARY_FAILURE,
                "zip_file_names": None,
            }
        )

        self.do_activity_passes.append(
            {
                "scenario": "no previous PMC zip file",
                "input_data": {
                    "data": {"document": "elife-19405-vor-v1-20160802113816.zip"},
                    "run": "test-uuid",
                },
                "pmc_zip_key_names": [],
                "related_article_json": [],
                "expected_zip_filename": "elife-05-19405.zip",
                "expected_result": True,
                "zip_file_names": [
                    "elife-19405-fig1.tif",
                    "elife-19405-inf1.tif",
                    "elife-19405.pdf",
                    "elife-19405.xml",
                ],
            }
        )

        self.do_activity_passes.append(
            {
                "scenario": "one previous PMC zip file",
                "input_data": {
                    "data": {"document": "elife-19405-vor-v1-20160802113816.zip"},
                    "run": "test-uuid",
                },
                "pmc_zip_key_names": ["pmc/zip/elife-05-19405.zip"],
                "related_article_json": [],
                "expected_zip_filename": "elife-05-19405.r1.zip",
                "expected_result": True,
                "zip_file_names": [
                    "elife-19405-fig1.tif",
                    "elife-19405-inf1.tif",
                    "elife-19405.pdf",
                    "elife-19405.xml",
                ],
            }
        )

        self.do_activity_passes.append(
            {
                "scenario": "two previous PMC zip file revisions present",
                "input_data": {
                    "data": {"document": "elife-19405-vor-v1-20160802113816.zip"},
                    "run": "test-uuid",
                },
                "pmc_zip_key_names": [
                    "pmc/zip/elife-05-19405.zip",
                    "pmc/zip/elife-05-19405.r1.zip",
                ],
                "related_article_json": [],
                "expected_zip_filename": "elife-05-19405.r2.zip",
                "expected_result": True,
                "zip_file_names": [
                    "elife-19405-fig1.tif",
                    "elife-19405-inf1.tif",
                    "elife-19405.pdf",
                    "elife-19405.xml",
                ],
            }
        )

        self.do_activity_passes.append(
            {
                "scenario": "retracted article",
                "input_data": {
                    "data": {"document": "elife-19405-vor-v1-20160802113816.zip"},
                    "run": "test-uuid",
                },
                "pmc_zip_key_names": [],
                "related_article_json": [{"type": "retraction"}],
                "expected_zip_filename": "elife-05-19405.zip",
                "expected_result": True,
                "zip_file_names": [
                    "elife-19405-fig1.tif",
                    "elife-19405-inf1.tif",
                    "elife-19405.pdf",
                    "elife-19405.xml",
                ],
            }
        )

    def tearDown(self):
        self.activity.clean_tmp_dir()
        helpers.delete_files_in_folder(
            activity_test_data.ExpandArticle_files_dest_folder, filter_out=[".gitkeep"]
        )

    def zip_file_list(self, zip_file_name):
        file_list = None
        zip_file_path = (
            self.activity.directories.get("ZIP_DIR") + os.sep + zip_file_name
        )
        with zipfile.ZipFile(zip_file_path, "r") as open_zip_file:
            file_list = open_zip_file.namelist()
        return file_list

    @patch.object(activity_PMCDeposit, "clean_tmp_dir")
    @patch("provider.lax_provider.article_related")
    @patch("activity.activity_PMCDeposit.FTP")
    @patch.object(FakeStorageContext, "list_resources")
    @patch("activity.activity_PMCDeposit.storage_context")
    def test_do_activity(
        self,
        fake_storage_context,
        fake_list_resources,
        fake_ftp,
        mock_article_related,
        fake_clean_tmp_dir,
    ):

        fake_ftp.return_value = FakeFTP()
        fake_clean_tmp_dir.return_value = None

        for test_data in self.do_activity_passes:

            fake_storage_context.return_value = FakeStorageContext(
                directory=self.test_data_dir
            )
            fake_list_resources.return_value = test_data["pmc_zip_key_names"]
            mock_article_related.return_value = 200, test_data["related_article_json"]

            success = self.activity.do_activity(test_data["input_data"])

            self.assertEqual(
                success,
                test_data["expected_result"],
                "{value} does not equal {expected}, scenario: {scenario}".format(
                    value=success,
                    expected=test_data["expected_result"],
                    scenario=test_data["scenario"],
                ),
            )
            self.assertEqual(
                self.activity.zip_file_name, test_data["expected_zip_filename"]
            )
            if test_data["zip_file_names"]:
                self.assertEqual(
                    sorted(self.zip_file_list(self.activity.zip_file_name)),
                    sorted(test_data["zip_file_names"]),
                )

    @patch("provider.lax_provider.article_related")
    @patch.object(FakeStorageContext, "list_resources")
    @patch.object(activity_PMCDeposit, "ftp_to_endpoint")
    @patch("activity.activity_PMCDeposit.storage_context")
    def test_do_activity_lax_failure(
        self,
        fake_storage_context,
        fake_ftp_to_endpoint,
        fake_list_resources,
        mock_article_related,
    ):

        test_data = self.do_activity_passes[1]

        fake_storage_context.return_value = FakeStorageContext(
            directory=self.test_data_dir
        )
        fake_list_resources.return_value = test_data["pmc_zip_key_names"]
        fake_ftp_to_endpoint.return_value = None
        mock_article_related.return_value = 404, None

        success = self.activity.do_activity(test_data["input_data"])

        self.assertEqual(self.activity.ACTIVITY_TEMPORARY_FAILURE, success)

    @patch("provider.lax_provider.article_related")
    @patch.object(FakeStorageContext, "list_resources")
    @patch.object(activity_PMCDeposit, "ftp_to_endpoint")
    @patch("activity.activity_PMCDeposit.storage_context")
    def test_do_activity_failed_ftp_to_endpoint(
        self,
        fake_storage_context,
        fake_ftp_to_endpoint,
        fake_list_resources,
        mock_article_related,
    ):

        test_data = self.do_activity_passes[1]

        fake_storage_context.return_value = FakeStorageContext(
            directory=self.test_data_dir
        )
        fake_list_resources.return_value = test_data["pmc_zip_key_names"]
        fake_ftp_to_endpoint.return_value = None
        mock_article_related.return_value = 200, []

        success = self.activity.do_activity(test_data["input_data"])

        self.assertEqual(False, success)

    @patch("provider.lax_provider.article_related")
    @patch.object(activity_module.email_provider, "smtp_connect")
    @patch("activity.activity_PMCDeposit.get_session")
    @patch.object(FakeStorageContext, "list_resources")
    @patch.object(activity_PMCDeposit, "ftp_to_endpoint")
    @patch("activity.activity_PMCDeposit.storage_context")
    def test_do_activity_ftp_to_endpoint_exception(
        self,
        fake_storage_context,
        fake_ftp_to_endpoint,
        fake_list_resources,
        fake_session,
        fake_email_smtp_connect,
        mock_article_related,
    ):

        test_data = self.do_activity_passes[1]
        fake_session.return_value = FakeSession({})
        fake_email_smtp_connect.return_value = FakeSMTPServer(
            self.activity.get_tmp_dir()
        )

        fake_storage_context.return_value = FakeStorageContext(
            directory=self.test_data_dir
        )
        fake_list_resources.return_value = test_data["pmc_zip_key_names"]
        fake_ftp_to_endpoint.side_effect = Exception("An exception")
        mock_article_related.return_value = 200, []

        success = self.activity.do_activity(test_data["input_data"])

        self.assertEqual(self.activity.ACTIVITY_TEMPORARY_FAILURE, success)
        self.assertEqual(
            str(self.activity.logger.logexception),
            (
                "Exception in ftp_to_endpoint sending file elife-05-19405.zip: An exception"
            ),
        )


class TestFtpToEndpoint(unittest.TestCase):
    def setUp(self):
        self.fake_logger = FakeLogger()
        self.activity = activity_PMCDeposit(
            settings_mock, self.fake_logger, None, None, None
        )
        self.test_data_dir = "tests/test_data/pmc/"

    @patch.object(activity_module.FTP, "ftp_connect")
    def test_ftp_to_endpoint_connect_exception(self, fake_ftp_connect):
        fake_ftp_connect.side_effect = Exception("An exception")
        with self.assertRaises(Exception):
            self.activity.ftp_to_endpoint(self.test_data_dir)
        self.assertEqual(
            str(self.activity.logger.logexception),
            ("Exception connecting to FTP server: An exception"),
        )

    @patch.object(activity_module.FTP, "ftp_disconnect")
    @patch.object(activity_module.FTP, "ftp_to_endpoint")
    @patch.object(activity_module.FTP, "ftp_connect")
    def test_ftp_to_endpoint_transfer_exception(
        self, fake_ftp_connect, fake_ftp_to_endpoint, fake_ftp_disconnect
    ):
        fake_ftp_connect.return_value = True
        fake_ftp_disconnect.return_value = True
        fake_ftp_to_endpoint.side_effect = Exception("An exception")
        with self.assertRaises(Exception):
            self.activity.ftp_to_endpoint(self.test_data_dir)
        self.assertEqual(
            str(self.activity.logger.logexception),
            ("Exception in transfer of files by FTP: An exception"),
        )

    @patch.object(activity_module.FTP, "ftp_disconnect")
    @patch.object(activity_module.FTP, "ftp_to_endpoint")
    @patch.object(activity_module.FTP, "ftp_connect")
    def test_ftp_to_endpoint_disconnect_exception(
        self, fake_ftp_connect, fake_ftp_to_endpoint, fake_ftp_disconnect
    ):
        fake_ftp_connect.return_value = True
        fake_ftp_to_endpoint.return_value = True
        fake_ftp_disconnect.side_effect = Exception("An exception")
        with self.assertRaises(Exception):
            self.activity.ftp_to_endpoint(self.test_data_dir)
        self.assertEqual(
            str(self.activity.logger.logexception),
            ("Exception disconnecting from FTP server: An exception"),
        )


@ddt
class TestArticleXMLFile(unittest.TestCase):
    @patch("provider.article_processing.file_list")
    @data(
        (["folder_name/elife-36842-v2.xml"], "folder_name/elife-36842-v2.xml"),
        (
            ["folder_name/elife-36842-supp9-v2.xml", "folder_name/elife-36842-v2.xml"],
            "folder_name/elife-36842-v2.xml",
        ),
        (["folder_name/not-an-xml-file.txt"], None),
    )
    @unpack
    def test_article_xml_file(self, list_of_files, expected, fake_file_list):
        fake_file_list.return_value = list_of_files
        xml_search_folders = ["folder_name"]
        self.assertEqual(activity_module.article_xml_file(xml_search_folders), expected)


class TestAlterXML(unittest.TestCase):
    def setUp(self):
        self.logger = FakeLogger()

    def tearDown(self):
        TempDirectory.cleanup_all()

    def test_alter_xml_unchanged(self):
        "test altering a file where no changes will be made, output is the same as input"
        directory = TempDirectory()
        filename = "elife-00353-v1.xml"
        source_file = "tests/files_source/%s" % filename
        test_file = os.path.join(directory.path, filename)
        shutil.copy(source_file, test_file)
        activity_module.alter_xml(test_file, self.logger)
        with open(source_file, "r") as open_file:
            with open(test_file, "r") as open_output_file:
                self.assertEqual(open_file.read(), open_output_file.read())

    def test_alter_xml(self):
        "test an example XML"
        directory = TempDirectory()
        xml_declaration = """<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD with MathML3 v1.2 20190208//EN"  "JATS-archivearticle1-mathml3.dtd">"""
        xml_string = (
            """%s<article xmlns:xlink="http://www.w3.org/1999/xlink">
<sub-article>
<front-stub>
<related-object id="sa0ro1" link-type="continued-by" object-id="10.1101/2021.02.28.433255" object-id-type="id" xlink:href="https://sciety.org/articles/activity/10.1101/2021.02.28.433255"/>
</front-stub>
</sub-article>
</article>"""
            % xml_declaration
        )
        expected = (
            """%s<article xmlns:xlink="http://www.w3.org/1999/xlink">
<sub-article>
<front-stub>
<ext-link ext-link-type="uri" id="sa0ro1" xlink:href="https://sciety.org/articles/activity/10.1101/2021.02.28.433255"/>
</front-stub>
</sub-article>
</article>"""
            % xml_declaration
        )
        filename = "elife-99999-v1.xml"
        test_file = os.path.join(directory.path, filename)
        with open(test_file, "w") as open_file:
            open_file.write(xml_string)
        activity_module.alter_xml(test_file, self.logger)
        with open(test_file, "r") as open_file:
            self.assertEqual(open_file.read(), expected)
        self.assertEqual(
            self.logger.loginfo[-1],
            "Converting related-object tag to ext-link tag in sub-article",
        )

import os
import shutil
import unittest
import zipfile
from mock import patch
from ddt import ddt, data, unpack
from testfixtures import TempDirectory
import activity.activity_PMCDeposit as activity_module
from activity.activity_PMCDeposit import activity_PMCDeposit
import tests.activity.settings_mock as settings_mock
from tests.classes_mock import FakeSMTPServer
from tests.activity.classes_mock import (
    FakeLogger,
    FakeSession,
    FakeStorageContext,
    FakeFTP,
)
import tests.activity.test_activity_data as activity_test_data
import tests.activity.helpers as helpers


@ddt
class TestPMCDeposit(unittest.TestCase):
    def setUp(self):
        fake_logger = FakeLogger()
        self.activity = activity_PMCDeposit(
            settings_mock, fake_logger, None, None, None
        )
        self.activity.make_activity_directories()
        self.test_data_dir = "tests/test_data/pmc/"

        self.do_activity_passes = []

        self.do_activity_passes.append(
            {
                "scenario": "zip file was not downloaded",
                "input_data": {
                    "data": {"document": "does_not_exist.zip"},
                    "run": "test-uuid",
                },
                "pmc_zip_key_names": [],
                "related_article_json": [],
                "expected_zip_filename": None,
                "expected_result": self.activity.ACTIVITY_TEMPORARY_FAILURE,
                "zip_file_names": None,
            }
        )

        self.do_activity_passes.append(
            {
                "scenario": "no previous PMC zip file",
                "input_data": {
                    "data": {"document": "elife-19405-vor-v1-20160802113816.zip"},
                    "run": "test-uuid",
                },
                "pmc_zip_key_names": [],
                "related_article_json": [],
                "expected_zip_filename": "elife-05-19405.zip",
                "expected_result": True,
                "zip_file_names": [
                    "elife-19405-fig1.tif",
                    "elife-19405-inf1.tif",
                    "elife-19405.pdf",
                    "elife-19405.xml",
                ],
            }
        )

        self.do_activity_passes.append(
            {
                "scenario": "one previous PMC zip file",
                "input_data": {
                    "data": {"document": "elife-19405-vor-v1-20160802113816.zip"},
                    "run": "test-uuid",
                },
                "pmc_zip_key_names": ["pmc/zip/elife-05-19405.zip"],
                "related_article_json": [],
                "expected_zip_filename": "elife-05-19405.r1.zip",
                "expected_result": True,
                "zip_file_names": [
                    "elife-19405-fig1.tif",
                    "elife-19405-inf1.tif",
                    "elife-19405.pdf",
                    "elife-19405.xml",
                ],
            }
        )

        self.do_activity_passes.append(
            {
                "scenario": "two previous PMC zip file revisions present",
                "input_data": {
                    "data": {"document": "elife-19405-vor-v1-20160802113816.zip"},
                    "run": "test-uuid",
                },
                "pmc_zip_key_names": [
                    "pmc/zip/elife-05-19405.zip",
                    "pmc/zip/elife-05-19405.r1.zip",
                ],
                "related_article_json": [],
                "expected_zip_filename": "elife-05-19405.r2.zip",
                "expected_result": True,
                "zip_file_names": [
                    "elife-19405-fig1.tif",
                    "elife-19405-inf1.tif",
                    "elife-19405.pdf",
                    "elife-19405.xml",
                ],
            }
        )

        self.do_activity_passes.append(
            {
                "scenario": "retracted article",
                "input_data": {
                    "data": {"document": "elife-19405-vor-v1-20160802113816.zip"},
                    "run": "test-uuid",
                },
                "pmc_zip_key_names": [],
                "related_article_json": [{"type": "retraction"}],
                "expected_zip_filename": "elife-05-19405.zip",
                "expected_result": True,
                "zip_file_names": [
                    "elife-19405-fig1.tif",
                    "elife-19405-inf1.tif",
                    "elife-19405.pdf",
                    "elife-19405.xml",
                ],
            }
        )

    def tearDown(self):
        self.activity.clean_tmp_dir()
        helpers.delete_files_in_folder(
            activity_test_data.ExpandArticle_files_dest_folder, filter_out=[".gitkeep"]
        )

    def zip_file_list(self, zip_file_name):
        file_list = None
        zip_file_path = (
            self.activity.directories.get("ZIP_DIR") + os.sep + zip_file_name
        )
        with zipfile.ZipFile(zip_file_path, "r") as open_zip_file:
            file_list = open_zip_file.namelist()
        return file_list

    @patch.object(activity_PMCDeposit, "clean_tmp_dir")
    @patch("provider.lax_provider.article_related")
    @patch("activity.activity_PMCDeposit.FTP")
    @patch.object(FakeStorageContext, "list_resources")
    @patch("activity.activity_PMCDeposit.storage_context")
    def test_do_activity(
        self,
        fake_storage_context,
        fake_list_resources,
        fake_ftp,
        mock_article_related,
        fake_clean_tmp_dir,
    ):

        fake_ftp.return_value = FakeFTP()
        fake_clean_tmp_dir.return_value = None

        for test_data in self.do_activity_passes:

            fake_storage_context.return_value = FakeStorageContext(
                directory=self.test_data_dir
            )
            fake_list_resources.return_value = test_data["pmc_zip_key_names"]
            mock_article_related.return_value = 200, test_data["related_article_json"]

            success = self.activity.do_activity(test_data["input_data"])

            self.assertEqual(
                success,
                test_data["expected_result"],
                "{value} does not equal {expected}, scenario: {scenario}".format(
                    value=success,
                    expected=test_data["expected_result"],
                    scenario=test_data["scenario"],
                ),
            )
            self.assertEqual(
                self.activity.zip_file_name, test_data["expected_zip_filename"]
            )
            if test_data["zip_file_names"]:
                self.assertEqual(
                    sorted(self.zip_file_list(self.activity.zip_file_name)),
                    sorted(test_data["zip_file_names"]),
                )

    @patch("provider.lax_provider.article_related")
    @patch.object(FakeStorageContext, "list_resources")
    @patch.object(activity_PMCDeposit, "ftp_to_endpoint")
    @patch("activity.activity_PMCDeposit.storage_context")
    def test_do_activity_lax_failure(
        self,
        fake_storage_context,
        fake_ftp_to_endpoint,
        fake_list_resources,
        mock_article_related,
    ):

        test_data = self.do_activity_passes[1]

        fake_storage_context.return_value = FakeStorageContext(
            directory=self.test_data_dir
        )
        fake_list_resources.return_value = test_data["pmc_zip_key_names"]
        fake_ftp_to_endpoint.return_value = None
        mock_article_related.return_value = 404, None

        success = self.activity.do_activity(test_data["input_data"])

        self.assertEqual(self.activity.ACTIVITY_TEMPORARY_FAILURE, success)

    @patch("provider.lax_provider.article_related")
    @patch.object(FakeStorageContext, "list_resources")
    @patch.object(activity_PMCDeposit, "ftp_to_endpoint")
    @patch("activity.activity_PMCDeposit.storage_context")
    def test_do_activity_failed_ftp_to_endpoint(
        self,
        fake_storage_context,
        fake_ftp_to_endpoint,
        fake_list_resources,
        mock_article_related,
    ):

        test_data = self.do_activity_passes[1]

        fake_storage_context.return_value = FakeStorageContext(
            directory=self.test_data_dir
        )
        fake_list_resources.return_value = test_data["pmc_zip_key_names"]
        fake_ftp_to_endpoint.return_value = None
        mock_article_related.return_value = 200, []

        success = self.activity.do_activity(test_data["input_data"])

        self.assertEqual(False, success)

    @patch("provider.lax_provider.article_related")
    @patch.object(activity_module.email_provider, "smtp_connect")
    @patch("activity.activity_PMCDeposit.get_session")
    @patch.object(FakeStorageContext, "list_resources")
    @patch.object(activity_PMCDeposit, "ftp_to_endpoint")
    @patch("activity.activity_PMCDeposit.storage_context")
    def test_do_activity_ftp_to_endpoint_exception(
        self,
        fake_storage_context,
        fake_ftp_to_endpoint,
        fake_list_resources,
        fake_session,
        fake_email_smtp_connect,
        mock_article_related,
    ):

        test_data = self.do_activity_passes[1]
        fake_session.return_value = FakeSession({})
        fake_email_smtp_connect.return_value = FakeSMTPServer(
            self.activity.get_tmp_dir()
        )

        fake_storage_context.return_value = FakeStorageContext(
            directory=self.test_data_dir
        )
        fake_list_resources.return_value = test_data["pmc_zip_key_names"]
        fake_ftp_to_endpoint.side_effect = Exception("An exception")
        mock_article_related.return_value = 200, []

        success = self.activity.do_activity(test_data["input_data"])

        self.assertEqual(self.activity.ACTIVITY_TEMPORARY_FAILURE, success)
        self.assertEqual(
            str(self.activity.logger.logexception),
            (
                "Exception in ftp_to_endpoint sending file elife-05-19405.zip: An exception"
            ),
        )


class TestFtpToEndpoint(unittest.TestCase):
    def setUp(self):
        self.fake_logger = FakeLogger()
        self.activity = activity_PMCDeposit(
            settings_mock, self.fake_logger, None, None, None
        )
        self.test_data_dir = "tests/test_data/pmc/"

    @patch.object(activity_module.FTP, "ftp_connect")
    def test_ftp_to_endpoint_connect_exception(self, fake_ftp_connect):
        fake_ftp_connect.side_effect = Exception("An exception")
        with self.assertRaises(Exception):
            self.activity.ftp_to_endpoint(self.test_data_dir)
        self.assertEqual(
            str(self.activity.logger.logexception),
            ("Exception connecting to FTP server: An exception"),
        )

    @patch.object(activity_module.FTP, "ftp_disconnect")
    @patch.object(activity_module.FTP, "ftp_to_endpoint")
    @patch.object(activity_module.FTP, "ftp_connect")
    def test_ftp_to_endpoint_transfer_exception(
        self, fake_ftp_connect, fake_ftp_to_endpoint, fake_ftp_disconnect
    ):
        fake_ftp_connect.return_value = True
        fake_ftp_disconnect.return_value = True
        fake_ftp_to_endpoint.side_effect = Exception("An exception")
        with self.assertRaises(Exception):
            self.activity.ftp_to_endpoint(self.test_data_dir)
        self.assertEqual(
            str(self.activity.logger.logexception),
            ("Exception in transfer of files by FTP: An exception"),
        )

    @patch.object(activity_module.FTP, "ftp_disconnect")
    @patch.object(activity_module.FTP, "ftp_to_endpoint")
    @patch.object(activity_module.FTP, "ftp_connect")
    def test_ftp_to_endpoint_disconnect_exception(
        self, fake_ftp_connect, fake_ftp_to_endpoint, fake_ftp_disconnect
    ):
        fake_ftp_connect.return_value = True
        fake_ftp_to_endpoint.return_value = True
        fake_ftp_disconnect.side_effect = Exception("An exception")
        with self.assertRaises(Exception):
            self.activity.ftp_to_endpoint(self.test_data_dir)
        self.assertEqual(
            str(self.activity.logger.logexception),
            ("Exception disconnecting from FTP server: An exception"),
        )


@ddt
class TestArticleXMLFile(unittest.TestCase):
    @patch("provider.article_processing.file_list")
    @data(
        (["folder_name/elife-36842-v2.xml"], "folder_name/elife-36842-v2.xml"),
        (
            ["folder_name/elife-36842-supp9-v2.xml", "folder_name/elife-36842-v2.xml"],
            "folder_name/elife-36842-v2.xml",
        ),
        (["folder_name/not-an-xml-file.txt"], None),
    )
    @unpack
    def test_article_xml_file(self, list_of_files, expected, fake_file_list):
        fake_file_list.return_value = list_of_files
        xml_search_folders = ["folder_name"]
        self.assertEqual(activity_module.article_xml_file(xml_search_folders), expected)


class TestAlterXML(unittest.TestCase):
    def setUp(self):
        self.logger = FakeLogger()

    def tearDown(self):
        TempDirectory.cleanup_all()

    def test_alter_xml_unchanged(self):
        "test altering a file where no changes will be made, output is the same as input"
        directory = TempDirectory()
        filename = "elife-00353-v1.xml"
        source_file = "tests/files_source/%s" % filename
        test_file = os.path.join(directory.path, filename)
        shutil.copy(source_file, test_file)
        activity_module.alter_xml(test_file, self.logger)
        with open(source_file, "r") as open_file:
            with open(test_file, "r") as open_output_file:
                self.assertEqual(open_file.read(), open_output_file.read())

    def test_alter_xml(self):
        "test an example XML"
        directory = TempDirectory()
        xml_declaration = """<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD with MathML3 v1.2 20190208//EN"  "JATS-archivearticle1-mathml3.dtd">"""
        xml_string = (
            """%s<article xmlns:xlink="http://www.w3.org/1999/xlink">
<sub-article>
<front-stub>
<related-object id="sa0ro1" link-type="continued-by" object-id="10.1101/2021.02.28.433255" object-id-type="id" xlink:href="https://sciety.org/articles/activity/10.1101/2021.02.28.433255"/>
</front-stub>
</sub-article>
</article>"""
            % xml_declaration
        )
        expected = (
            """%s<article xmlns:xlink="http://www.w3.org/1999/xlink">
<sub-article>
<front-stub>
<ext-link ext-link-type="uri" id="sa0ro1" xlink:href="https://sciety.org/articles/activity/10.1101/2021.02.28.433255"/>
</front-stub>
</sub-article>
</article>"""
            % xml_declaration
        )
        filename = "elife-99999-v1.xml"
        test_file = os.path.join(directory.path, filename)
        with open(test_file, "w") as open_file:
            open_file.write(xml_string)
        activity_module.alter_xml(test_file, self.logger)
        with open(test_file, "r") as open_file:
            self.assertEqual(open_file.read(), expected)
        self.assertEqual(
            self.logger.loginfo[-1],
            "Converting related-object tag to ext-link tag in sub-article",
        )

<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE article PUBLIC "-//NLM//DTD JATS (Z39.96) Journal Archiving and Interchange DTD with MathML3 v1.2 20190208//EN"  "JATS-archivearticle1-mathml3.dtd"> %s<article xmlns:xlink="http://www.w3.org/1999/xlink"> <sub-article> <front-stub> <related-object id="sa0ro1" link-type="continued-by" object-id="10.1101/2021.02.28.433255" object-id-type="id" xlink:href="https://sciety.org/articles/activity/10.1101/2021.02.28.433255"/> </front-stub> </sub-article> </article> %s<article xmlns:xlink="http://www.w3.org/1999/xlink"> <sub-article> <front-stub> <ext-link ext-link-type="uri" id="sa0ro1" xlink:href="https://sciety.org/articles/activity/10.1101/2021.02.28.433255"/> </front-stub> </sub-article> </article>