def test_it_should_raise_exception_for_an_inexistent_config_value_without_specify_a_default_value(self):
     config = Config()
     config.put("some_key", "some_value")
     try:
         config.get("ANOTHER_KEY")
     except Exception, e:
         self.assertEqual("invalid key ('another_key')", str(e))
 def test_it_should_not_update_saved_config_values(self):
     config = Config()
     config.put("some_key", "some_value")
     try:
         config.put("some_key", "another_value")
     except Exception, e:
         self.assertEqual("the configuration key 'some_key' already exists and you cannot override any configuration", str(e))
 def test_it_should_raise_exception_when_removing_an_inexistent_config_value(self):
     config = Config()
     config.put("some_key", "some_value")
     try:
         config.remove("ANOTHER_KEY")
     except Exception, e:
         self.assertEqual("invalid configuration key ('another_key')", str(e))
 def test_it_should_transform_keys_to_lower_case(self):
     config = Config()
     config.put("sOmE_kEy", "original_value")
     self.assertEqual("original_value", config.get("SoMe_KeY"))
     config.update("sOMe_kEy", "new_value")
     self.assertEqual("new_value", config.get("some_KEY"))
     config.remove("SOME_KEY")
     self.assertRaises(Exception, config.get, "sOMe_KEY")
 def test_it_should_parse_migrations_dir_with_multiple_relative_dirs(self):
     dirs = Config._parse_migrations_dir('test:migrations:./a/relative/path:another/path')
     self.assertEqual(4, len(dirs))
     self.assertEqual(os.path.abspath('test'), dirs[0])
     self.assertEqual(os.path.abspath('migrations'), dirs[1])
     self.assertEqual(os.path.abspath('./a/relative/path'), dirs[2])
     self.assertEqual(os.path.abspath('another/path'), dirs[3])
 def test_it_should_parse_migrations_dir_with_mixed_relative_and_absolute_dirs(self):
     dirs = Config._parse_migrations_dir('%s:%s:%s:%s' % ('/tmp/test', '.', './a/relative/path', os.path.abspath('another/path')))
     self.assertEqual(4, len(dirs))
     self.assertEqual('/tmp/test', dirs[0])
     self.assertEqual(os.path.abspath('.'), dirs[1])
     self.assertEqual(os.path.abspath('./a/relative/path'), dirs[2])
     self.assertEqual(os.path.abspath('another/path'), dirs[3])
 def test_it_should_parse_migrations_dir_with_relative_dirs_using_config_dir_parameter_as_base_path(self):
     dirs = Config._parse_migrations_dir(
             '%s:%s:%s:%s' % ('/tmp/test', '.', './a/relative/path', os.path.abspath('another/path')),
             config_dir='/base/path_to_relative_dirs'
     )
     self.assertEqual(4, len(dirs))
     self.assertEqual('/tmp/test', dirs[0])
     self.assertEqual('/base/path_to_relative_dirs', dirs[1])
     self.assertEqual('/base/path_to_relative_dirs/a/relative/path', dirs[2])
     self.assertEqual(os.path.abspath('another/path'), dirs[3])
 def test_it_should_accept_initial_values_as_configuration(self):
     config = Config({"some_key": "some_value"})
     self.assertEqual("some_value", config.get("some_key"))
 def test_it_should_remove_saved_config_values(self):
     config = Config()
     config.put("some_key", "some_value")
     initial = str(config)
     config.remove("some_key")
     self.assertNotEqual(initial, str(config))
 def test_it_should_parse_migrations_dir_with_one_relative_dir(self):
     dirs = Config._parse_migrations_dir('.')
     self.assertEqual(1, len(dirs))
     self.assertEqual(os.path.abspath('.'), dirs[0])
 def test_it_should_accept_non_empty_stringand_false_as_default_value(self):
     _dict = {"some_key": "some_value"}
     self.assertEqual(None, Config._get(_dict,"ANOTHER_KEY", None))
     self.assertEqual("", Config._get(_dict,"ANOTHER_KEY", ""))
     self.assertEqual(False, Config._get(_dict,"ANOTHER_KEY", False))
 def test_it_should_return_default_value_for_an_inexistent_dict_value(self):
     _dict = {"some_key": "some_value"}
     self.assertEqual("default_value", Config._get(_dict, "ANOTHER_KEY", "default_value"))
 def test_it_should_raise_exception_for_an_inexistent_dict_value_without_specify_a_default_value(self):
     _dict = {"some_key": "some_value"}
     try:
         Config._get(_dict, "ANOTHER_KEY")
     except Exception, e:
         self.assertEqual("invalid key ('ANOTHER_KEY')", str(e))
 def test_it_should_return_previous_saved_config_values(self):
     config = Config()
     config.put("some_key", "some_value")
     self.assertEqual("some_value", config.get("some_key"))
 def test_it_should_return_value_from_a_dict(self):
     _dict = {"some_key": "some_value"}
     self.assertEqual("some_value", Config._get(_dict, "some_key"))
class VirtuosoTest(BaseTest):

    def setUp(self):
        super(VirtuosoTest, self).setUp()
        self.config = Config()
        self.config.put("database_migrations_dir", ".")
        self.config.put("database_ontology", "test.ttl")
        self.config.put("database_graph", "test")
        self.config.put("database_host", "localhost")
        self.config.put("database_user", "user")
        self.config.put("database_password", "password")
        self.config.put("database_port", 9999)
        self.config.put("database_endpoint", "endpoint")
        self.config.put("host_user", "host-user")
        self.config.put("host_password", "host-passwd")
        self.config.put("virtuoso_dirs_allowed", "/tmp")
        self.config.put("migration_graph", "http://example.com/")
        create_file("test.ttl", "")

        self.data_ttl_content = """
@prefix : <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
<http://example.com/John> rdf:type <http://example.com/Person>.
"""

        create_file("data.ttl", self.data_ttl_content)

        self.structure_01_ttl_content = """
@prefix : <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

:Actor rdf:type owl:Class .
:SoapOpera rdf:type owl:Class .
"""

        create_file("structure_01.ttl", self.structure_01_ttl_content)

        self.structure_02_ttl_content = """
@prefix : <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

:Actor rdf:type owl:Class .
:SoapOpera rdf:type owl:Class .
:RoleOnSoapOpera rdf:type owl:Class .

:role rdf:type owl:Class ;
                rdfs:subClassOf [
                    rdf:type owl:Restriction ;
                    owl:onProperty :play_a_role ;
                    owl:onClass :RoleOnSoapOpera ;
                    owl:minQualifiedCardinality "1"^^xsd:nonNegativeInteger ;
                    owl:maxQualifiedCardinality "1"^^xsd:nonNegativeInteger
                ] .
"""

        create_file("structure_02.ttl", self.structure_02_ttl_content)

        self.structure_03_ttl_content = """
@prefix : <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

:Actor rdf:type owl:Class .
:SoapOpera rdf:type owl:Class .
:RoleOnSoapOpera rdf:type owl:Class .

:role rdf:type owl:Class ;
                rdfs:subClassOf [
                    rdf:type owl:Restriction ;
                    owl:onProperty :play_a_role ;
                    owl:onClass :RoleOnSoapOpera ;
                    owl:minQualifiedCardinality "1111"^^xsd:nonNegativeInteger 
                ] ,
                [
                    rdf:type owl:Restriction ;
                    owl:onProperty :play_a_role ;
                    owl:onClass :RoleOnSoapOpera ;
                    owl:maxQualifiedCardinality "3333"^^xsd:nonNegativeInteger
                ] .
"""

        create_file("structure_03.ttl", self.structure_03_ttl_content)

        self.structure_04_ttl_content = """
@prefix : <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

:Actor rdf:type owl:Class .
:SoapOpera rdf:type owl:Class .
:RoleOnSoapOpera rdf:type owl:Class .

:role rdf:type owl:Class ;
                rdfs:subClassOf [
                    rdf:type owl:Restriction ;
                    owl:onProperty :play_a_role ;
                    owl:onClass :RoleOnSoapOpera ;
                    owl:minQualifiedCardinality "1"^^xsd:nonNegativeInteger
                ] .
"""

        create_file("structure_04.ttl", self.structure_02_ttl_content)

    def tearDown(self):
        super(VirtuosoTest, self).tearDown()
        delete_files("*.ttl")

#    @patch('subprocess.Popen', return_value=Mock(**{"communicate.return_value": ("out", "err")}))
#    def test_it_should_use_popen_to_run_a_command(self, popen_mock):
#        Virtuoso(self.config).command_call("echo 1")
#        popen_mock.assert_called_with('echo 1', shell=True, stderr=-1, stdout=-1)

#    def test_it_should_return_stdout_and_stderr(self):
#        stdout, _ = Virtuoso(self.config).command_call("echo 'out'")
#        _, stderr = Virtuoso(self.config).command_call("python -V")
#
#        self.assertEqual('out\n', stdout)
#        self.assertEqual('python', stderr.split(' ')[0].lower())

    @patch('subprocess.Popen.communicate', return_value=("", ""))
    @patch('subprocess.Popen.__init__', return_value=None)
    def test_it_should_use_isql_executable_to_send_commands_to_virtuoso(self, popen_mock, communicate_mock):
        virtuoso = Virtuoso(self.config)
        virtuoso._run_isql("command")
        popen_mock.assert_called_with('echo "command" | isql -U user -P password -H localhost -S 9999 -b 1', shell=True, stderr=-1, stdout=-1)

    @patch('subprocess.Popen.communicate', return_value=("", ""))
    @patch('subprocess.Popen.__init__', return_value=None)
    def test_it_should_set_the_command_buffer_size_on_isql_calls_to_support_large_commands(self, popen_mock, communicate_mock):
        virtuoso = Virtuoso(self.config)
        cmd = "0123456789" * 250000
        virtuoso._run_isql(cmd)
        popen_mock.assert_called_with('echo "%s" | isql -U user -P password -H localhost -S 9999 -b 2500' % cmd, shell=True, stderr=-1, stdout=-1)

    @patch('subprocess.Popen.communicate', return_value=("", ""))
    @patch('subprocess.Popen.__init__', return_value=None)
    def test_it_should_use_file_size_as_the_command_buffer_size_on_isql_calls(self, popen_mock, communicate_mock):
        virtuoso = Virtuoso(self.config)
        create_file("big_file.ttl", "0123456789" * 480000)
        virtuoso._run_isql("big_file.ttl", True)
        popen_mock.assert_called_with('isql -U user -P password -H localhost -S 9999 -b 4800 < "big_file.ttl"', shell=True, stderr=-1, stdout=-1)

#    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso.command_call',
#           return_value=('', 'some error'))
#    def test_it_should_raise_error_if_isql_status_return_error(self, command_call_mock):
#        virtuoso = Virtuoso(self.config)
#        self.assertRaisesWithMessage(Exception, 'could not connect to virtuoso: some error', virtuoso.connect)

    @patch('simple_virtuoso_migrate.virtuoso.Utils.write_temporary_file', return_value='filename.ttl')
    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso._run_isql', return_value=('', ''))
    def test_it_should_write_a_file_with_sparql_up_when_executing_change(self, run_isql_mock, write_temporary_file_mock):
        virtuoso = Virtuoso(self.config)
        virtuoso.execute_change("sparql_up", "sparql_down")
        write_temporary_file_mock.assert_called_with("set echo on;\nsparql_up", "file_up")
        run_isql_mock.assert_called_with('filename.ttl', True)

    @patch('simple_virtuoso_migrate.virtuoso.Utils.write_temporary_file', return_value='filename.ttl')
    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso._run_isql', return_value=('', ''))
    def test_it_should_delete_the_temporary_file_with_sparql_up_when_executing_change(self, run_isql_mock, write_temporary_file_mock):
        create_file('filename.ttl', 'content')

        virtuoso = Virtuoso(self.config)
        virtuoso.execute_change("sparql_up", "sparql_down")
        self.assertFalse(os.path.exists('filename.ttl'))

    @patch('simple_virtuoso_migrate.virtuoso.Utils.write_temporary_file', return_value='filename.ttl')
    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso._run_isql', side_effect=Exception("some error"))
    def test_it_should_delete_the_temporary_file_with_sparql_up_when_executing_change_raise_an_error(self, run_isql_mock, write_temporary_file_mock):
        create_file('filename.ttl', 'content')

        virtuoso = Virtuoso(self.config)
        self.assertRaisesWithMessage(Exception, 'some error', virtuoso.execute_change, "sparql_up", "sparql_down")
        self.assertFalse(os.path.exists('filename.ttl'))

    @patch('simple_virtuoso_migrate.virtuoso.Utils.write_temporary_file')
    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso._run_isql')
    def test_it_should_write_a_file_with_sparql_down_when_executing_change_raise_an_error(self, run_isql_mock, write_temporary_file_mock):
        run_isql_mock.side_effect = command_call_side_effect
        write_temporary_file_mock.side_effect = temp_file_side_effect

        virtuoso = Virtuoso(self.config)
        self.assertRaisesWithMessage(MigrationException, '\nerror executing migration statement: err\n\nRollback done successfully!!!', virtuoso.execute_change, "sparql_up", "sparql_down")
        expected_calls = [
            call("set echo on;\nsparql_up", "file_up"),
            call("set echo on;\nsparql_down", "file_down")
        ]
        self.assertEqual(expected_calls, write_temporary_file_mock.mock_calls)

        expected_calls = [
            call('filename_up.ttl', True),
            call('filename_down.ttl', True)
        ]
        self.assertEqual(expected_calls, run_isql_mock.mock_calls)

    @patch('simple_virtuoso_migrate.virtuoso.Utils.write_temporary_file')
    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso._run_isql')
    def test_it_should_delete_the_temporary_file_with_sparql_down_when_executing_change(self, run_isql_mock, write_temporary_file_mock):
        create_file('filename_down.ttl', 'content')
        run_isql_mock.side_effect = command_call_side_effect
        write_temporary_file_mock.side_effect = temp_file_side_effect

        virtuoso = Virtuoso(self.config)
        self.assertRaisesWithMessage(MigrationException, '\nerror executing migration statement: err\n\nRollback done successfully!!!', virtuoso.execute_change, "sparql_up", "sparql_down")
        self.assertFalse(os.path.exists('filename_down.ttl'))

    @patch('simple_virtuoso_migrate.virtuoso.Utils.write_temporary_file')
    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso._run_isql')
    def test_it_should_raise_a_specific_message_when_rollback_fails_when_executing_change(self, run_isql_mock, write_temporary_file_mock):
        def command_call_side_effect(*args):
            if (args[0].find("_up") > 0) or (args[0].find("_down") > 0):
                return ("", "err")
            return ("out", "")

        run_isql_mock.side_effect = command_call_side_effect
        write_temporary_file_mock.side_effect = temp_file_side_effect

        virtuoso = Virtuoso(self.config)
        self.assertRaisesWithMessage(MigrationException, '\nerror executing migration statement: err\n\nRollback done partially: error executing rollback statement: err', virtuoso.execute_change, "sparql_up", "sparql_down")

    @patch('simple_virtuoso_migrate.virtuoso.Utils.write_temporary_file', return_value='filename.ttl')
    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso._run_isql', return_value=('output', ''))
    def test_it_should_log_stdout_when_executing_change(self, run_isql_mock, write_temporary_file_mock):
        execution_log = Mock()
        virtuoso = Virtuoso(self.config)
        virtuoso.execute_change("sparql_up", "sparql_down", execution_log)
        execution_log.assert_called_with("output")

    @patch('simple_virtuoso_migrate.virtuoso.Graph.query')
    @patch('simple_virtuoso_migrate.virtuoso.Graph.namespaces', return_value=['ns0'])
    def test_it_should_get_current_version_none_when_database_is_empty(self, graph_mock, graph_query_mock):
        result_mock = MagicMock()
        result_mock.__iter__.return_value = []
        result_mock.__len__.return_value = 0
        graph_query_mock.return_value = result_mock
        current, source = Virtuoso(self.config).get_current_version()

        graph_query_mock.assert_called_with('prefix owl: <http://www.w3.org/2002/07/owl#>\nprefix xsd: <http://www.w3.org/2001/XMLSchema#>\nselect distinct ?version ?origen\nFROM <http://example.com/>\n{{\nselect distinct ?version ?origen ?data\nFROM <http://example.com/>\nwhere {?s owl:versionInfo ?version;\n<http://example.com/commited> ?data;\n<http://example.com/produto> "test";\n<http://example.com/origen> ?origen.}\nORDER BY desc(?data) LIMIT 1\n}}')

        self.assertIsNone(current)
        self.assertIsNone(source)

    @patch('simple_virtuoso_migrate.virtuoso.Graph.query')
    @patch('simple_virtuoso_migrate.virtuoso.Graph.namespaces', return_value=['ns0'])
    def test_it_should_get_current_version_when_database_is_not_empty(self, graph_mock, graph_query_mock):
        result_mock = MagicMock()
        result_mock.__iter__.return_value = [('2', 'git'), ('1', 'file')]
        result_mock.__len__.return_value = 2
        graph_query_mock.return_value = result_mock

        current, source = Virtuoso(self.config).get_current_version()

        self.assertEqual(['version', 'origen'], result_mock.vars)
        self.assertEqual('2', current)
        self.assertEqual('git', source)

    def test_it_should_get_sparql_statments_from_given_ontology(self):

        query_up, query_down = Virtuoso(self.config).get_sparql(destination_ontology=self.data_ttl_content, insert="data.ttl")

        self.assertEqual('\nSPARQL INSERT INTO <http://example.com/> { [] owl:versionInfo "None"; <http://example.com/endpoint> "endpoint"; <http://example.com/usuario> "user"; <http://example.com/ambiente> "localhost"; <http://example.com/produto> "test"; <http://example.com/commited> "%s"^^xsd:dateTime; <http://example.com/origen> "None"; <http://example.com/inserted> "data.ttl".};' % datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), query_up)
        self.assertEqual('\nSPARQL DELETE FROM <http://example.com/> {?s ?p ?o} WHERE {?s owl:versionInfo "None"; <http://example.com/endpoint> "endpoint"; <http://example.com/usuario> "user"; <http://example.com/ambiente> "localhost"; <http://example.com/produto> "test"; <http://example.com/commited> "%s"^^xsd:dateTime; <http://example.com/origen> "None"; <http://example.com/inserted> "data.ttl"; ?p ?o.};'  % datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S"), query_down)

    def test_it_should_get_sparql_statments_from_given_ontology_when_breaking_a_blank_node_in_two(self):
        query_up, query_down = Virtuoso(self.config).get_sparql(current_ontology=self.structure_02_ttl_content,
                                                                destination_ontology=self.structure_03_ttl_content)

        query_up_lines = [line.strip() for line in query_up.split("\n")[1:]]

        self.assertTrue(len(query_up_lines),3)
        self.assertTrue(query_up_lines[0].startswith("SPARQL DELETE FROM"))
        self.assertTrue(query_up_lines[1].startswith("SPARQL INSERT INTO"))
        self.assertTrue(query_up_lines[2].startswith("SPARQL INSERT INTO"))

        query_down_lines = [line.strip() for line in query_down.split("\n")[1:]]

        self.assertTrue(len(query_down_lines),3)
        self.assertTrue(query_down_lines[0].startswith("SPARQL DELETE FROM"))
        self.assertTrue(query_down_lines[1].startswith("SPARQL DELETE FROM"))
        self.assertTrue(query_down_lines[2].startswith("SPARQL INSERT INTO"))

    def test_generate_migration_sparql_commands_when_only_a_triple_of_an_existing_blank_node_is_deleted(self):
        ttl_before = self.structure_02_ttl_content
        graph_before = ConjunctiveGraph()
        graph_before.parse(data=ttl_before, format='turtle')

        ttl_after = self.structure_04_ttl_content
        graph_after = ConjunctiveGraph()
        graph_after.parse(data=ttl_after, format='turtle')

        virtuoso_ = Virtuoso(self.config)

        query_up, query_down = virtuoso_._generate_migration_sparql_commands(origin_store=graph_after, destination_store=graph_before)
        expected_query_up = u'\nSPARQL INSERT INTO <test> { <http://example.com/role> <http://www.w3.org/2000/01/rdf-schema#subClassOf> [<http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Restriction> ; <http://www.w3.org/2002/07/owl#minQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#integer> ; <http://www.w3.org/2002/07/owl#onClass> <http://example.com/RoleOnSoapOpera> ; <http://www.w3.org/2002/07/owl#onProperty> <http://example.com/play_a_role> ; ] };'
        expected_query_down  = u'\nSPARQL DELETE FROM <test> { <http://example.com/role> <http://www.w3.org/2000/01/rdf-schema#subClassOf>  ?s. ?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Restriction> ; <http://www.w3.org/2002/07/owl#minQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#integer> ; <http://www.w3.org/2002/07/owl#onClass> <http://example.com/RoleOnSoapOpera> ; <http://www.w3.org/2002/07/owl#onProperty> <http://example.com/play_a_role>  } WHERE { <http://example.com/role> <http://www.w3.org/2000/01/rdf-schema#subClassOf>  ?s. ?s <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Restriction> ; <http://www.w3.org/2002/07/owl#minQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#integer> ; <http://www.w3.org/2002/07/owl#onClass> <http://example.com/RoleOnSoapOpera> ; <http://www.w3.org/2002/07/owl#onProperty> <http://example.com/play_a_role>  };'
        self.assertEqual(query_up, expected_query_up)
        self.assertEqual(query_down, expected_query_down)

    def test_it_should_get_sparql_statments_when_forward_migration(self):

        query_up, query_down = Virtuoso(self.config).get_sparql(current_ontology=self.structure_01_ttl_content, destination_ontology=self.structure_02_ttl_content, origen='file', destination_version='02')

        expected_lines_up = ["SPARQL INSERT INTO <test> {<http://example.com/RoleOnSoapOpera> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
                             "SPARQL INSERT INTO <test> {<http://example.com/role> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
                            ]

        expected_log_migration_up = """SPARQL INSERT INTO <http://example.com/> { [] owl:versionInfo "02"; <http://example.com/endpoint> "endpoint"; <http://example.com/usuario> "user"; <http://example.com/ambiente> "localhost"; <http://example.com/produto> "test"; <http://example.com/commited> "%s"^^xsd:dateTime; <http://example.com/origen> "file"; <http://example.com/changes> "\\n<log>".};""" % datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        expected_lines_down = ["SPARQL DELETE FROM <test> {<http://example.com/role> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
                             "SPARQL DELETE FROM <test> {<http://example.com/RoleOnSoapOpera> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
                            ]

        expected_log_migration_down = """SPARQL DELETE FROM <http://example.com/> {?s ?p ?o} WHERE {?s owl:versionInfo "02"; <http://example.com/endpoint> "endpoint"; <http://example.com/usuario> "user"; <http://example.com/ambiente> "localhost"; <http://example.com/produto> "test"; <http://example.com/commited> "%s"^^xsd:dateTime; <http://example.com/origen> "file"; <http://example.com/changes> "\\n<log>"; ?p ?o.};""" % datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        lines_up = query_up.strip(' \t\n\r').splitlines()
        self.assertEqual(4, len(lines_up))
        [self.assertTrue(l in lines_up) for l in expected_lines_up]
        self.assertEqual(lines_up[-1], expected_log_migration_up.replace('<log>', "\n".join(lines_up[0:-1]).replace('"','\\"').replace('\n', '\\n')))

        matchObj = re.search(r"SPARQL INSERT INTO <test> { <http://example.com/role> <http://www.w3.org/2000/01/rdf-schema#subClassOf> \[(.*)\] };", query_up,  re.MULTILINE)
        sub_classes = [c.strip(' \t\n\r') for c in re.split(r" ; | \?s\. \?s ", matchObj.group(1))]
        [self.assertTrue(c in sub_classes) for c in [
            '<http://www.w3.org/2002/07/owl#minQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#integer>',
            '<http://www.w3.org/2002/07/owl#maxQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#integer>',
            '<http://www.w3.org/2002/07/owl#onClass> <http://example.com/RoleOnSoapOpera>',
            '<http://www.w3.org/2002/07/owl#onProperty> <http://example.com/play_a_role>',
            '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Restriction>'
            ]]

        lines_down = query_down.strip(' \t\n\r').splitlines()
        self.assertEqual(4, len(lines_down))
        [self.assertTrue(l in lines_down) for l in expected_lines_down]
        self.assertEqual(lines_down[-1], expected_log_migration_down.replace('<log>', "\n".join(lines_up[0:-1]).replace('"','\\"').replace('\n', '\\n')))

        matchObj = re.search(r"SPARQL DELETE FROM <test> {(.*)} WHERE {(.*)};", query_down,  re.MULTILINE)
        sub_classes_01 = [c.strip(' \t\n\r') for c in re.split(r" ; | \?s\. \?s ", matchObj.group(1))]
        sub_classes_02 = [c.strip(' \t\n\r') for c in re.split(r" ; | \?s\. \?s ", matchObj.group(2))]
        [self.assertTrue((c in sub_classes_01) and (c in sub_classes_02)) for c in [
            '<http://example.com/role> <http://www.w3.org/2000/01/rdf-schema#subClassOf>',
            '<http://www.w3.org/2002/07/owl#minQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#integer>',
            '<http://www.w3.org/2002/07/owl#maxQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#integer>',
            '<http://www.w3.org/2002/07/owl#onClass> <http://example.com/RoleOnSoapOpera>',
            '<http://www.w3.org/2002/07/owl#onProperty> <http://example.com/play_a_role>',
            '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Restriction>'
            ]]


    def test_it_should_get_sparql_statments_when_backward_migration(self):

        query_up, query_down = Virtuoso(self.config).get_sparql(current_ontology=self.structure_02_ttl_content, destination_ontology=self.structure_01_ttl_content, origen='file', destination_version='01')


        expected_lines_up = ["SPARQL DELETE FROM <test> {<http://example.com/role> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
                             "SPARQL DELETE FROM <test> {<http://example.com/RoleOnSoapOpera> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
                            ]

        expected_log_migration_up = """SPARQL INSERT INTO <http://example.com/> { [] owl:versionInfo "01"; <http://example.com/endpoint> "endpoint"; <http://example.com/usuario> "user"; <http://example.com/ambiente> "localhost"; <http://example.com/produto> "test"; <http://example.com/commited> "%s"^^xsd:dateTime; <http://example.com/origen> "file"; <http://example.com/changes> "\\n<log>".};""" % datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        expected_lines_down = ["SPARQL INSERT INTO <test> {<http://example.com/RoleOnSoapOpera> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
                             "SPARQL INSERT INTO <test> {<http://example.com/role> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
                            ]

        expected_log_migration_down = """SPARQL DELETE FROM <http://example.com/> {?s ?p ?o} WHERE {?s owl:versionInfo "01"; <http://example.com/endpoint> "endpoint"; <http://example.com/usuario> "user"; <http://example.com/ambiente> "localhost"; <http://example.com/produto> "test"; <http://example.com/commited> "%s"^^xsd:dateTime; <http://example.com/origen> "file"; <http://example.com/changes> "\\n<log>"; ?p ?o.};""" % datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        """SPARQL INSERT INTO <http://example.com/> { [] owl:versionInfo "01"; <http://example.com/endpoint> "endpoint"; <http://example.com/usuario> "user"; <http://example.com/ambiente> "localhost"; <http://example.com/produto> "test"; <http://example.com/commited> "%s"^^xsd:dateTime; <http://example.com/origen> "file"; <http://example.com/changes> "\\n<log>".};""" % datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        lines_up = query_up.strip(' \t\n\r').splitlines()
        self.assertEqual(4, len(lines_up))
        [self.assertTrue(l in lines_up) for l in expected_lines_up]
        self.assertEqual(lines_up[-1], expected_log_migration_up.replace('<log>', "\n".join(lines_up[0:-1]).replace('"','\\"').replace('\n', '\\n')))

        matchObj = re.search(r"SPARQL DELETE FROM <test> {(.*)} WHERE {(.*)};", query_up,  re.MULTILINE)
        sub_classes_01 = [c.strip(' \t\n\r') for c in re.split(r" ; | \?s\. \?s ", matchObj.group(1))]
        sub_classes_02 = [c.strip(' \t\n\r') for c in re.split(r" ; | \?s\. \?s ", matchObj.group(2))]
        [self.assertTrue((c in sub_classes_01) and (c in sub_classes_02)) for c in [
            '<http://example.com/role> <http://www.w3.org/2000/01/rdf-schema#subClassOf>',
            '<http://www.w3.org/2002/07/owl#minQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#integer>',
            '<http://www.w3.org/2002/07/owl#maxQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#integer>',
            '<http://www.w3.org/2002/07/owl#onClass> <http://example.com/RoleOnSoapOpera>',
            '<http://www.w3.org/2002/07/owl#onProperty> <http://example.com/play_a_role>',
            '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Restriction>'
            ]]


        lines_down = query_down.strip(' \t\n\r').splitlines()
        self.assertEqual(4, len(lines_down))
        [self.assertTrue(l in lines_down) for l in expected_lines_down]
        self.assertEqual(lines_down[-1], expected_log_migration_down.replace('<log>', "\n".join(lines_up[0:-1]).replace('"','\\"').replace('\n', '\\n')))

        matchObj = re.search(r"SPARQL INSERT INTO <test> { <http://example.com/role> <http://www.w3.org/2000/01/rdf-schema#subClassOf> \[(.*)\] };", query_down,  re.MULTILINE)
        sub_classes = [c.strip(' \t\n\r') for c in re.split(r" ; | \?s\. \?s ", matchObj.group(1))]
        [self.assertTrue(c in sub_classes) for c in [
            '<http://www.w3.org/2002/07/owl#minQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#integer>',
            '<http://www.w3.org/2002/07/owl#maxQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#integer>',
            '<http://www.w3.org/2002/07/owl#onClass> <http://example.com/RoleOnSoapOpera>',
            '<http://www.w3.org/2002/07/owl#onProperty> <http://example.com/play_a_role>',
            '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Restriction>'
            ]]

#    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso.get_sparql')
#    def test_it_should_get_statments_to_execute_when_comparing_the_given_file_with_the_current_version(self, get_sparql_mock):
#        Virtuoso(self.config).get_statements("data.ttl", current_version='01', origen='file')
#        get_sparql_mock.assert_called_with(None, self.data_ttl_content, '01', None, 'file', 'data.ttl')

#    def test_it_should_raise_exception_when_getting_statments_of_an_unexistent_ttl_file(self):
#        self.assertRaisesWithMessage(Exception, 'migration file does not exist (current_file.ttl)', Virtuoso(self.config).get_statements, "current_file.ttl", current_version='01', origen='file')

    def test_it_should_raise_exception_if_specified_ontology_does_not_exists_on_migrations_dir(self):
        self.config.update('database_ontology', 'ontology.ttl')
        self.config.update('database_migrations_dir', '.')
        self.assertRaisesWithMessage(Exception, 'migration file does not exist (./ontology.ttl)', Virtuoso(self.config).get_ontology_by_version, '01')

    @patch('simple_virtuoso_migrate.virtuoso.Git')
    def test_it_should_return_git_content(self, git_mock):
        execute_mock = Mock(**{'return_value':'content'})
        git_mock.return_value = Mock(**{'execute':execute_mock})

        content = Virtuoso(self.config).get_ontology_by_version('version')
        self.assertEqual('content', content)
        git_mock.assert_called_with('.')
        execute_mock.assert_called_with(['git', 'show', 'version:test.ttl'])

    def test_it_should_print_error_message_with_correct_encoding(self):
        graph = """
        :is_part_of rdf:type owl:ObjectProperty ;
                     rdfs:label "É parte de outro objeto" ;
                     rdfs:domain absent:Prefix ;
                     rdfs:range absent:Prefix .
        """

        expected_message = 'Error parsing graph at line 2 of <>:\nBad syntax (Prefix ":" not bound) at ^ in:\n"\n        ^:is_part_of rdf:type owl:ObjectProperty ;\n                  ..."'
        self.assertRaisesWithMessage(Exception, expected_message, Virtuoso(self.config).get_sparql, current_ontology=None, destination_ontology=graph)
 def test_it_should_return_default_value_for_an_inexistent_config_value(self):
     config = Config()
     config.put("some_key", "some_value")
     self.assertEqual("default_value", config.get("another_key", "default_value"))
 def test_it_should_update_value_to_a_existing_key_keeping_original_value_if_new_value_is_none_false_or_empty_string(self):
     config = Config()
     config.put("some_key", "original_value")
     config.update("some_key", None)
     self.assertEqual("original_value", config.get("some_key"))
     config.update("some_key", False)
     self.assertEqual("original_value", config.get("some_key"))
     config.update("some_key", "")
     self.assertEqual("original_value", config.get("some_key"))
 def test_it_should_update_value_to_a_existing_key(self):
     config = Config()
     config.put("some_key", "original_value")
     config.update("some_key", "some_value")
     self.assertEqual("some_value", config.get("some_key"))
 def test_it_should_accept_non_empty_string_and_false_as_default_value(self):
     config = Config()
     config.put("some_key", "some_value")
     self.assertEqual(None, config.get("ANOTHER_KEY", None))
     self.assertEqual("", config.get("ANOTHER_KEY", ""))
     self.assertEqual(False, config.get("ANOTHER_KEY", False))
    def setUp(self):
        super(VirtuosoTest, self).setUp()
        self.config = Config()
        self.config.put("database_migrations_dir", ".")
        self.config.put("database_ontology", "test.ttl")
        self.config.put("database_graph", "test")
        self.config.put("database_host", "localhost")
        self.config.put("database_user", "user")
        self.config.put("database_password", "password")
        self.config.put("database_port", 9999)
        self.config.put("database_endpoint", "endpoint")
        self.config.put("host_user", "host-user")
        self.config.put("host_password", "host-passwd")
        self.config.put("virtuoso_dirs_allowed", "/tmp")
        self.config.put("migration_graph", "http://example.com/")
        create_file("test.ttl", "")

        self.data_ttl_content = """
@prefix : <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
<http://example.com/John> rdf:type <http://example.com/Person>.
"""

        create_file("data.ttl", self.data_ttl_content)

        self.structure_01_ttl_content = """
@prefix : <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

:Actor rdf:type owl:Class .
:SoapOpera rdf:type owl:Class .
"""

        create_file("structure_01.ttl", self.structure_01_ttl_content)

        self.structure_02_ttl_content = """
@prefix : <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

:Actor rdf:type owl:Class .
:SoapOpera rdf:type owl:Class .
:RoleOnSoapOpera rdf:type owl:Class .

:role rdf:type owl:Class ;
                rdfs:subClassOf [
                    rdf:type owl:Restriction ;
                    owl:onProperty :play_a_role ;
                    owl:onClass :RoleOnSoapOpera ;
                    owl:minQualifiedCardinality "1"^^xsd:nonNegativeInteger ;
                    owl:maxQualifiedCardinality "1"^^xsd:nonNegativeInteger
                ] .
"""

        create_file("structure_02.ttl", self.structure_02_ttl_content)

        self.structure_03_ttl_content = """
@prefix : <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

:Actor rdf:type owl:Class .
:SoapOpera rdf:type owl:Class .
:RoleOnSoapOpera rdf:type owl:Class .

:role rdf:type owl:Class ;
                rdfs:subClassOf [
                    rdf:type owl:Restriction ;
                    owl:onProperty :play_a_role ;
                    owl:onClass :RoleOnSoapOpera ;
                    owl:minQualifiedCardinality "1111"^^xsd:nonNegativeInteger 
                ] ,
                [
                    rdf:type owl:Restriction ;
                    owl:onProperty :play_a_role ;
                    owl:onClass :RoleOnSoapOpera ;
                    owl:maxQualifiedCardinality "3333"^^xsd:nonNegativeInteger
                ] .
"""

        create_file("structure_03.ttl", self.structure_03_ttl_content)

        self.structure_04_ttl_content = """
@prefix : <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

:Actor rdf:type owl:Class .
:SoapOpera rdf:type owl:Class .
:RoleOnSoapOpera rdf:type owl:Class .

:role rdf:type owl:Class ;
                rdfs:subClassOf [
                    rdf:type owl:Restriction ;
                    owl:onProperty :play_a_role ;
                    owl:onClass :RoleOnSoapOpera ;
                    owl:minQualifiedCardinality "1"^^xsd:nonNegativeInteger
                ] .
"""

        create_file("structure_04.ttl", self.structure_02_ttl_content)
class VirtuosoTest(BaseTest):
    maxDiff = None

    def setUp(self):
        super(VirtuosoTest, self).setUp()
        self.config = Config()
        self.config.put("database_migrations_dir", ".")
        self.config.put("database_ontology", "test.ttl")
        self.config.put("database_graph", "test")
        self.config.put("database_host", "localhost")
        self.config.put("database_user", "user")
        self.config.put("database_password", "password")
        self.config.put("database_port", 9999)
        self.config.put("database_endpoint", "endpoint")
        self.config.put("host_user", "root")
        self.config.put("host_password", "")
        self.config.put("virtuoso_dirs_allowed", ".")
        self.config.put("migration_graph", "http://example.com")
        create_file("test.ttl", "")

        self.data_ttl_content = """
@prefix : <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix foaf: <http://xmlns.com/foaf/0.1/> .
<http://example.com/John> rdf:type <http://example.com/Person>.
"""

        create_file("data.ttl", self.data_ttl_content)

        self.structure_01_ttl_content = """
@prefix : <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

:Actor rdf:type owl:Class .
:SoapOpera rdf:type owl:Class .
"""

        create_file("structure_01.ttl", self.structure_01_ttl_content)

        self.structure_02_ttl_content = """
@prefix : <http://example.com/> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .

:Actor rdf:type owl:Class .
:SoapOpera rdf:type owl:Class .
:RoleOnSoapOpera rdf:type owl:Class .

:role rdf:type owl:Class ;
                rdfs:subClassOf [
                    rdf:type owl:Restriction ;
                    owl:onProperty :play_a_role ;
                    owl:onClass :RoleOnSoapOpera ;
                    owl:minQualifiedCardinality "1"^^xsd:nonNegativeInteger ;
                    owl:maxQualifiedCardinality "1"^^xsd:nonNegativeInteger
                ]."""

        create_file("structure_02.ttl", self.structure_02_ttl_content)

    def tearDown(self):
        super(VirtuosoTest, self).tearDown()
        delete_files("*.ttl")

    #    @patch('subprocess.Popen', return_value=Mock(**{"communicate.return_value": ("out", "err")}))
    #    def test_it_should_use_popen_to_run_a_command(self, popen_mock):
    #        Virtuoso(self.config).command_call("echo 1")
    #        popen_mock.assert_called_with('echo 1', shell=True, stderr=-1, stdout=-1)

    #    def test_it_should_return_stdout_and_stderr(self):
    #        stdout, _ = Virtuoso(self.config).command_call("echo 'out'")
    #        _, stderr = Virtuoso(self.config).command_call("python -V")
    #
    #        self.assertEqual('out\n', stdout)
    #        self.assertEqual('python', stderr.split(' ')[0].lower())

    #    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso.command_call', return_value=('', ''))
    #    def test_it_should_use_isql_executable_to_connect_to_virtuoso(self, command_call_mock):
    #        virtuoso = Virtuoso(self.config)
    #        conn = virtuoso.connect()
    #        self.assertEqual('isql -U user -P password -H localhost -S 9999 < ', conn)
    #        command_call_mock.assert_called_with('echo "status();"| isql -U user -P password -H localhost -S 9999 ')

    #    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso.command_call',
    #           return_value=('', 'some error'))
    #    def test_it_should_raise_error_if_isql_status_return_error(self, command_call_mock):
    #        virtuoso = Virtuoso(self.config)
    #        self.assertRaisesWithMessage(Exception, 'could not connect to virtuoso: some error', virtuoso.connect)

    #    @patch('simple_virtuoso_migrate.virtuoso.Utils.write_temporary_file',
    #           return_value='filename.ttl')
    #    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso.command_call',
    #           return_value=('', ''))
    #    def test_it_should_write_a_file_with_sparql_up_when_executing_change(self, command_call_mock, write_temporary_file_mock):
    #        virtuoso = Virtuoso(self.config)
    #        virtuoso.execute_change("sparql_up", "sparql_down")
    #        write_temporary_file_mock.assert_called_with("set echo on;\nsparql_up",
    #                                                     "file_up")
    #        command_call_mock.assert_called_with('isql -U user -P password -H localhost -S 9999 < filename.ttl')

    #    @patch('simple_virtuoso_migrate.virtuoso.Utils.write_temporary_file',
    #           return_value='filename.ttl')
    #    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso.command_call',
    #           return_value=('', ''))
    #    def test_it_should_delete_the_temporary_file_with_sparql_up_when_executing_change(self, command_call_mock, write_temporary_file_mock):
    #        create_file('filename.ttl', 'content')
    #
    #        virtuoso = Virtuoso(self.config)
    #        virtuoso.execute_change("sparql_up", "sparql_down")
    #        self.assertFalse(os.path.exists('filename.ttl'))
    #
    #    @patch('simple_virtuoso_migrate.virtuoso.Utils.write_temporary_file', return_value='filename.ttl')
    #    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso.command_call', side_effect=Exception("some error"))
    #    def test_it_should_delete_the_temporary_file_with_sparql_up_when_executing_change_raise_an_error(self, command_call_mock, write_temporary_file_mock):
    #        create_file('filename.ttl', 'content')
    #
    #        virtuoso = Virtuoso(self.config)
    #        self.assertRaisesWithMessage(Exception, 'could not connect to virtuoso: some error', virtuoso.execute_change, "sparql_up", "sparql_down")
    #        self.assertFalse(os.path.exists('filename.ttl'))

    #    @patch('simple_virtuoso_migrate.virtuoso.Utils.write_temporary_file')
    #    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso.command_call')
    #    def test_it_should_write_a_file_with_sparql_down_when_executing_change_raise_an_error(self, command_call_mock, write_temporary_file_mock):
    #        command_call_mock.side_effect = command_call_side_effect
    #        write_temporary_file_mock.side_effect = temp_file_side_effect
    #
    #        virtuoso = Virtuoso(self.config)
    #        self.assertRaisesWithMessage(MigrationException, '\nerror executing migration statement: err\n\nRollback done successfully!!!', virtuoso.execute_change, "sparql_up", "sparql_down")
    #        expected_calls = [
    #            call("set echo on;\nsparql_up", "file_up"),
    #            call("set echo on;\nsparql_down", "file_down")
    #        ]
    #        self.assertEqual(expected_calls, write_temporary_file_mock.mock_calls)
    #
    #        expected_calls = [
    #            call('echo "status();"| isql -U user -P password -H localhost -S 9999 '),
    #            call('isql -U user -P password -H localhost -S 9999 < filename_up.ttl'),
    #            call('isql -U user -P password -H localhost -S 9999 < filename_down.ttl')
    #        ]
    #        self.assertEqual(expected_calls, command_call_mock.mock_calls)

    #    @patch('simple_virtuoso_migrate.virtuoso.Utils.write_temporary_file')
    #    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso.command_call')
    #    def test_it_should_delete_the_temporary_file_with_sparql_down_when_executing_change(self, command_call_mock, write_temporary_file_mock):
    #        create_file('filename_down.ttl', 'content')
    #        command_call_mock.side_effect = command_call_side_effect
    #        write_temporary_file_mock.side_effect = temp_file_side_effect
    #
    #        virtuoso = Virtuoso(self.config)
    #        self.assertRaisesWithMessage(MigrationException, '\nerror executing migration statement: err\n\nRollback done successfully!!!', virtuoso.execute_change, "sparql_up", "sparql_down")
    #        self.assertFalse(os.path.exists('filename_down.ttl'))

    #    @patch('simple_virtuoso_migrate.virtuoso.Utils.write_temporary_file')
    #    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso.command_call')
    #    def test_it_should_raise_a_specific_message_when_rollback_fails_when_executing_change(self, command_call_mock, write_temporary_file_mock):
    #        def command_call_side_effect(args):
    #            if (args.find("_up") > 0) or (args.find("_down") > 0):
    #                return ("", "err")
    #            return ("out", "")
    #
    #        command_call_mock.side_effect = command_call_side_effect
    #        write_temporary_file_mock.side_effect = temp_file_side_effect
    #
    #        virtuoso = Virtuoso(self.config)
    #        self.assertRaisesWithMessage(MigrationException, '\nerror executing migration statement: err\n\nRollback done partially: error executing rollback statement: err', virtuoso.execute_change, "sparql_up", "sparql_down")

    #    @patch('simple_virtuoso_migrate.virtuoso.Utils.write_temporary_file', return_value='filename.ttl')
    #    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso.command_call', return_value=('output', ''))
    #    def test_it_should_log_stdout_when_executing_change(self, command_call_mock, write_temporary_file_mock):
    #        execution_log = Mock()
    #        virtuoso = Virtuoso(self.config)
    #        virtuoso.execute_change("sparql_up", "sparql_down", execution_log)
    #        execution_log.assert_called_with("output")

    @patch("simple_virtuoso_migrate.virtuoso.Graph.query")
    @patch("simple_virtuoso_migrate.virtuoso.Graph.namespaces", return_value=["ns0"])
    def test_it_should_get_current_version_none_when_database_is_empty(self, graph_mock, graph_query_mock):
        result_mock = MagicMock()
        result_mock.__iter__.return_value = []
        result_mock.__len__.return_value = 0
        graph_query_mock.return_value = result_mock
        current, source = Virtuoso(self.config).get_current_version()
        self.assertIsNone(current)
        self.assertIsNone(source)

    @patch("simple_virtuoso_migrate.virtuoso.Graph.query")
    @patch("simple_virtuoso_migrate.virtuoso.Graph.namespaces", return_value=["ns0"])
    def test_it_should_get_current_version_when_database_is_not_empty(self, graph_mock, graph_query_mock):
        result_mock = MagicMock()
        result_mock.__iter__.return_value = [("2", "git"), ("1", "file")]
        result_mock.__len__.return_value = 2
        graph_query_mock.return_value = result_mock

        current, source = Virtuoso(self.config).get_current_version()

        self.assertEqual(["version", "origen"], result_mock.vars)
        self.assertEqual("2", current)
        self.assertEqual("git", source)

    def test_it_should_get_sparql_statments_when_forward_migration(self):

        query_up, query_down = Virtuoso(self.config).get_sparql(
            current_ontology=self.structure_01_ttl_content,
            destination_ontology=self.structure_02_ttl_content,
            origen="file",
            destination_version="02",
        )

        expected_lines_up = [
            "SPARQL INSERT INTO <test> {<http://example.com/RoleOnSoapOpera> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
            "SPARQL INSERT INTO <test> {<http://example.com/role> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
        ]

        expected_log_migration_up = """SPARQL INSERT INTO <http://migration.example.com/> {[] owl:versionInfo "02"; <http://migration.example.com/endpoint> "endpoint"; <http://migration.example.com/usuario> "user"; <http://migration.example.com/ambiente> "localhost"; <http://migration.example.com/produto> "test"; <http://migration.example.com/commited> "%s"^^xsd:dateTime; <http://migration.example.com/origen> "file"; <http://migration.example.com/changes> "\\n<log>".};""" % datetime.datetime.now().strftime(
            "%Y-%m-%d %H:%M:%S"
        )

        expected_lines_down = [
            "SPARQL DELETE FROM <test> {<http://example.com/role> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
            "SPARQL DELETE FROM <test> {<http://example.com/RoleOnSoapOpera> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
        ]

        expected_log_migration_down = """SPARQL DELETE FROM <http://migration.example.com/> {?s ?p ?o} WHERE {?s owl:versionInfo "02"; <http://migration.example.com/endpoint> "endpoint"; <http://migration.example.com/usuario> "user"; <http://migration.example.com/ambiente> "localhost"; <http://migration.example.com/produto> "test"; <http://migration.example.com/commited> "%s"^^xsd:dateTime; <http://migration.example.com/origen> "file"; <http://migration.example.com/changes> "\\n<log>"; ?p ?o.};""" % datetime.datetime.now().strftime(
            "%Y-%m-%d %H:%M:%S"
        )

        lines_up = query_up.strip(" \t\n\r").splitlines()
        self.assertEqual(4, len(lines_up))
        [self.assertTrue(l in lines_up) for l in expected_lines_up]
        #        self.assertEqual(lines_up[-1],
        #                         expected_log_migration_up.replace('<log>', "\n".join(lines_up[0:-1]).replace('"','\"').replace('\n', '\n')))
        #
        #        matchObj = re.search(r"SPARQL INSERT INTO <test> { <http://example.com/role> <http://www.w3.org/2000/01/rdf-schema#subClassOf> \[(.*)\] };", query_up,  re.MULTILINE)
        #        sub_classes = [c.strip(' \t\n\r') for c in re.split(r" ; | \?s\. \?s ", matchObj.group(1))]
        #        [self.assertTrue(c in sub_classes) for c in [
        #            '<http://www.w3.org/2002/07/owl#minQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#nonNegativeInteger>',
        #            '<http://www.w3.org/2002/07/owl#maxQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#nonNegativeInteger>',
        #            '<http://www.w3.org/2002/07/owl#onClass> <http://example.com/RoleOnSoapOpera>',
        #            '<http://www.w3.org/2002/07/owl#onProperty> <http://example.com/play_a_role>',
        #            '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Restriction>'
        #            ]]

        lines_down = query_down.strip(" \t\n\r").splitlines()
        self.assertEqual(4, len(lines_down))
        [self.assertTrue(l in lines_down) for l in expected_lines_down]

    #        self.assertEqual(lines_down[-1], expected_log_migration_down.replace('<log>', "\n".join(lines_up[0:-1]).replace('"','\\"').replace('\n', '\\n')))

    #        matchObj = re.search(r"SPARQL DELETE FROM <test> {(.*)} WHERE {(.*)};", query_down,  re.MULTILINE)
    #        sub_classes_01 = [c.strip(' \t\n\r') for c in re.split(r" ; | \?s\. \?s ", matchObj.group(1))]
    #        sub_classes_02 = [c.strip(' \t\n\r') for c in re.split(r" ; | \?s\. \?s ", matchObj.group(2))]
    #        [self.assertTrue((c in sub_classes_01) and (c in sub_classes_02)) for c in [
    #            '<http://example.com/role> <http://www.w3.org/2000/01/rdf-schema#subClassOf>',
    #            '<http://www.w3.org/2002/07/owl#minQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#nonNegativeInteger>',
    #            '<http://www.w3.org/2002/07/owl#maxQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#nonNegativeInteger>',
    #            '<http://www.w3.org/2002/07/owl#onClass> <http://example.com/RoleOnSoapOpera>',
    #            '<http://www.w3.org/2002/07/owl#onProperty> <http://example.com/play_a_role>',
    #            '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Restriction>'
    #            ]]

    def test_it_should_get_sparql_statments_when_backward_migration(self):

        query_up, query_down = Virtuoso(self.config).get_sparql(
            current_ontology=self.structure_02_ttl_content,
            destination_ontology=self.structure_01_ttl_content,
            origen="file",
            destination_version="01",
        )

        expected_lines_up = [
            "SPARQL DELETE FROM <test> {<http://example.com/role> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
            "SPARQL DELETE FROM <test> {<http://example.com/RoleOnSoapOpera> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
        ]

        expected_log_migration_up = """SPARQL INSERT INTO <http://migration.example.com/> {[] owl:versionInfo "01"; <http://migration.example.com/endpoint> "endpoint"; <http://migration.example.com/usuario> "user"; <http://migration.example.com/ambiente> "localhost"; <http://migration.example.com/produto> "test"; <http://migration.example.com/commited> "%s"^^xsd:dateTime; <http://migration.example.com/origen> "file"; <http://migration.example.com/changes> "\\n<log>".};""" % datetime.datetime.now().strftime(
            "%Y-%m-%d %H:%M:%S"
        )

        expected_lines_down = [
            "SPARQL INSERT INTO <test> {<http://example.com/RoleOnSoapOpera> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
            "SPARQL INSERT INTO <test> {<http://example.com/role> <http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Class> . };",
        ]

        expected_log_migration_down = """SPARQL DELETE FROM <http://migration.example.com/> {?s ?p ?o} WHERE {?s owl:versionInfo "01"; <http://migration.example.com/endpoint> "endpoint"; <http://migration.example.com/usuario> "user"; <http://migration.example.com/ambiente> "localhost"; <http://migration.example.com/produto> "test"; <http://migration.example.com/commited> "%s"^^xsd:dateTime; <http://migration.example.com/origen> "file"; <http://migration.example.com/changes> "\\n<log>"; ?p ?o.};""" % datetime.datetime.now().strftime(
            "%Y-%m-%d %H:%M:%S"
        )

        """SPARQL INSERT INTO <http://migration.example.com/> {[] owl:versionInfo "01"; <http://migration.example.com/endpoint> "endpoint"; <http://migration.example.com/usuario> "user"; <http://migration.example.com/ambiente> "localhost"; <http://migration.example.com/produto> "test"; <http://migration.example.com/commited> "%s"^^xsd:dateTime; <http://migration.example.com/origen> "file"; <http://migration.example.com/changes> "\\n<log>".};""" % datetime.datetime.now().strftime(
            "%Y-%m-%d %H:%M:%S"
        )

        lines_up = query_up.strip(" \t\n\r").splitlines()
        self.assertEqual(4, len(lines_up))
        [self.assertTrue(l in lines_up) for l in expected_lines_up]
        # self.assertEqual(lines_up[-1], expected_log_migration_up.replace('<log>', "\n".join(lines_up[0:-1]).replace('"','\\"').replace('\n', '\\n')))

        #        matchObj = re.search(r"SPARQL DELETE FROM <test> {(.*)} WHERE {(.*)};", query_up,  re.MULTILINE)
        #        sub_classes_01 = [c.strip(' \t\n\r') for c in re.split(r" ; | \?s\. \?s ", matchObj.group(1))]
        #        sub_classes_02 = [c.strip(' \t\n\r') for c in re.split(r" ; | \?s\. \?s ", matchObj.group(2))]
        #        [self.assertTrue((c in sub_classes_01) and (c in sub_classes_02)) for c in [
        #            '<http://example.com/role> <http://www.w3.org/2000/01/rdf-schema#subClassOf>',
        #            '<http://www.w3.org/2002/07/owl#minQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#nonNegativeInteger>',
        #            '<http://www.w3.org/2002/07/owl#maxQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#nonNegativeInteger>',
        #            '<http://www.w3.org/2002/07/owl#onClass> <http://example.com/RoleOnSoapOpera>',
        #            '<http://www.w3.org/2002/07/owl#onProperty> <http://example.com/play_a_role>',
        #            '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Restriction>'
        #            ]]

        lines_down = query_down.strip(" \t\n\r").splitlines()
        self.assertEqual(4, len(lines_down))
        [self.assertTrue(l in lines_down) for l in expected_lines_down]

    #        self.assertEqual(lines_down[-1], expected_log_migration_down.replace('<log>', "\n".join(lines_up[0:-1]).replace('"','\\"').replace('\n', '\\n')))

    #        matchObj = re.search(r"SPARQL INSERT INTO <test> { <http://example.com/role> <http://www.w3.org/2000/01/rdf-schema#subClassOf> \[(.*)\] };", query_down,  re.MULTILINE)
    #        sub_classes = [c.strip(' \t\n\r') for c in re.split(r" ; | \?s\. \?s ", matchObj.group(1))]
    #        [self.assertTrue(c in sub_classes) for c in [
    #            '<http://www.w3.org/2002/07/owl#minQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#nonNegativeInteger>',
    #            '<http://www.w3.org/2002/07/owl#maxQualifiedCardinality> "1"^^<http://www.w3.org/2001/XMLSchema#nonNegativeInteger>',
    #            '<http://www.w3.org/2002/07/owl#onClass> <http://example.com/RoleOnSoapOpera>',
    #            '<http://www.w3.org/2002/07/owl#onProperty> <http://example.com/play_a_role>',
    #            '<http://www.w3.org/1999/02/22-rdf-syntax-ns#type> <http://www.w3.org/2002/07/owl#Restriction>'
    #            ]]

    #    @patch('simple_virtuoso_migrate.virtuoso.Virtuoso.get_sparql')
    #    def test_it_should_get_statments_to_execute_when_comparing_the_given_file_with_the_current_version(self, get_sparql_mock):
    #        Virtuoso(self.config).get_statements("data.ttl", current_version='01', origen='file')
    #        get_sparql_mock.assert_called_with(None, self.data_ttl_content, '01', None, 'file', 'data.ttl')

    #    def test_it_should_raise_exception_when_getting_statments_of_an_unexistent_ttl_file(self):
    #        self.assertRaisesWithMessage(Exception, 'migration file does not exist (current_file.ttl)', Virtuoso(self.config).get_statements, "current_file.ttl", current_version='01', origen='file')

    def test_it_should_raise_exception_if_specified_ontology_does_not_exists_on_migrations_dir(self):
        self.config.update("database_ontology", "ontology.ttl")
        self.config.update("database_migrations_dir", ".")
        self.assertRaisesWithMessage(
            Exception,
            "migration file does not exist (./ontology.ttl)",
            Virtuoso(self.config).get_ontology_by_version,
            "01",
        )

    @patch("simple_virtuoso_migrate.virtuoso.Git")
    def test_it_should_return_git_content(self, git_mock):
        execute_mock = Mock(**{"return_value": "content"})
        git_mock.return_value = Mock(**{"execute": execute_mock})

        content = Virtuoso(self.config).get_ontology_by_version("version")
        self.assertEqual("content", content)
        git_mock.assert_called_with(".")
        execute_mock.assert_called_with(["git", "show", "version:test.ttl"])

    def test_it_should_print_error_message_with_correct_encoding(self):
        graph = """
        :is_part_of rdf:type owl:ObjectProperty ;
                     rdfs:label "É parte de outro objeto" ;
                     rdfs:domain absent:Prefix ;
                     rdfs:range absent:Prefix .
        """

        expected_message = 'Error parsing graph at line 2 of <>:\nBad syntax (Prefix ":" not bound) at ^ in:\n"\n        ^:is_part_of rdf:type owl:ObjectProperty ;\n                  ..."'
        self.assertRaisesWithMessage(
            Exception,
            expected_message,
            Virtuoso(self.config).get_sparql,
            current_ontology=None,
            destination_ontology=graph,
        )